FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(dbapi): wire timeout parameter through Connection to execute_sql by waiho-gumloop · Pull Request #1535 · googleapis/python-spanner · GitHub

This repository was archived by the owner on Jun 8, 2026. It is now read-only.
/ python-spanner Public archive

feat(dbapi): wire timeout parameter through Connection to execute_sql - #1535

Closed
waiho-gumloop wants to merge 2 commits into
googleapis:mainfrom
waiho-gumloop:feat/dbapi-timeout-parameter
Closed

waiho-gumloop wants to merge 2 commits into
googleapis:mainfrom
waiho-gumloop:feat/dbapi-timeout-parameter

Conversation

waiho-gumloop commented Mar 27, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Summary

Add a timeout property to Connection and pass timeout= to execute_sql() in the three DBAPI code paths that currently omit it: snapshot reads, transaction statements, and autocommit DML.

Fixes googleapis/google-cloud-python#16492

Background

_SnapshotBase.execute_sql() accepts a timeout parameter that controls the gRPC deadline for the ExecuteStreamingSql RPC. When not provided, it defaults to gapic_v1.method.DEFAULT, which resolves to default_timeout=3600.0 in the transport layer.

The DBAPI calls execute_sql() in three locations, none of which pass timeout=:

  1. Snapshot readscursor._handle_DQL_with_snapshot() calls snapshot.execute_sql(sql, params, param_types, request_options=...) without timeout=
  2. Transaction reads/writesconnection.run_statement() calls transaction.execute_sql(sql, params, param_types=..., request_options=...) without timeout=
  3. Autocommit DMLcursor._do_execute_update_in_autocommit() calls transaction.execute_sql(sql, params=..., param_types=..., last_statement=True) without timeout=

This means DBAPI consumers (SQLAlchemy, Django, raw DBAPI) cannot control the gRPC deadline for individual statements — all queries use the 3600-second default.

Timeline

Date Commit Event
Nov 2018 9b7fcd6 (PR #6536) timeout= parameter added to _SnapshotBase.execute_sql() in the Spanner client
Nov 2020 2493fa1 (PR #160) DBAPI created — cursor._handle_DQL_with_snapshot() calls execute_sql() without timeout=
Nov 2020 d59d502 (PR #168) connection.run_statement() added — calls execute_sql() without timeout=
Mar 2021 1a7c9d2 (PR #278) timeout= expanded to read(), partition_read(), partition_query() in the client
Oct 2021 cd3b950 (PR #475) _handle_DQL_with_snapshot() extracted as separate method — still no timeout=
Oct 2022 ab768e4 (PR #838) request_options added to _handle_DQL_with_snapshot() — timeout= not added
Jan 2025 ee9662f (PR #1262) request_tag/transaction_tag added to cursor/connection — timeout= not added

Other execution parameters (request_options, request_priority, transaction_tag, request_tag) were each wired through the DBAPI incrementally. The timeout parameter was not included in any of these additions.

Changes

connection.py

  • Add self._timeout = None to __init__
  • Add timeout property and setter
  • Pass timeout=self._timeout in run_statement() when set

cursor.py

  • Pass timeout=self.connection._timeout in _handle_DQL_with_snapshot() when set
  • Pass timeout=self.connection._timeout in _do_execute_update_in_autocommit() when set

When timeout is None (the default), timeout= is not passed, preserving the existing behavior of using gapic_v1.method.DEFAULT.

Usage

from google.cloud.spanner_dbapi import connect

conn = connect(instance_id, database_id, project=project)
conn.timeout = 60  # 60-second gRPC deadline for subsequent statements

cursor = conn.cursor()
cursor.execute("SELECT * FROM my_table")

Tests

Added 7 unit tests:

  • test_timeout_default_none — verifies default is None
  • test_timeout_property — verifies getter/setter
  • test_timeout_passed_to_run_statement — verifies timeout= is passed in run_statement()
  • test_timeout_not_passed_when_none — verifies timeout= is omitted when None
  • test_do_execute_update_with_timeout — verifies timeout= in autocommit DML path
  • test_handle_DQL_with_snapshot_timeout — verifies timeout= in snapshot read path
  • test_handle_DQL_with_snapshot_no_timeout — verifies omission in snapshot read path

All 205 existing DBAPI tests continue to pass.

Related

waiho-gumloop requested review from a team as code owners March 27, 2026 01:48
product-auto-label Bot added size: m Pull request size is medium. api: spanner Issues related to the googleapis/python-spanner API. labels Mar 27, 2026

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Code Review

This pull request introduces a timeout property to the Connection class in the Spanner DB-API, allowing users to set a gRPC deadline for SQL operations. The timeout value is propagated to execute_sql calls in run_statement, _do_execute_update_in_autocommit, and _handle_DQL_with_snapshot. Corresponding unit tests have been added to verify the property behavior and its correct application during execution. Feedback suggests refactoring the execute_sql call in _handle_DQL_with_snapshot to use a consistent keyword argument pattern for param_types, matching other updated call sites.

Comment thread google/cloud/spanner_dbapi/cursor.py Outdated
Comment on lines 549 to 557
kwargs = dict(request_options=self.request_options)
if self.connection._timeout is not None:
kwargs["timeout"] = self.connection._timeout
self._result_set = snapshot.execute_sql(
sql,
params,
get_param_types(params),
request_options=self.request_options,
**kwargs,
)

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

For consistency with the changes in run_statement and _do_execute_update_in_autocommit, consider including param_types in the kwargs dictionary instead of passing it as a positional argument. This would make all three modified call sites for execute_sql follow the same pattern, improving maintainability.

Suggested change
kwargs = dict(request_options=self.request_options)
if self.connection._timeout is not None:
kwargs["timeout"] = self.connection._timeout
self._result_set = snapshot.execute_sql(
sql,
params,
get_param_types(params),
request_options=self.request_options,
**kwargs,
)
kwargs = dict(
param_types=get_param_types(params),
request_options=self.request_options,
)
if self.connection._timeout is not None:
kwargs["timeout"] = self.connection._timeout
self._result_set = snapshot.execute_sql(
sql,
params,
**kwargs,
)

The DBAPI layer calls _SnapshotBase.execute_sql() in three code paths
(snapshot reads, transaction reads/writes, autocommit DML) but never
passes the timeout= argument. This causes all queries to use the gRPC
default timeout of 3600 seconds.

Add a timeout property to Connection and pass it through to
execute_sql() in cursor._handle_DQL_with_snapshot(),
cursor._do_execute_update_in_autocommit(), and
connection.run_statement().

Fixes #1534
waiho-gumloop force-pushed the feat/dbapi-timeout-parameter branch from b73fa31 to f93bed6 Compare March 27, 2026 02:00

parthea commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Hi @waiho-gumloop, The code in this repository has moved to https://github.com/googleapis/google-cloud-python/tree/main/packages/google-cloud-spanner. Please could you open a new PR in google-cloud-python?

parthea closed this Mar 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

api: spanner Issues related to the googleapis/python-spanner API. size: m Pull request size is medium.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(dbapi): wire timeout parameter through to execute_sql in cursor and connection

3 participants


Back | FazBrowse Home | New Git URL