Skip to content

feat: Add high precision TIMESTAMP values for queries#7147

Open
danieljbruce wants to merge 8 commits intomainfrom
big-query-query-changes
Open

feat: Add high precision TIMESTAMP values for queries#7147
danieljbruce wants to merge 8 commits intomainfrom
big-query-query-changes

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?


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.

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

it('should create a QueryRequest from a SQL string', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

we can write similar tests to this one to assert the logic for default dataFormatOptions value. The existing tests assert only not passing any format from the customer and expecting ISO8601_STRING, but we can test passing any timestampOutputFormat or useInt64Timestamp and assert that this overrides the default value. This is faster than the system test and covers the change that is to use ISO8601_STRING as the default, depending on the format option provided by the customer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now I understand your preference for unit tests. I'll get to this one next.

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.

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

it('should create a QueryRequest from a SQL string', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now I understand your preference for unit tests. I'll get to this one next.

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