Skip to content

Feature: per-source readonly_session_sql applied on every read-only execution #449

Description

Summary

Add a readonly_session_sql field to [[sources]] holding session-setting statements that DBHub runs
before every read-only execution on that source.

[[sources]]
id = "production"
dsn = "mysql://user:pass@localhost:3306/mydb"

readonly_session_sql = """
SET SESSION max_execution_time = 30000;
SET SESSION innodb_lock_wait_timeout = 5;
"""

Problem

DBHub exposes a few guardrails as named options (query_timeout, search_path,
pool_max_connections), but there is no way to set anything else the database can enforce
on its own:

Setting What it protects against
max_execution_time (MySQL) No server-side statement deadline on MySQL
innodb_lock_wait_timeout (MySQL), lock_timeout (Postgres) A read query waiting on a lock indefinitely
work_mem (Postgres) One query consuming unbounded memory

MySQL is where this matters most. The MariaDB connector applies query_timeout server-side as
max_statement_time, while the MySQL connector relies on a client-side deadline plus
KILL QUERY (#386). Mapping query_timeout to max_execution_time would cover the deadline
alone; readonly_session_sql also covers lock waits and the other settings above, without a new option
for each.

Why per execution rather than at connect time

A setting applied at connect time can be changed mid-session, and the change stays on the
pooled connection (#448 shows this on PostgreSQL). Every connector already opens a read-only
transaction for each read-only execution, so running the statements right after it puts the
configured values back before every statement.

Why not role-level settings

Per-role defaults (ALTER ROLE dbhub SET lock_timeout = '5s') are an option, and I agree a
least-privilege account is the real boundary. DBHub-side settings still help because:

Dialects

Database Statement form Scope
MySQL / MariaDB SET SESSION name = value Overwritten per execution
PostgreSQL SET LOCAL name = value Reverts at the end of each execution

Anything else fails at config load, so this does not become a general SQL execution path.
SQL Server, Oracle and SQLite are left out for now: SQLite has no server-side deadline to set,
and the SQL Server and Oracle containers would make the integration job heavier. They can follow
the same pattern if there is demand.

It applies to every read-only execution (execute_sql, explain_sql, read-only custom tools);
writable executions are untouched.

#450 implements this for MySQL/MariaDB with tests and docs. PostgreSQL support is ready on top
of it in a follow-up branch,
which I will open once the first PR settles. Happy to adjust the field name or scope.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions