Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 15 additions & 126 deletions handwritten/bigquery/src/bigquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,9 +597,6 @@ export class BigQuery extends Service {
wrapIntegers: boolean | IntegerTypeCastOptions;
selectedFields?: string[];
parseJSON?: boolean;
listParams?:
| bigquery.tabledata.IListParams
| bigquery.jobs.IGetQueryResultsParams;
},
) {
// deep copy schema fields to avoid mutation
Expand Down Expand Up @@ -1100,11 +1097,6 @@ export class BigQuery extends Service {
};
}),
};
} else if ((providedType as string).toUpperCase() === 'TIMESTAMP(12)') {
return {
type: 'TIMESTAMP',
timestampPrecision: '12',
};
}

providedType = (providedType as string).toUpperCase();
Expand Down Expand Up @@ -2256,33 +2248,14 @@ export class BigQuery extends Service {
if (res && res.jobComplete) {
let rows: any = [];
if (res.schema && res.rows) {
try {
/*
Without this try/catch block, calls to getRows will hang indefinitely if
a call to mergeSchemaWithRows_ fails because the error never makes it to
the callback. Instead, pass the error to the callback the user provides
so that the user can see the error.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const listParams = {
'formatOptions.timestampOutputFormat':
queryReq.formatOptions?.timestampOutputFormat,
'formatOptions.useInt64Timestamp':
queryReq.formatOptions?.useInt64Timestamp,
};
if (options.skipParsing) {
rows = res.rows;
} else {
rows = BigQuery.mergeSchemaWithRows_(res.schema, res.rows, {
wrapIntegers: options.wrapIntegers || false,
parseJSON: options.parseJSON,
listParams,
});
delete res.rows;
}
} catch (e) {
(callback as SimpleQueryRowsCallback)(e as Error, null, job);
return;
if (options.skipParsing) {
rows = res.rows;
} else {
rows = BigQuery.mergeSchemaWithRows_(res.schema, res.rows, {
wrapIntegers: options.wrapIntegers || false,
parseJSON: options.parseJSON,
});
delete res.rows;
}
}
this.trace_('[runJobsQuery] job complete');
Expand Down Expand Up @@ -2364,18 +2337,6 @@ export class BigQuery extends Service {
if (options.job) {
return undefined;
}
const hasAnyFormatOpts =
options['formatOptions.timestampOutputFormat'] !== undefined ||
options['formatOptions.useInt64Timestamp'] !== undefined;
const defaultOpts = hasAnyFormatOpts
? {}
: {
timestampOutputFormat: 'ISO8601_STRING',
};
const formatOptions = extend(defaultOpts, {
timestampOutputFormat: options['formatOptions.timestampOutputFormat'],
useInt64Timestamp: options['formatOptions.useInt64Timestamp'],
});
const req: bigquery.IQueryRequest = {
useQueryCache: queryObj.useQueryCache,
labels: queryObj.labels,
Expand All @@ -2384,7 +2345,9 @@ export class BigQuery extends Service {
maximumBytesBilled: queryObj.maximumBytesBilled,
timeoutMs: options.timeoutMs,
location: queryObj.location || options.location,
formatOptions,
formatOptions: {
useInt64Timestamp: true,
},
maxResults: queryObj.maxResults || options.maxResults,
query: queryObj.query,
useLegacySql: false,
Expand Down Expand Up @@ -2514,9 +2477,6 @@ function convertSchemaFieldValue(
wrapIntegers: boolean | IntegerTypeCastOptions;
selectedFields?: string[];
parseJSON?: boolean;
listParams?:
| bigquery.tabledata.IListParams
| bigquery.jobs.IGetQueryResultsParams;
},
) {
if (value === null) {
Expand Down Expand Up @@ -2576,43 +2536,9 @@ function convertSchemaFieldValue(
break;
}
case 'TIMESTAMP': {
/*
At this point, 'value' will equal the timestamp value returned from the
server. We need to parse this value differently depending on its format.
For example, value could be any of the following:
1672574400123456
1672574400.123456
2023-01-01T12:00:00.123456789123Z
*/
const listParams = options.listParams;
const timestampOutputFormat = listParams
? listParams['formatOptions.timestampOutputFormat']
: undefined;
const useInt64Timestamp = listParams
? listParams['formatOptions.useInt64Timestamp']
: undefined;
if (timestampOutputFormat === 'ISO8601_STRING') {
// value is ISO string, create BigQueryTimestamp wrapping the string
value = BigQuery.timestamp(value);
} else if (
useInt64Timestamp !== true &&
timestampOutputFormat !== 'INT64' &&
(useInt64Timestamp !== undefined || timestampOutputFormat !== undefined)
) {
// NOTE: The additional
// (useInt64Timestamp !== undefined || timestampOutputFormat !== und...)
// check is to ensure that calls to the /query endpoint remain
// unaffected as they will not be providing any listParams.
//
// If the program reaches this point in time then
// value is float seconds so convert to BigQueryTimestamp
value = BigQuery.timestamp(Number(value));
} else {
// Expect int64 micros (default or explicit INT64)
const pd = new PreciseDate();
pd.setFullTime(PreciseDate.parseFull(BigInt(value) * BigInt(1000)));
value = BigQuery.timestamp(pd);
}
const pd = new PreciseDate();
pd.setFullTime(PreciseDate.parseFull(BigInt(value) * BigInt(1000)));
value = BigQuery.timestamp(pd);
break;
}
case 'GEOGRAPHY': {
Expand All @@ -2628,7 +2554,6 @@ function convertSchemaFieldValue(
value = BigQueryRange.fromSchemaValue_(
value,
schemaField.rangeElementType!.type!,
options.listParams, // Required to convert TIMESTAMP values
);
break;
}
Expand Down Expand Up @@ -2706,14 +2631,6 @@ export class BigQueryRange {
};
}

/**
* This method returns start and end values for RANGE typed values returned from
* the server. It decodes the server RANGE value into start and end values so
* they can be used to construct a BigQueryRange.
* @private
* @param {string} value The range value.
* @returns {string[]} The start and end of the range.
*/
private static fromStringValue_(value: string): [start: string, end: string] {
let cleanedValue = value;
if (cleanedValue.startsWith('[') || cleanedValue.startsWith('(')) {
Expand All @@ -2733,32 +2650,14 @@ export class BigQueryRange {
return [start, end];
}

/**
* This method is only used by convertSchemaFieldValue and only when range
* values are passed into convertSchemaFieldValue. It produces a value that is
* delivered to the user for read calls and it needs to pass along listParams
* to ensure TIMESTAMP types are converted properly.
* @private
* @param {string} value The range value.
* @param {string} elementType The element type.
* @param {bigquery.tabledata.IListParams | bigquery.jobs.IGetQueryResultsParams} [listParams] The list parameters.
* @returns {BigQueryRange}
*/
static fromSchemaValue_(
value: string,
elementType: string,
listParams?:
| bigquery.tabledata.IListParams
| bigquery.jobs.IGetQueryResultsParams,
): BigQueryRange {
static fromSchemaValue_(value: string, elementType: string): BigQueryRange {
const [start, end] = BigQueryRange.fromStringValue_(value);
const convertRangeSchemaValue = (value: string) => {
if (value === 'UNBOUNDED' || value === 'NULL') {
return null;
}
return convertSchemaFieldValue({type: elementType}, value, {
wrapIntegers: false,
listParams,
});
};
return BigQuery.range(
Expand Down Expand Up @@ -2834,16 +2733,6 @@ export class BigQueryTimestamp {
} else if (typeof value === 'string') {
if (/^\d{4}-\d{1,2}-\d{1,2}/.test(value)) {
pd = new PreciseDate(value);
if (value.match(/\.\d{10,}/) && !Number.isNaN(pd.getTime())) {
/*
TODO:
When https://github.com/googleapis/nodejs-precise-date/pull/302
is released and we have full support for picoseconds in PreciseData
then we can remove this if block.
*/
this.value = value;
return;
}
} else {
const floatValue = Number.parseFloat(value);
if (!Number.isNaN(floatValue)) {
Expand Down
27 changes: 8 additions & 19 deletions handwritten/bigquery/src/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,25 +596,14 @@ class Job extends Operation {
let rows: any = [];

if (resp.schema && resp.rows) {
try {
/*
Without this try/catch block, calls to /query endpoint will hang
indefinitely if a call to mergeSchemaWithRows_ fails because the
error never makes it to the callback. Instead, pass the error to the
callback the user provides so that the user can see the error.
*/
if (options.skipParsing) {
rows = resp.rows;
} else {
rows = BigQuery.mergeSchemaWithRows_(resp.schema, resp.rows, {
wrapIntegers,
parseJSON,
});
delete resp.rows;
}
} catch (e) {
callback!(e as Error, null, null, resp);
return;
if (options.skipParsing) {
rows = resp.rows;
} else {
rows = BigQuery.mergeSchemaWithRows_(resp.schema, resp.rows, {
wrapIntegers,
parseJSON,
});
delete resp.rows;
}
}

Expand Down
50 changes: 16 additions & 34 deletions handwritten/bigquery/src/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import {JobMetadata, JobOptions} from './job';
import bigquery from './types';
import {IntegerTypeCastOptions} from './bigquery';
import {RowQueue} from './rowQueue';
import IDataFormatOptions = bigquery.IDataFormatOptions;

// This is supposed to be a @google-cloud/storage `File` type. The storage npm
// module includes these types, but is current installed as a devDependency.
Expand Down Expand Up @@ -1867,42 +1866,25 @@ class Table extends ServiceObject {
callback!(err, null, null, resp);
return;
}
try {
/*
Without this try/catch block, calls to getRows will hang indefinitely if
a call to mergeSchemaWithRows_ fails because the error never makes it to
the callback. Instead, pass the error to the callback the user provides
so that the user can see the error.
*/
if (options.skipParsing) {
rows = rows || [];
} else {
rows = BigQuery.mergeSchemaWithRows_(
this.metadata.schema,
rows || [],
{
wrapIntegers,
selectedFields,
parseJSON,
listParams: qs,
},
);
}
} catch (err) {
callback!(err as Error | null, null, null, resp);
return;
if (options.skipParsing) {
rows = rows || [];
} else {
rows = BigQuery.mergeSchemaWithRows_(this.metadata.schema, rows || [], {
wrapIntegers,
selectedFields,
parseJSON,
});
}
callback!(null, rows, nextQuery, resp);
};
const hasAnyFormatOpts =
options['formatOptions.timestampOutputFormat'] !== undefined ||
options['formatOptions.useInt64Timestamp'] !== undefined;
const defaultOpts = hasAnyFormatOpts
? {}
: {
'formatOptions.timestampOutputFormat': 'ISO8601_STRING',
};
const qs = extend(defaultOpts, options);

const qs = extend(
{
'formatOptions.useInt64Timestamp': true,
},
options,
);

this.request(
{
uri: '/data',
Expand Down
Loading
Loading