From: Mike Bayer Date: Sun, 18 Oct 2009 16:48:46 +0000 (+0000) Subject: - initial MySQL Connector/Python driver X-Git-Tag: rel_0_6beta1~245 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=facb6516e9def708c2b53dcf65b3d2e3c8169744;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - initial MySQL Connector/Python driver - support exceptions raised in dialect initialize phase - provide default dialect create_connect_args() method --- diff --git a/lib/sqlalchemy/dialects/mysql/__init__.py b/lib/sqlalchemy/dialects/mysql/__init__.py index fbd238b1a6..9ade1a65b0 100644 --- a/lib/sqlalchemy/dialects/mysql/__init__.py +++ b/lib/sqlalchemy/dialects/mysql/__init__.py @@ -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 index 0000000000..f8e6d1254b --- /dev/null +++ b/lib/sqlalchemy/dialects/mysql/myconnpy.py @@ -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 diff --git a/lib/sqlalchemy/dialects/mysql/mysqldb.py b/lib/sqlalchemy/dialects/mysql/mysqldb.py index a7764add73..49fa044a3e 100644 --- a/lib/sqlalchemy/dialects/mysql/mysqldb.py +++ b/lib/sqlalchemy/dialects/mysql/mysqldb.py @@ -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 = [] diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 829f975582..643faa982b 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -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: diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index ad728da9c6..12391c0057 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -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