Skip to content
This repository

Bind support and query debug fix #94

Closed
wants to merge 2 commits into from

2 participants

Ievgen Voloshchuk Armin Ronacher
Ievgen Voloshchuk

1) get_debug_queries does not provide enough information for the query executing in a setup with multiple binds available.
This fix makes bind information available for debugging.
2) Query debugging is not compatible with sqlalchemy >= 7.0.
The fix adds proper way of debugging queries though new api.

Armin Ronacher
Owner

No longer applies.

Armin Ronacher mitsuhiko closed this July 20, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 67 additions and 14 deletions. Show diff stats Hide diff stats

  1. 81  flask_sqlalchemy.py
81  flask_sqlalchemy.py
@@ -9,6 +9,7 @@
9 9
     :license: BSD, see LICENSE for more details.
10 10
 """
11 11
 from __future__ import with_statement, absolute_import
  12
+from distutils.version import StrictVersion
12 13
 import os
13 14
 import re
14 15
 import sys
@@ -103,16 +104,18 @@ class _DebugQueryTuple(tuple):
103 104
     start_time = property(itemgetter(2))
104 105
     end_time = property(itemgetter(3))
105 106
     context = property(itemgetter(4))
  107
+    bind = property(itemgetter(5))
106 108
 
107 109
     @property
108 110
     def duration(self):
109 111
         return self.end_time - self.start_time
110 112
 
111 113
     def __repr__(self):
112  
-        return '<query statement="%s" parameters=%r duration=%.03f>' % (
  114
+        return '<query statement="%s" parameters=%r duration=%.03f bind=%r>' % (
113 115
             self.statement,
114 116
             self.parameters,
115  
-            self.duration
  117
+            self.duration,
  118
+            self.bind
116 119
         )
117 120
 
118 121
 
@@ -131,11 +134,25 @@ def _calling_context(app_path):
131 134
     return '<unknown>'
132 135
 
133 136
 
  137
+def _add_debug_query(statement, parameters, start, app_package, bind):
  138
+    """Remember query debug information"""
  139
+    ctx = connection_stack.top
  140
+    if ctx is not None:
  141
+        queries = getattr(ctx, 'sqlalchemy_queries', None)
  142
+        if queries is None:
  143
+            queries = []
  144
+            setattr(ctx, 'sqlalchemy_queries', queries)
  145
+        queries.append(_DebugQueryTuple((
  146
+            statement, parameters, start, _timer(),
  147
+            _calling_context(app_package), bind)))
  148
+
  149
+
134 150
 class _ConnectionDebugProxy(ConnectionProxy):
135 151
     """Helps debugging the database."""
136 152
 
137  
-    def __init__(self, import_name):
  153
+    def __init__(self, import_name, bind):
138 154
         self.app_package = import_name
  155
+        self.bind = bind
139 156
 
140 157
     def cursor_execute(self, execute, cursor, statement, parameters,
141 158
                        context, executemany):
@@ -143,15 +160,44 @@ def cursor_execute(self, execute, cursor, statement, parameters,
143 160
         try:
144 161
             return execute(cursor, statement, parameters, context)
145 162
         finally:
146  
-            ctx = connection_stack.top
147  
-            if ctx is not None:
148  
-                queries = getattr(ctx, 'sqlalchemy_queries', None)
149  
-                if queries is None:
150  
-                    queries = []
151  
-                    setattr(ctx, 'sqlalchemy_queries', queries)
152  
-                queries.append(_DebugQueryTuple((
153  
-                    statement, parameters, start, _timer(),
154  
-                    _calling_context(self.app_package))))
  163
+            _add_debug_query(statement, parameters, start,
  164
+                self.app_package, self.bind)
  165
+
  166
+
  167
+class _QueryDebugger(ConnectionProxy):
  168
+    """Helps debugging the database."""
  169
+
  170
+    _instance = None
  171
+
  172
+    def __new__(cls, *args, **kwargs):
  173
+        """Use singleton to make sure that handlers are attached only once"""
  174
+        if not cls._instance:
  175
+            cls._instance = super(_QueryDebugger, cls).__new__(
  176
+                cls, *args, **kwargs)
  177
+
  178
+            from sqlalchemy import event
  179
+            from sqlalchemy.engine import Engine
  180
+
  181
+            @event.listens_for(Engine, "before_cursor_execute")
  182
+            def before_cursor_execute(conn, cursor, statement,
  183
+                                      parameters, context, executemany):
  184
+                context._query_start_time = _timer()
  185
+
  186
+            @event.listens_for(Engine, "after_cursor_execute")
  187
+            def after_cursor_execute(conn, cursor, statement,
  188
+                                     parameters, context, executemany):
  189
+                app_packege = cls._instance.engines[conn.engine]['app_package']
  190
+                bind = cls._instance.engines[conn.engine]['bind']
  191
+                _add_debug_query(statement, parameters,
  192
+                    context._query_start_time, app_packege, bind)
  193
+
  194
+        return cls._instance
  195
+
  196
+    engines = {}
  197
+
  198
+    def __init__(self, engine, import_name, bind):
  199
+        """Remember import name and bind for an engine"""
  200
+        self.engines[engine] = {'app_package': import_name, 'bind': bind}
155 201
 
156 202
 
157 203
 class _SignalTrackingMapperExtension(MapperExtension):
@@ -436,12 +482,19 @@ def get_engine(self):
436 482
             options = {'convert_unicode': True}
437 483
             self._sa.apply_pool_defaults(self._app, options)
438 484
             self._sa.apply_driver_hacks(self._app, info, options)
439  
-            if _record_queries(self._app):
440  
-                options['proxy'] = _ConnectionDebugProxy(self._app.import_name)
  485
+            if (_record_queries(self._app) and
  486
+                    StrictVersion(sqlalchemy.__version__) <
  487
+                        StrictVersion('0.7.0')):
  488
+                options['proxy'] = _ConnectionDebugProxy(self._app.import_name,
  489
+                    self._bind)
441 490
             if echo:
442 491
                 options['echo'] = True
443 492
             self._engine = rv = sqlalchemy.create_engine(info, **options)
444 493
             self._connected_for = (uri, echo)
  494
+            if (_record_queries(self._app) and
  495
+                    StrictVersion(sqlalchemy.__version__) >=
  496
+                        StrictVersion('0.7.0')):
  497
+                _QueryDebugger(rv, self._app.import_name, self._bind)
445 498
             return rv
446 499
 
447 500
 
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.