Skip to content

feat(bigquery): add timestamp_precision for Arrow query results - #18649

Open
ohmayr wants to merge 1 commit into
fix/bigquery-arrow-serialization-optionsfrom
feat/bigquery-arrow-timestamp-precision
Open

ohmayr wants to merge 1 commit into
fix/bigquery-arrow-serialization-optionsfrom
feat/bigquery-arrow-timestamp-precision

Conversation

@ohmayr

@ohmayr ohmayr commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Adds timestamp_precision to query_and_wait() for Arrow results, like Client.query() and Client.list_rows() already have, so picosecond timestamps aren't truncated to microseconds.

  • TimestampPrecision.PICOSECOND returns full-precision ISO 8601 strings. Anything else returns microseconds.
  • Raises ValueError if it's set without ARROW.
  • Adds unit tests and system tests for microsecond, TIMESTAMP(12), and picosecond table columns.

@ohmayr
ohmayr requested review from a team as code owners October 13, 2026 08:20
@ohmayr
ohmayr requested review from julianxu-goog and removed request for a team October 13, 2026 08:20

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request adds a timestamp_precision parameter to query_and_wait to support picosecond precision for ARROW query results, along with corresponding validation, serialization, and tests. The review feedback recommends enhancing robustness by accepting both enum values and case-insensitive string representations for timestamp_precision, updating type annotations, and adding unit tests for these string inputs.

Comment on lines +124 to +128
"picosTimestampPrecision": (
"TIMESTAMP_PRECISION_PICOS"
if timestamp_precision == enums.TimestampPrecision.PICOSECOND
else "TIMESTAMP_PRECISION_MICROS"
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To make the timestamp_precision parameter more robust and consistent with other parameters like compression_codec, we should support both the Enum members and their raw string representations (including case-insensitive inputs). Currently, passing a string like 'PICOSECOND' or 'picosecond' will silently fall back to 'TIMESTAMP_PRECISION_MICROS' without any warning or error. We can normalize the input using getattr and .upper() before performing the comparison.

        "picosTimestampPrecision": (\n            "TIMESTAMP_PRECISION_PICOS"\n            if timestamp_precision is not None\n            and str(getattr(timestamp_precision, "value", timestamp_precision)).upper() == "PICOSECOND"\n            else "TIMESTAMP_PRECISION_MICROS"\n        ),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm keeping this consistent with Client.query() and Client.list_rows(), which take the same timestamp_precision parameter and compare against TimestampPrecision.PICOSECOND the same way. TimestampPrecision is a plain Enum (PICOSECOND = 12), not a str enum, so accepting strings here but not there would be surprising. If we want string support it should be one change across all three methods.

max_results: Optional[int] = None,
query_results_format: Optional[str] = None,
compression_codec: Optional[str] = None,
timestamp_precision: Optional[enums.TimestampPrecision] = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To support both the Enum members and their raw string representations (which is standard across Google Cloud Python libraries), please update the type annotation to include str.

Suggested change
timestamp_precision: Optional[enums.TimestampPrecision] = None,
timestamp_precision: Optional[Union[str, enums.TimestampPrecision]] = None,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reasoning as in the first thread on this PR: keeping the type the same as Client.query() and Client.list_rows().

max_results: Optional[int] = None,
query_results_format: Optional[str] = None,
compression_codec: Optional[str] = None,
timestamp_precision: Optional[enums.TimestampPrecision] = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To support both the Enum members and their raw string representations (which is standard across Google Cloud Python libraries), please update the type annotation to include str.

Suggested change
timestamp_precision: Optional[enums.TimestampPrecision] = None,
timestamp_precision: Optional[Union[str, enums.TimestampPrecision]] = None,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reasoning as in the first thread on this PR: keeping the type the same as Client.query() and Client.list_rows().

max_results: Optional[int] = None,
query_results_format: Optional[str] = None,
compression_codec: Optional[str] = None,
timestamp_precision: Optional[enums.TimestampPrecision] = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To support both the Enum members and their raw string representations (which is standard across Google Cloud Python libraries), please update the type annotation to include str.

Suggested change
timestamp_precision: Optional[enums.TimestampPrecision] = None,
timestamp_precision: Optional[Union[str, enums.TimestampPrecision]] = None,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reasoning as in the first thread on this PR: keeping the type the same as Client.query() and Client.list_rows().

Comment on lines +928 to +935
@pytest.mark.parametrize(
("timestamp_precision", "expected"),
[
(None, "TIMESTAMP_PRECISION_MICROS"),
(TimestampPrecision.MICROSECOND, "TIMESTAMP_PRECISION_MICROS"),
(TimestampPrecision.PICOSECOND, "TIMESTAMP_PRECISION_PICOS"),
],
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Let's add test cases for string values (both uppercase and lowercase) to ensure that the robust string normalization works as expected and doesn't regress in the future.

@pytest.mark.parametrize(\n    ("timestamp_precision", "expected"),\n    [\n        (None, "TIMESTAMP_PRECISION_MICROS"),\n        (TimestampPrecision.MICROSECOND, "TIMESTAMP_PRECISION_MICROS"),\n        (TimestampPrecision.PICOSECOND, "TIMESTAMP_PRECISION_PICOS"),\n        ("PICOSECOND", "TIMESTAMP_PRECISION_PICOS"),\n        ("picosecond", "TIMESTAMP_PRECISION_PICOS"),\n        ("MICROSECOND", "TIMESTAMP_PRECISION_MICROS"),\n        ("microsecond", "TIMESTAMP_PRECISION_MICROS"),\n    ],\n)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reasoning as in the first thread on this PR: keeping the type the same as Client.query() and Client.list_rows().

@ohmayr
ohmayr added this pull request to stack #18648 October 13, 2026 08:32
ARROW results always truncated picosecond TIMESTAMP columns to
microseconds, because query_and_wait() had no way to ask for another
precision. Client.query() and Client.list_rows() already take
timestamp_precision for STRUCT_ENCODING results.

Add the same timestamp_precision parameter, with the existing
TimestampPrecision enum, to query_and_wait(). PICOSECOND maps to
TIMESTAMP_PRECISION_PICOS, which returns picosecond columns at full
precision as ISO 8601 strings, and anything else maps to
TIMESTAMP_PRECISION_MICROS. Nanoseconds aren't offered, since the
picosecond strings already keep full precision. The value goes into
the arrowSerializationOptions sent on jobs.query and ReadRows, and
setting it without ARROW raises ValueError, like compression_codec.
It's labeled [Private Preview], as on Client.query().
@ohmayr
ohmayr force-pushed the feat/bigquery-arrow-timestamp-precision branch from 9cc4ea4 to 660ff1d Compare October 13, 2026 10:02
@ohmayr
ohmayr requested a review from tswast October 13, 2026 20:41

This branch has not been deployed

No deployments
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.

1 participant