Commits

jason kirtland committed 453e2a6

Merged lower case caching, fetching from r2955
Be sure to close rows fetched in reflection (if not autoclosed)
Fixed bind test, needed transactional storage engine for mysql

Comments (0)

Files changed (2)

lib/sqlalchemy/databases/mysql.py

 # This module is part of SQLAlchemy and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-import re, datetime, inspect, warnings
+import re, datetime, inspect, warnings, weakref
 
-from sqlalchemy import sql,schema,ansisql
+from sqlalchemy import sql, schema, ansisql
 from sqlalchemy.engine import default
 import sqlalchemy.types as sqltypes
 import sqlalchemy.exceptions as exceptions
 import sqlalchemy.util as util
 from array import array as _array
 
+try:
+    from threading import Lock
+except ImportError:
+    from dummy_threading import Lock
+
+
 RESERVED_WORDS = util.Set(
     ['accessible', 'add', 'all', 'alter', 'analyze','and', 'as', 'asc',
      'asensitive', 'before', 'between', 'bigint', 'binary', 'blob', 'both',
      'read_only', 'read_write', # 5.1
      ])
 
+_per_connection_mutex = Lock()
+
 class _NumericType(object):
     "Base for MySQL numeric types."
 
 class MySQLDialect(ansisql.ANSIDialect):
     def __init__(self, **kwargs):
         ansisql.ANSIDialect.__init__(self, default_paramstyle='format', **kwargs)
+        self.per_connection = weakref.WeakKeyDictionary()
 
     def dbapi(cls):
         import MySQLdb as mysql
         """Load column definitions from the server."""
 
         decode_from = self._detect_charset(connection)
-
-        # reference:
-        # http://dev.mysql.com/doc/refman/5.0/en/name-case-sensitivity.html
-        row = _compat_fetch(connection.execute(
-            "SHOW VARIABLES LIKE 'lower_case_table_names'"),
-                            one=True, charset=decode_from)
-        if not row:
-            case_sensitive = True
-        else:
-            case_sensitive = row[1] in ('0', 'OFF' 'off')
+        case_sensitive = self._detect_case_sensitive(connection, decode_from)
 
         if not case_sensitive:
             table.name = table.name.lower()
                 raise exceptions.NoSuchTableError(table.fullname)
             raise
 
-        for row in _compat_fetch(rp, charset=decode_from):
+        for row in _compat_fetchall(rp, charset=decode_from):
             (name, type, nullable, primary_key, default) = \
                    (row[0], row[1], row[2] == 'YES', row[3] == 'PRI', row[4])
 
         """SHOW CREATE TABLE to get foreign key/table options."""
 
         rp = connection.execute("SHOW CREATE TABLE " + self._escape_table_name(table), {})
-        row = _compat_fetch(rp, one=True, charset=charset)
+        row = _compat_fetchone(rp, charset=charset)
         if not row:
             raise exceptions.NoSuchTableError(table.fullname)
         desc = row[1].strip()
+        row.close()
 
         tabletype = ''
         lastparen = re.search(r'\)[^\)]*\Z', desc)
         # on a connection via set_character_set()
         
         rs = connection.execute("show variables like 'character_set%%'")
-        opts = dict([(row[0], row[1]) for row in _compat_fetch(rs)])
+        opts = dict([(row[0], row[1]) for row in _compat_fetchall(rs)])
 
         if 'character_set_results' in opts:
             return opts['character_set_results']
                 warnings.warn(RuntimeWarning("Could not detect the connection character set with this combination of MySQL server and MySQL-python.  MySQL-python >= 1.2.2 is recommended.  Assuming latin1."))
                 return 'latin1'
 
-def _compat_fetch(rp, one=False, charset=None):
+    def _detect_case_sensitive(self, connection, charset=None):
+        """Sniff out identifier case sensitivity.
+
+        Cached per-connection. This value can not change without a server
+        restart.
+        """
+        # http://dev.mysql.com/doc/refman/5.0/en/name-case-sensitivity.html
+
+        _per_connection_mutex.acquire()
+        try:
+            raw_connection = connection.connection.connection
+            cache = self.per_connection.get(raw_connection, {})
+            if 'lower_case_table_names' not in cache:
+                row = _compat_fetchone(connection.execute(
+                        "SHOW VARIABLES LIKE 'lower_case_table_names'"),
+                        charset=charset)
+                if not row:
+                    cs = True
+                else:
+                    cs = row[1] in ('0', 'OFF' 'off')
+                    row.close()
+                cache['lower_case_table_names'] = cs
+                self.per_connection[raw_connection] = cache
+            return cache.get('lower_case_table_names')
+        finally:
+            _per_connection_mutex.release()
+
+def _compat_fetchall(rp, charset=None):
     """Proxy result rows to smooth over MySQL-Python driver inconsistencies."""
 
-    if one:
-        return _MySQLPythonRowProxy(rp.fetchone(), charset)
-    else:
-        return [_MySQLPythonRowProxy(row, charset) for row in rp.fetchall()]
+    return [_MySQLPythonRowProxy(row, charset) for row in rp.fetchall()]
+
+def _compat_fetchone(rp, charset=None):
+    """Proxy a result row to smooth over MySQL-Python driver inconsistencies."""
+
+    return _MySQLPythonRowProxy(rp.fetchone(), charset)
         
 
 class _MySQLPythonRowProxy(object):

test/engine/bind.py

             ):
                 metadata = MetaData(*args[0], **args[1])
                 table = Table('test_table', metadata,   
-                    Column('foo', Integer))
+                              Column('foo', Integer))
 
                 assert metadata.bind is metadata.engine is table.bind is table.engine is bind
                 metadata.create_all()
     def test_implicit_execution(self):
         metadata = MetaData()
         table = Table('test_table', metadata,   
-            Column('foo', Integer))
+            Column('foo', Integer),
+            mysql_engine='InnoDB')
         conn = testbase.db.connect()
         metadata.create_all(bind=conn)
         try:
         
                
 if __name__ == '__main__':
-    testbase.main()
+    testbase.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.