Class: TalkToYourApp::Plugins::Db::Tools::Query
- Defined in:
- lib/talk_to_your_app/plugins/db/tools/query.rb
Overview
Runs a SQL query on the connection wired into the DB plugin and renders the rows as JSON, plain text, or an HTML table. The query runs inside a transaction with a per-query statement timeout, so a slow query cannot poison the connection pool. On a :reading connection (the default) writes are rejected by Rails' write prevention and the read-only DB role — the gem does not parse SQL. On a :writing connection (opt-in) writes execute.
Constant Summary collapse
- DEFAULT_TIMEOUT_MS =
30_000
Class Method Summary collapse
-
.timeout_statement(adapter_name, ms) ⇒ Object
The SQL that arms a per-query timeout for an adapter, or nil when the adapter has no per-statement timeout.
Instance Method Summary collapse
Methods inherited from Tool
argument, arguments, clear_custom_registry!, connection, custom_registry, default_arguments, description, dispatch, inherited, input_schema_hash, invoke, name, normalize_response, to_mcp_definition, to_mcp_tool, tool_name
Class Method Details
.timeout_statement(adapter_name, ms) ⇒ Object
The SQL that arms a per-query timeout for an adapter, or nil when the
adapter has no per-statement timeout. Pure (no connection) so it is
unit-testable without each database installed.
Postgres -> transaction-local statement_timeout (unwound with the txn).
MySQL -> session max_execution_time (ms), bounding read-only SELECTs.
SQLite / MariaDB / others -> none (documented in the README).
67 68 69 70 71 72 |
# File 'lib/talk_to_your_app/plugins/db/tools/query.rb', line 67 def self.timeout_statement(adapter_name, ms) case adapter_name when /postgres/i then "SET LOCAL statement_timeout = #{ms.to_i}" when /mysql|trilogy/i then "SET SESSION max_execution_time = #{ms.to_i}" end end |
Instance Method Details
#call(args, ctx) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/talk_to_your_app/plugins/db/tools/query.rb', line 27 def call(args, ctx) format = args[:format] || "json" ms = timeout_ms(ctx) result = ctx.connection do |conn| conn.transaction do apply_statement_timeout(conn, ms) begin conn.exec_query(args[:sql]) ensure clear_statement_timeout(conn) end end end render(result, format) rescue ActiveRecord::QueryCanceled, ActiveRecord::StatementTimeout => e error("Query exceeded the #{ms}ms statement timeout: #{e.}") rescue ActiveRecord::ReadOnlyError => e # Rails' connected_to(role: :reading) blocks writes before they reach # the database; the read-only DB role is the backstop behind it. error("Write rejected: this connection is read-only. #{e.}") rescue ActiveRecord::StatementInvalid => e error("Query failed: #{e.}") end |