Coverage for python / lsst / daf / butler / registry / interfaces / _database_explain.py: 48%
19 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-24 08:16 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-24 08:16 +0000
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28from __future__ import annotations
30from typing import Any
32from sqlalchemy import ClauseElement, Connection, Executable
33from sqlalchemy.ext.compiler import compiles
34from sqlalchemy.sql.compiler import SQLCompiler
37def get_query_plan(connection: Connection, sql: ClauseElement) -> str:
38 """Retrieve the query plan for a given statement from the DB as a
39 human-readable string.
41 Parameters
42 ----------
43 connection : `sqlalchemy.Connection`
44 Database connection used to retrieve query plan.
45 sql : `sqlalchemy.ClauseElement`
46 SQL statement for which we will retrieve a query plan.
47 """
48 if connection.dialect.name != "postgresql":
49 # This could be implemented for SQLite using its EXPLAIN QUERY PLAN
50 # syntax, but the result rows are a little different and we haven't had
51 # a need for it yet.
52 return "(not available)"
54 with connection.execute(_Explain(sql)) as explain_cursor:
55 lines = explain_cursor.scalars().all()
56 return "\n".join(lines)
59# This is based on code from the sqlalchemy wiki at
60# https://github.com/sqlalchemy/sqlalchemy/wiki/Query-Plan-SQL-construct
61class _Explain(Executable, ClauseElement):
62 """Custom SQLAlchemy construct for retrieving query plan from the DB.
64 Parameters
65 ----------
66 statement : `sqlalchemy.ClauseElement`
67 SQLAlchemy SELECT statement to retrieve query plan for.
68 """
70 def __init__(self, statement: ClauseElement) -> None:
71 self.statement = statement
74@compiles(_Explain, "postgresql")
75def _compile_explain(element: _Explain, compiler: SQLCompiler, **kw: Any) -> str:
76 text = "EXPLAIN (VERBOSE TRUE, SETTINGS TRUE)"
77 text += compiler.process(element.statement, **kw)
79 return text