Execute a read-only SQL query against a PostgreSQL database. This function: 1. Opens a new connection with LLM-specific application_name 2. Starts a READ ONLY transaction 3. Executes the query 4. Returns results (limited to max_rows) 5. Rolls back and closes the connect
(
sid: int,
did: int,
query: str,
max_rows: int = 1000
)
| 310 | |
| 311 | |
| 312 | def execute_readonly_query( |
| 313 | sid: int, |
| 314 | did: int, |
| 315 | query: str, |
| 316 | max_rows: int = 1000 |
| 317 | ) -> dict: |
| 318 | """ |
| 319 | Execute a read-only SQL query against a PostgreSQL database. |
| 320 | |
| 321 | This function: |
| 322 | 1. Opens a new connection with LLM-specific application_name |
| 323 | 2. Starts a READ ONLY transaction |
| 324 | 3. Executes the query |
| 325 | 4. Returns results (limited to max_rows) |
| 326 | 5. Rolls back and closes the connection |
| 327 | |
| 328 | Args: |
| 329 | sid: Server ID from the Object Explorer |
| 330 | did: Database ID (OID) from the Object Explorer |
| 331 | query: SQL query to execute (should be SELECT or read-only) |
| 332 | max_rows: Maximum number of rows to return (default 1000) |
| 333 | |
| 334 | Returns: |
| 335 | Dictionary containing: |
| 336 | - columns: List of column names |
| 337 | - rows: List of row data (as lists) |
| 338 | - row_count: Number of rows returned |
| 339 | - truncated: True if results were limited |
| 340 | |
| 341 | Raises: |
| 342 | DatabaseToolError: If the query is rejected by validation, the |
| 343 | query fails at runtime, or a connection cannot be |
| 344 | established. |
| 345 | """ |
| 346 | # Validate the LLM-supplied query before allocating a connection. |
| 347 | # The BEGIN TRANSACTION READ ONLY wrapper below is only effective |
| 348 | # if the query is a single read-only statement; see |
| 349 | # _validate_readonly_query for the threat model and rules. |
| 350 | _validate_readonly_query(query) |
| 351 | |
| 352 | # Generate unique connection ID for this LLM query |
| 353 | conn_id = f"llm_{secrets.choice(range(1, 9999999))}" |
| 354 | |
| 355 | manager = None |
| 356 | conn = None |
| 357 | |
| 358 | try: |
| 359 | # Get connection manager and connection object |
| 360 | manager, conn = _get_connection(sid, did, conn_id) |
| 361 | |
| 362 | # Connect with read-only settings |
| 363 | status, error = _connect_readonly(manager, conn, conn_id) |
| 364 | if not status: |
| 365 | raise DatabaseToolError( |
| 366 | f"Connection failed: {error}", |
| 367 | code="CONNECTION_ERROR" |
| 368 | ) |
| 369 |
no test coverage detected