Add support for all postgres explain options
This commit is contained in:
		| @@ -1,26 +1,66 @@ | ||||
| import sqlalchemy as sa | ||||
| from sqlalchemy.sql import expression | ||||
| from sqlalchemy.sql.expression import Executable, ClauseElement, _literal_as_text | ||||
| from sqlalchemy.sql.expression import ( | ||||
|     Executable, | ||||
|     ClauseElement, | ||||
|     _literal_as_text | ||||
| ) | ||||
| from sqlalchemy.ext.compiler import compiles | ||||
| from sqlalchemy_utils.types import TSVectorType | ||||
|  | ||||
|  | ||||
| class explain(Executable, ClauseElement): | ||||
|     def __init__(self, stmt, analyze=False): | ||||
|     """ | ||||
|     Define EXPLAIN element. | ||||
|  | ||||
|     http://www.postgresql.org/docs/devel/static/sql-explain.html | ||||
|     """ | ||||
|     def __init__( | ||||
|         self, | ||||
|         stmt, | ||||
|         analyze=False, | ||||
|         verbose=False, | ||||
|         costs=True, | ||||
|         buffers=False, | ||||
|         timing=True, | ||||
|         format='text' | ||||
|     ): | ||||
|         self.statement = _literal_as_text(stmt) | ||||
|         self.analyze = analyze | ||||
|         self.verbose = verbose | ||||
|         self.costs = costs | ||||
|         self.buffers = buffers | ||||
|         self.timing = timing | ||||
|         self.format = format | ||||
|  | ||||
|  | ||||
| class explain_analyze(explain): | ||||
|     def __init__(self, stmt): | ||||
|         super(explain_analyze, self).__init__(stmt, analyze=True) | ||||
|     def __init__(self, stmt, **kwargs): | ||||
|         super(explain_analyze, self).__init__( | ||||
|             stmt, | ||||
|             analyze=True, | ||||
|             **kwargs | ||||
|         ) | ||||
|  | ||||
|  | ||||
| @compiles(explain, 'postgresql') | ||||
| def pg_explain(element, compiler, **kw): | ||||
|     text = "EXPLAIN " | ||||
|     options = [] | ||||
|     if element.analyze: | ||||
|         text += "ANALYZE " | ||||
|         options.append('ANALYZE true') | ||||
|     if not element.timing: | ||||
|         options.append('TIMING false') | ||||
|     if element.buffers: | ||||
|         options.append('BUFFERS true') | ||||
|     if element.format != 'text': | ||||
|         options.append('FORMAT %s' % element.format) | ||||
|     if element.verbose: | ||||
|         options.append('VERBOSE true') | ||||
|     if not element.costs: | ||||
|         options.append('COSTS false') | ||||
|     if options: | ||||
|         text += '(%s) ' % ', '.join(options) | ||||
|     text += compiler.process(element.statement) | ||||
|     return text | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ try: | ||||
| except: | ||||
|     pass | ||||
| from sqlalchemy import types | ||||
| from sqlalchemy_utils import ImproperlyConfigured | ||||
| from sqlalchemy_utils.exceptions import ImproperlyConfigured | ||||
| from .scalar_coercible import ScalarCoercible | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import six | ||||
| from sqlalchemy import types | ||||
| from sqlalchemy_utils import ImproperlyConfigured | ||||
| from sqlalchemy_utils.exceptions import ImproperlyConfigured | ||||
| from .scalar_coercible import ScalarCoercible | ||||
|  | ||||
| colour = None | ||||
|   | ||||
| @@ -11,7 +11,7 @@ except ImportError: | ||||
|  | ||||
|  | ||||
| from sqlalchemy import types | ||||
| from sqlalchemy_utils import ImproperlyConfigured | ||||
| from sqlalchemy_utils.exceptions import ImproperlyConfigured | ||||
| from .scalar_coercible import ScalarCoercible | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import six | ||||
| import weakref | ||||
| from sqlalchemy_utils import ImproperlyConfigured | ||||
| from sqlalchemy_utils.exceptions import ImproperlyConfigured | ||||
| from sqlalchemy import types | ||||
| from sqlalchemy.dialects import postgresql, oracle | ||||
| from .scalar_coercible import ScalarCoercible | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import six | ||||
| from sqlalchemy import types | ||||
| from sqlalchemy_utils import ImproperlyConfigured | ||||
| from sqlalchemy_utils.exceptions import ImproperlyConfigured | ||||
| from .scalar_coercible import ScalarCoercible | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import six | ||||
| from sqlalchemy import types | ||||
| from sqlalchemy_utils import ImproperlyConfigured | ||||
| from sqlalchemy_utils.exceptions import ImproperlyConfigured | ||||
| from .scalar_coercible import ScalarCoercible | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -27,22 +27,62 @@ class ExpressionTestCase(TestCase): | ||||
|  | ||||
|         self.Article = Article | ||||
|  | ||||
|     def assert_startswith(self, query, query_part): | ||||
|         assert str( | ||||
|             query.compile(dialect=postgresql.dialect()) | ||||
|         ).startswith(query_part) | ||||
|         # Check that query executes properly | ||||
|         self.session.execute(query) | ||||
|  | ||||
|  | ||||
| class TestExplain(ExpressionTestCase): | ||||
|     def test_render_explain(self): | ||||
|         assert str( | ||||
|             explain(self.session.query(self.Article)).compile( | ||||
|                 dialect=postgresql.dialect() | ||||
|             ) | ||||
|         ).startswith('EXPLAIN SELECT') | ||||
|         self.assert_startswith( | ||||
|             explain(self.session.query(self.Article)), | ||||
|             'EXPLAIN SELECT' | ||||
|         ) | ||||
|  | ||||
|     def test_render_explain_with_analyze(self): | ||||
|         assert str( | ||||
|             explain(self.session.query(self.Article), analyze=True) | ||||
|             .compile( | ||||
|                 dialect=postgresql.dialect() | ||||
|             ) | ||||
|         ).startswith('EXPLAIN ANALYZE SELECT') | ||||
|         self.assert_startswith( | ||||
|             explain(self.session.query(self.Article), analyze=True), | ||||
|             'EXPLAIN (ANALYZE true) SELECT' | ||||
|         ) | ||||
|  | ||||
|     def test_with_string_as_stmt_param(self): | ||||
|         self.assert_startswith( | ||||
|             explain('SELECT 1 FROM article'), | ||||
|             'EXPLAIN SELECT' | ||||
|         ) | ||||
|  | ||||
|     def test_format(self): | ||||
|         self.assert_startswith( | ||||
|             explain('SELECT 1 FROM article', format='json'), | ||||
|             'EXPLAIN (FORMAT json) SELECT' | ||||
|         ) | ||||
|  | ||||
|     def test_timing(self): | ||||
|         self.assert_startswith( | ||||
|             explain('SELECT 1 FROM article', analyze=True, timing=False), | ||||
|             'EXPLAIN (ANALYZE true, TIMING false) SELECT' | ||||
|         ) | ||||
|  | ||||
|     def test_verbose(self): | ||||
|         self.assert_startswith( | ||||
|             explain('SELECT 1 FROM article', verbose=True), | ||||
|             'EXPLAIN (VERBOSE true) SELECT' | ||||
|         ) | ||||
|  | ||||
|     def test_buffers(self): | ||||
|         self.assert_startswith( | ||||
|             explain('SELECT 1 FROM article', analyze=True, buffers=True), | ||||
|             'EXPLAIN (ANALYZE true, BUFFERS true) SELECT' | ||||
|         ) | ||||
|  | ||||
|     def test_costs(self): | ||||
|         self.assert_startswith( | ||||
|             explain('SELECT 1 FROM article', costs=False), | ||||
|             'EXPLAIN (COSTS false) SELECT' | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class TestExplainAnalyze(ExpressionTestCase): | ||||
| @@ -52,7 +92,7 @@ class TestExplainAnalyze(ExpressionTestCase): | ||||
|             .compile( | ||||
|                 dialect=postgresql.dialect() | ||||
|             ) | ||||
|         ).startswith('EXPLAIN ANALYZE SELECT') | ||||
|         ).startswith('EXPLAIN (ANALYZE true) SELECT') | ||||
|  | ||||
|  | ||||
| class TestMatchTSVector(ExpressionTestCase): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Konsta Vesterinen
					Konsta Vesterinen