]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- initial MySQL Connector/Python driver
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 18 Oct 2009 16:48:46 +0000 (16:48 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 18 Oct 2009 16:48:46 +0000 (16:48 +0000)
- support exceptions raised in dialect initialize phase
- provide default dialect create_connect_args() method

lib/sqlalchemy/dialects/mysql/__init__.py
lib/sqlalchemy/dialects/mysql/myconnpy.py [new file with mode: 0644]
lib/sqlalchemy/dialects/mysql/mysqldb.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py

index fbd238b1a633735cfaa0749f5d15c96aff6c838b..9ade1a65b05b0e0e70b2984dedc65981ff4135c2 100644 (file)
@@ -1,4 +1,4 @@
-from sqlalchemy.dialects.mysql import base, mysqldb, pyodbc, zxjdbc
+from sqlalchemy.dialects.mysql import base, mysqldb, pyodbc, zxjdbc, myconnpy
 
 # default dialect
 base.dialect = mysqldb.dialect
diff --git a/lib/sqlalchemy/dialects/mysql/myconnpy.py b/lib/sqlalchemy/dialects/mysql/myconnpy.py
new file mode 100644 (file)
index 0000000..f8e6d12
--- /dev/null
@@ -0,0 +1,109 @@
+"""Support for the MySQL database via the MySQL Connector/Python adapter.
+
+This dialect is in development pending further progress on this
+new DBAPI.
+
+current issue (2009-10-18):
+
+fetchone() does not obey PEP 249
+
+https://bugs.launchpad.net/myconnpy/+bug/454782
+
+"""
+
+import re
+
+from sqlalchemy.dialects.mysql.base import MySQLDialect, MySQLExecutionContext,\
+                                            MySQLCompiler, MySQLIdentifierPreparer
+                                            
+from sqlalchemy.engine import base as engine_base, default
+from sqlalchemy.sql import operators as sql_operators
+from sqlalchemy import exc, log, schema, sql, types as sqltypes, util
+
+class MySQL_myconnpyExecutionContext(MySQLExecutionContext):
+    # DBAPI BUG:
+    # fetchone() improperly raises an exception when no rows remain
+    
+    
+    def get_lastrowid(self):
+        # DBAPI BUG: wrong name of attribute
+        # https://bugs.launchpad.net/myconnpy/+bug/454782
+        return self.cursor._lastrowid
+        
+        # this is the fallback approach.
+#        cursor = self.create_cursor()
+#        cursor.execute("SELECT LAST_INSERT_ID()")
+#        lastrowid = cursor.fetchone()[0]
+#        cursor.close()
+#        return lastrowid
+    
+        
+class MySQL_myconnpyCompiler(MySQLCompiler):
+    def visit_mod(self, binary, **kw):
+        return self.process(binary.left) + " %% " + self.process(binary.right)
+    
+    def post_process_text(self, text):
+        return text.replace('%', '%%')
+
+
+class MySQL_myconnpyIdentifierPreparer(MySQLIdentifierPreparer):
+    
+    def _escape_identifier(self, value):
+        value = value.replace(self.escape_quote, self.escape_to_quote)
+        return value.replace("%", "%%")
+
+class MySQL_myconnpy(MySQLDialect):
+    driver = 'myconnpy'
+    supports_unicode_statements = False
+    supports_sane_rowcount = True
+    supports_sane_multi_rowcount = True
+
+    default_paramstyle = 'format'
+    execution_ctx_cls = MySQL_myconnpyExecutionContext
+    statement_compiler = MySQL_myconnpyCompiler
+    
+    preparer = MySQL_myconnpyIdentifierPreparer
+    
+    def __init__(self, **kw):
+        # DBAPI BUG:
+        # named parameters don't work:
+        # "Parameters must be given as a sequence."
+        # https://bugs.launchpad.net/myconnpy/+bug/454782
+        kw['paramstyle'] = 'format'
+        MySQLDialect.__init__(self, **kw)
+        
+    @classmethod
+    def dbapi(cls):
+        from mysql import connector
+        return connector
+
+    def create_connect_args(self, url):
+        opts = url.translate_connect_args(username='user')
+        opts.update(url.query)
+        return [[], opts]
+
+    def _get_server_version_info(self, connection):
+        dbapi_con = connection.connection
+        version = []
+        r = re.compile('[.\-]')
+        for n in r.split(dbapi_con.get_server_version()):
+            try:
+                version.append(int(n))
+            except ValueError:
+                version.append(n)
+        return tuple(version)
+
+    def _detect_charset(self, connection):
+        """Sniff out the character set in use for connection results."""
+        
+        return connection.connection.get_characterset_info()
+
+    def _extract_error_code(self, exception):
+        m = re.compile(r"\(.*\)\s+(\d+)").search(str(exception))
+        c = m.group(1)
+        if c:
+            return int(c)
+        else:
+            return None
+
+dialect = MySQL_myconnpy
index a7764add7329f6eb4cdfe3027c5fa629eeb9c579..49fa044a3e038d4ee9260011c26614136f92f1a0 100644 (file)
@@ -139,9 +139,6 @@ class MySQL_mysqldb(MySQLDialect):
             opts['client_flag'] = client_flag
         return [[], opts]
     
-    def do_ping(self, connection):
-        connection.ping()
-
     def _get_server_version_info(self, connection):
         dbapi_con = connection.connection
         version = []
index 829f975582c4435bdfb0c7c851faeefcc59a928d..643faa982b48472cc14375551b432ad2aaa48aed 100644 (file)
@@ -880,7 +880,9 @@ class Connection(Connectable):
             raise
 
     def _rollback_impl(self):
-        if not self.closed and not self.invalidated and self.__connection.is_valid:
+        # use getattr() for is_valid to support exceptions raised in dialect initializer, 
+        # where we do not yet have the pool wrappers plugged in
+        if not self.closed and not self.invalidated and getattr(self.__connection, 'is_valid', False):
             if self.engine._should_log_info:
                 self.engine.logger.info("ROLLBACK")
             try:
index ad728da9c6645b422b081185b7472d05395c6890..12391c00572cc3fb8205817703973a454fbfe4a8 100644 (file)
@@ -130,6 +130,11 @@ class DefaultDialect(base.Dialect):
     def connect(self, *cargs, **cparams):
         return self.dbapi.connect(*cargs, **cparams)
 
+    def create_connect_args(self, url):
+        opts = url.translate_connect_args()
+        opts.update(url.query)
+        return [[], opts]
+
     def do_begin(self, connection):
         """Implementations might want to put logic here for turning
         autocommit on/off, etc.
@@ -239,7 +244,6 @@ class DefaultExecutionContext(base.ExecutionContext):
             if self.isinsert or self.isupdate:
                 self.__process_defaults()
             self.parameters = self.__convert_compiled_params(self.compiled_parameters)
-
         elif statement is not None:
             # plain text statement
             self.result_map = self.compiled = None