Skip to content

Conversation

@danieljbruce
Copy link
Contributor

Description

This PR makes it so that all requests to the /queries endpoint support returning high precision results for the TIMESTAMP type. This ensures that users will always get the highest precision possible for TIMESTAMP typed values requested in queries provided they request getting a higher precision timestamp by specifying a type of TIMESTAMP(12):

const query = {
    query: 'SELECT ? as ts',
    params: [bigquery.timestamp('2023-01-01T12:00:00.123456789123Z')],
    types: ['TIMESTAMP(12)']
};
const [rows] = await bigquery.query(query, options);

googleapis/nodejs-bigquery#1596 adds similar support for the /data REST endpoint which may be useful for reference. And googleapis/java-bigquery#4010 contains a similar PR to this one, but for the Bigquery Java codebase.

Impact

Allows users to opt in to fetching high precision timestamps for calls to the /queries endpoint.

Testing

System tests were added to make sure no matter what formatOptions they provide, the values sent back by the server will be delivered to the user without losing any precision when requested.

Additional Information

Notice that the PR adds timestampPrecision: 12 only when the user explicitly uses the TIMESTAMP(12) types. This is because if we automatically opt users into timestampPrecision: 12 then their code will suddenly fail for certain queries that were working before. Therefore, we are going to make timestamp precision an opt-in property.

@danieljbruce danieljbruce changed the title move over all code changes supporting high feat: Add high precision TIMESTAMP values for queries Feb 5, 2026
@danieljbruce danieljbruce marked this pull request as ready for review February 5, 2026 19:56
});
try {
/*
Without this try/catch block, calls to getRows will hang indefinitely if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: getRows = jobsQuery endpoint ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! This has been fixed now. This was a typo.

import {describe, it, before} from 'mocha';
import {BigQuery} from '../src';

describe('High Precision Query System Tests', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separated file ? The previous PR we already deviated a bit from the pattern of having system tests in the https://github.com/googleapis/nodejs-bigquery/blob/main/system-test/bigquery.ts and added https://github.com/googleapis/nodejs-bigquery/blob/main/system-test/timestamp_output_format.ts, can we bundle all timestamp related tests into the same file ?

And gonna bring this again that we don't need the amount of combinations and system tests to check if the support for picosecond resolution is working. More tests doesn't necessarily means more coverage. With this PR and the previous one we are adding 28 query executions, where previously we had a total of around 50. Not saying that the coverage was perfect before, but adding too many system/integration test for a time format change seems bit too much, I'd lean more on unit tests. System tests makes CI slower, and I'm not seeing enough justification for the amount of combination and tests being added here.

We already got bitten by too much client side validation or tests trying to assert internal details of the service in the past that can cause problems. In this case, is not a SDK code, but is test that can fail in the future if things changes in the service.

See internal b/460198628

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separated file ?

I realize having a separate file deviates from the pattern of putting every test in system-test/bigquery.ts, but I really believe that having a separate file is a better pattern. One of the biggest drawbacks with putting every test in one file is that you lose encapsulation. For instance, the before/after hooks in the bigquery.ts test file don't have anything to do with the new tests we are adding. So if we add the new tests to this file then future maintainers have to think about how everything else in this file including the before/after hooks influence these tests and we don't want that.

we don't need the amount of combinations and system tests to check if the support for picosecond resolution is working

Ok. I needed the tests myself to understand how the client was behaving, but maybe now we can comment some of them out to improve the CI performance. I would prefer to comment the tests out rather than delete them though because the commented tests could be useful later when we want to investigate the product's behaviour. Which ones do you want to comment out?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize having a separate file deviates from the pattern of putting every test in system-test/bigquery.ts, but I really believe that having a separate file is a better pattern. One of the biggest drawbacks with putting every test in one file is that you lose encapsulation. For instance, the before/after hooks in the bigquery.ts test file don't have anything to do with the new tests we are adding. So if we add the new tests to this file then future maintainers have to think about how everything else in this file including the before/after hooks influence these tests and we don't want that.

I still don't understand why we can't bundle all timestamp related tests into the same file. I can understand the reasoning about the system-test/bigquery.ts currently being bloated ( and we should plan to refactor it), but those timestamp formats can definitely live together. Am I missing something ?

Ok. I needed the tests myself to understand how the client was behaving, but maybe now we can comment some of them out to improve the CI performance. I would prefer to comment the tests out rather than delete them though because the commented tests could be useful later when we want to investigate the product's behaviour. Which ones do you want to comment out?

Was a nice catch to add the nested query test, but I don't think is a good approach to keep tests commented out here. The tests added at test/bigquery.ts are already excising all the combinations. For the integration tests we can exercise a few scenarios like:

  • Not passing parameters -> should default to picosecond
  • Passing useInt64 -> should override default and return nanoseconds
  • Passing timestampOutputFormat: INT64 -> should override default and return nanoseconds
  • Passing two incompatible params (useInt64: true and timestampOutputFormat: Float64) -> error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but those timestamp formats can definitely live together. Am I missing something ?

I put the tests in the file like you wanted. I think this is just a discussion about pros and cons. Not bloating bigquery.ts has pros, putting the tests with the other similar test has pros. We can likely move on at this point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the integration tests we can exercise a few scenarios like

I added three of the scenarios, but I'm not sure what you mean by Passing timestampOutputFormat: INT64.


describe('High Precision Query System Tests', () => {
let bigquery: BigQuery;
const expectedTsValueMicroseconds = '2023-01-01T12:00:00.123456000Z';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was suppose to be Nanosecond. We missed that the previous PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Good catch. I changed this in both places.

describe('High Precision Query System Tests', () => {
let bigquery: BigQuery;
const expectedTsValueMicroseconds = '2023-01-01T12:00:00.123456000Z';
const expectedTsValueNanoseconds = '2023-01-01T12:00:00.123456789123Z';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Good catch. I changed this in both places.

expectedTsValue: expectedTsValueMicroseconds,
},
{
name: 'TOF: FLOAT64, UI64: true (error)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the service side is going to reject it and is not a reasonable combination that customers would use. Not sure if we need to assert this. If the service side changes behavior (and can happen) we are gonna have a flaky test. I'd add just one error combination here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commented this test out. Let me know if you want more tests commented out.

Copy link
Contributor Author

@danieljbruce danieljbruce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just need to follow up on one more PR comment.

});
try {
/*
Without this try/catch block, calls to getRows will hang indefinitely if
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! This has been fixed now. This was a typo.

import {describe, it, before} from 'mocha';
import {BigQuery} from '../src';

describe('High Precision Query System Tests', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separated file ?

I realize having a separate file deviates from the pattern of putting every test in system-test/bigquery.ts, but I really believe that having a separate file is a better pattern. One of the biggest drawbacks with putting every test in one file is that you lose encapsulation. For instance, the before/after hooks in the bigquery.ts test file don't have anything to do with the new tests we are adding. So if we add the new tests to this file then future maintainers have to think about how everything else in this file including the before/after hooks influence these tests and we don't want that.

we don't need the amount of combinations and system tests to check if the support for picosecond resolution is working

Ok. I needed the tests myself to understand how the client was behaving, but maybe now we can comment some of them out to improve the CI performance. I would prefer to comment the tests out rather than delete them though because the commented tests could be useful later when we want to investigate the product's behaviour. Which ones do you want to comment out?


describe('High Precision Query System Tests', () => {
let bigquery: BigQuery;
const expectedTsValueMicroseconds = '2023-01-01T12:00:00.123456000Z';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Good catch. I changed this in both places.

describe('High Precision Query System Tests', () => {
let bigquery: BigQuery;
const expectedTsValueMicroseconds = '2023-01-01T12:00:00.123456000Z';
const expectedTsValueNanoseconds = '2023-01-01T12:00:00.123456789123Z';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Good catch. I changed this in both places.

expectedTsValue: expectedTsValueMicroseconds,
},
{
name: 'TOF: FLOAT64, UI64: true (error)',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commented this test out. Let me know if you want more tests commented out.

Adds a new test suite to bigquery.ts that verifies the correct
construction of QueryRequest formatOptions for various combinations
of timestampOutputFormat and useInt64Timestamp. The test cases
are modeled after the high-precision-query system tests.

Co-authored-by: danieljbruce <8935272+danieljbruce@users.noreply.github.com>
return fromStringValue_(value);
}

static fromSchemaValue_(value: string, elementType: string): BigQueryRange {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an internal only method (because it ends with _), so we don't need to worry about keeping the public interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like you would like to move the code back to where it was and avoid creating a new method so I have reverted the change.

assert.deepStrictEqual(req, expectedReq);
});

describe('format options', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "timestamp format options"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been updated now.

import {describe, it, before} from 'mocha';
import {BigQuery} from '../src';

describe('High Precision Query System Tests', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize having a separate file deviates from the pattern of putting every test in system-test/bigquery.ts, but I really believe that having a separate file is a better pattern. One of the biggest drawbacks with putting every test in one file is that you lose encapsulation. For instance, the before/after hooks in the bigquery.ts test file don't have anything to do with the new tests we are adding. So if we add the new tests to this file then future maintainers have to think about how everything else in this file including the before/after hooks influence these tests and we don't want that.

I still don't understand why we can't bundle all timestamp related tests into the same file. I can understand the reasoning about the system-test/bigquery.ts currently being bloated ( and we should plan to refactor it), but those timestamp formats can definitely live together. Am I missing something ?

Ok. I needed the tests myself to understand how the client was behaving, but maybe now we can comment some of them out to improve the CI performance. I would prefer to comment the tests out rather than delete them though because the commented tests could be useful later when we want to investigate the product's behaviour. Which ones do you want to comment out?

Was a nice catch to add the nested query test, but I don't think is a good approach to keep tests commented out here. The tests added at test/bigquery.ts are already excising all the combinations. For the integration tests we can exercise a few scenarios like:

  • Not passing parameters -> should default to picosecond
  • Passing useInt64 -> should override default and return nanoseconds
  • Passing timestampOutputFormat: INT64 -> should override default and return nanoseconds
  • Passing two incompatible params (useInt64: true and timestampOutputFormat: Float64) -> error

useInt64Timestamp: false,
},
{
name: 'TOF: TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED, UI64: omitted (default INT64)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both omitted it should default to ISO8601_STRING right ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This combination results in ISO8601_STRING.

useInt64Timestamp: false,
},
{
name: 'TOF: omitted, UI64: omitted (default INT64)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both omitted it should default to ISO8601_STRING right ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This test case has actually been replaced by your most recent commit.

useInt64Timestamp: undefined,
},
{
name: 'TOF: ISO8601_STRING, UI64: omitted (error)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this should be an error ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be, but these tests were failing anyway. We need to get the CI set up for the monorepo to catch stuff like this automatically.

In any case, these tests have now been updated. Keep in mind that the exact same logic we used for the /data endpoint is now being applied to the /queries endpoint so if you see any problems with these tests then the same correction should be applied to the /data test as well.

Copy link
Contributor Author

@danieljbruce danieljbruce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making changes. Work in progress.

return fromStringValue_(value);
}

static fromSchemaValue_(value: string, elementType: string): BigQueryRange {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like you would like to move the code back to where it was and avoid creating a new method so I have reverted the change.

import {describe, it, before} from 'mocha';
import {BigQuery} from '../src';

describe('High Precision Query System Tests', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but those timestamp formats can definitely live together. Am I missing something ?

I put the tests in the file like you wanted. I think this is just a discussion about pros and cons. Not bloating bigquery.ts has pros, putting the tests with the other similar test has pros. We can likely move on at this point.

import {describe, it, before} from 'mocha';
import {BigQuery} from '../src';

describe('High Precision Query System Tests', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the integration tests we can exercise a few scenarios like

I added three of the scenarios, but I'm not sure what you mean by Passing timestampOutputFormat: INT64.

Copy link
Contributor Author

@danieljbruce danieljbruce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I addressed most of the comments, but it seems there are more regressions in the buildQueryRequest_ tests that I need to address.

assert.deepStrictEqual(req, expectedReq);
});

describe('format options', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been updated now.

useInt64Timestamp: false,
},
{
name: 'TOF: omitted, UI64: omitted (default INT64)',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This test case has actually been replaced by your most recent commit.

useInt64Timestamp: false,
},
{
name: 'TOF: TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED, UI64: omitted (default INT64)',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This combination results in ISO8601_STRING.

Copy link
Contributor Author

@danieljbruce danieljbruce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed comments. Ready for another review.

useInt64Timestamp: undefined,
},
{
name: 'TOF: ISO8601_STRING, UI64: omitted (error)',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be, but these tests were failing anyway. We need to get the CI set up for the monorepo to catch stuff like this automatically.

In any case, these tests have now been updated. Keep in mind that the exact same logic we used for the /data endpoint is now being applied to the /queries endpoint so if you see any problems with these tests then the same correction should be applied to the /data test as well.

opts: QueryOptions;
expected?: any;
bail?: boolean;
}[] = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: what are we asserting ?

  • Not passing anything and expecting a default makes sense to me
  • Passing X and expecting X makes sense too. But then passing Y and expecting Y, passing Z and expecting Z starts to get redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created https://b.corp.google.com/issues/483681188 to address this in a separate PR.

Copy link
Contributor

@alvarowolfx alvarowolfx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still have nits on the unit tests, but function wise, LGTM

@danieljbruce danieljbruce merged commit bea42b2 into main Feb 11, 2026
15 of 17 checks passed
@danieljbruce danieljbruce deleted the big-query-query-changes branch February 11, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants