1 | ;
|
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 | */
|
17 | Object.defineProperty(exports, "__esModule", { value: true });
|
18 | exports.BigQueryInt = exports.BigQueryTime = exports.BigQueryDatetime = exports.BigQueryTimestamp = exports.Geography = exports.BigQueryDate = exports.BigQueryRange = exports.BigQuery = exports.PROTOCOL_REGEX = exports.common = void 0;
|
19 | const common_1 = require("@google-cloud/common");
|
20 | const common = require("@google-cloud/common");
|
21 | exports.common = common;
|
22 | const paginator_1 = require("@google-cloud/paginator");
|
23 | const promisify_1 = require("@google-cloud/promisify");
|
24 | const precise_date_1 = require("@google-cloud/precise-date");
|
25 | const arrify = require("arrify");
|
26 | const Big = require("big.js");
|
27 | const extend = require("extend");
|
28 | const is = require("is");
|
29 | const uuid = require("uuid");
|
30 | const dataset_1 = require("./dataset");
|
31 | const job_1 = require("./job");
|
32 | const table_1 = require("./table");
|
33 | const logger_1 = require("./logger");
|
34 | exports.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 | */
|
108 | class 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 | }
|
1463 | exports.BigQuery = BigQuery;
|
1464 | BigQuery.setLogFunction = logger_1.setLogFunction;
|
1465 | /*! Developer Documentation
|
1466 | *
|
1467 | * These methods can be auto-paginated.
|
1468 | */
|
1469 | paginator_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 | });
|
1488 | function convertSchemaFieldValue(schemaField,
|
1489 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
1490 | value, 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 | */
|
1574 | class 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 | }
|
1669 | exports.BigQueryRange = BigQueryRange;
|
1670 | /**
|
1671 | * Date class for BigQuery.
|
1672 | */
|
1673 | class BigQueryDate {
|
1674 | constructor(value) {
|
1675 | if (typeof value === 'object') {
|
1676 | value = BigQuery.datetime(value).value;
|
1677 | }
|
1678 | this.value = value;
|
1679 | }
|
1680 | }
|
1681 | exports.BigQueryDate = BigQueryDate;
|
1682 | /**
|
1683 | * Geography class for BigQuery.
|
1684 | */
|
1685 | class Geography {
|
1686 | constructor(value) {
|
1687 | this.value = value;
|
1688 | }
|
1689 | }
|
1690 | exports.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 | */
|
1699 | class 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 | }
|
1743 | exports.BigQueryTimestamp = BigQueryTimestamp;
|
1744 | /**
|
1745 | * Datetime class for BigQuery.
|
1746 | */
|
1747 | class 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 | }
|
1766 | exports.BigQueryDatetime = BigQueryDatetime;
|
1767 | /**
|
1768 | * Time class for BigQuery.
|
1769 | */
|
1770 | class 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 | }
|
1782 | exports.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 | */
|
1803 | class 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 | }
|
1857 | exports.BigQueryInt = BigQueryInt;
|
1858 | //# sourceMappingURL=bigquery.js.map |
\ | No newline at end of file |