summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-05-30 18:06:09 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-05-30 18:06:09 -0400
commit809a7890a1cc1c957ae2263bdcbda2d42f5372ba (patch)
tree01ed5e92e11564511db30e3521abf77bb25bb4ef /lib/sqlalchemy
parent814637e291953bc7e05ced3e215ef33bde5b040a (diff)
downloadsqlalchemy-809a7890a1cc1c957ae2263bdcbda2d42f5372ba.tar.gz
- remove drizzle dialect
- restore mysqldb fully within dialects/mysql/, it's no longer a connector. fixes #2984
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/connectors/mysqldb.py144
-rw-r--r--lib/sqlalchemy/databases/__init__.py2
-rw-r--r--lib/sqlalchemy/dialects/__init__.py1
-rw-r--r--lib/sqlalchemy/dialects/drizzle/__init__.py22
-rw-r--r--lib/sqlalchemy/dialects/drizzle/base.py498
-rw-r--r--lib/sqlalchemy/dialects/drizzle/mysqldb.py48
-rw-r--r--lib/sqlalchemy/dialects/mysql/mysqldb.py143
7 files changed, 123 insertions, 735 deletions
diff --git a/lib/sqlalchemy/connectors/mysqldb.py b/lib/sqlalchemy/connectors/mysqldb.py
deleted file mode 100644
index e4efb2201..000000000
--- a/lib/sqlalchemy/connectors/mysqldb.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# connectors/mysqldb.py
-# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-
-"""Define behaviors common to MySQLdb dialects.
-
-Currently includes MySQL and Drizzle.
-
-"""
-
-from . import Connector
-from ..engine import base as engine_base, default
-from ..sql import operators as sql_operators
-from .. import exc, log, schema, sql, types as sqltypes, util, processors
-import re
-
-
-# the subclassing of Connector by all classes
-# here is not strictly necessary
-
-
-class MySQLDBExecutionContext(Connector):
-
- @property
- def rowcount(self):
- if hasattr(self, '_rowcount'):
- return self._rowcount
- else:
- return self.cursor.rowcount
-
-
-class MySQLDBCompiler(Connector):
- def visit_mod_binary(self, binary, operator, **kw):
- return self.process(binary.left, **kw) + " %% " + \
- self.process(binary.right, **kw)
-
- def post_process_text(self, text):
- return text.replace('%', '%%')
-
-
-class MySQLDBIdentifierPreparer(Connector):
-
- def _escape_identifier(self, value):
- value = value.replace(self.escape_quote, self.escape_to_quote)
- return value.replace("%", "%%")
-
-
-class MySQLDBConnector(Connector):
- driver = 'mysqldb'
- supports_unicode_statements = False
- supports_sane_rowcount = True
- supports_sane_multi_rowcount = True
-
- supports_native_decimal = True
-
- default_paramstyle = 'format'
-
- @classmethod
- def dbapi(cls):
- # is overridden when pymysql is used
- return __import__('MySQLdb')
-
-
- def do_executemany(self, cursor, statement, parameters, context=None):
- rowcount = cursor.executemany(statement, parameters)
- if context is not None:
- context._rowcount = rowcount
-
- def create_connect_args(self, url):
- opts = url.translate_connect_args(database='db', username='user',
- password='passwd')
- opts.update(url.query)
-
- util.coerce_kw_type(opts, 'compress', bool)
- util.coerce_kw_type(opts, 'connect_timeout', int)
- util.coerce_kw_type(opts, 'read_timeout', int)
- util.coerce_kw_type(opts, 'client_flag', int)
- util.coerce_kw_type(opts, 'local_infile', int)
- # Note: using either of the below will cause all strings to be returned
- # as Unicode, both in raw SQL operations and with column types like
- # String and MSString.
- util.coerce_kw_type(opts, 'use_unicode', bool)
- util.coerce_kw_type(opts, 'charset', str)
-
- # Rich values 'cursorclass' and 'conv' are not supported via
- # query string.
-
- ssl = {}
- keys = ['ssl_ca', 'ssl_key', 'ssl_cert', 'ssl_capath', 'ssl_cipher']
- for key in keys:
- if key in opts:
- ssl[key[4:]] = opts[key]
- util.coerce_kw_type(ssl, key[4:], str)
- del opts[key]
- if ssl:
- opts['ssl'] = ssl
-
- # FOUND_ROWS must be set in CLIENT_FLAGS to enable
- # supports_sane_rowcount.
- client_flag = opts.get('client_flag', 0)
- if self.dbapi is not None:
- try:
- CLIENT_FLAGS = __import__(
- self.dbapi.__name__ + '.constants.CLIENT'
- ).constants.CLIENT
- client_flag |= CLIENT_FLAGS.FOUND_ROWS
- except (AttributeError, ImportError):
- self.supports_sane_rowcount = False
- opts['client_flag'] = client_flag
- 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_info()):
- try:
- version.append(int(n))
- except ValueError:
- version.append(n)
- return tuple(version)
-
- def _extract_error_code(self, exception):
- return exception.args[0]
-
- def _detect_charset(self, connection):
- """Sniff out the character set in use for connection results."""
-
- try:
- # note: the SQL here would be
- # "SHOW VARIABLES LIKE 'character_set%%'"
- cset_name = connection.connection.character_set_name
- except AttributeError:
- util.warn(
- "No 'character_set_name' can be detected with "
- "this MySQL-Python version; "
- "please upgrade to a recent version of MySQL-Python. "
- "Assuming latin1.")
- return 'latin1'
- else:
- return cset_name()
-
diff --git a/lib/sqlalchemy/databases/__init__.py b/lib/sqlalchemy/databases/__init__.py
index 915eefa4a..57de841fa 100644
--- a/lib/sqlalchemy/databases/__init__.py
+++ b/lib/sqlalchemy/databases/__init__.py
@@ -12,7 +12,6 @@ from ..dialects.sqlite import base as sqlite
from ..dialects.postgresql import base as postgresql
postgres = postgresql
from ..dialects.mysql import base as mysql
-from ..dialects.drizzle import base as drizzle
from ..dialects.oracle import base as oracle
from ..dialects.firebird import base as firebird
from ..dialects.mssql import base as mssql
@@ -20,7 +19,6 @@ from ..dialects.sybase import base as sybase
__all__ = (
- 'drizzle',
'firebird',
'mssql',
'mysql',
diff --git a/lib/sqlalchemy/dialects/__init__.py b/lib/sqlalchemy/dialects/__init__.py
index 974d4f787..b5ef85aba 100644
--- a/lib/sqlalchemy/dialects/__init__.py
+++ b/lib/sqlalchemy/dialects/__init__.py
@@ -5,7 +5,6 @@
# the MIT License: http://www.opensource.org/licenses/mit-license.php
__all__ = (
- 'drizzle',
'firebird',
'mssql',
'mysql',
diff --git a/lib/sqlalchemy/dialects/drizzle/__init__.py b/lib/sqlalchemy/dialects/drizzle/__init__.py
deleted file mode 100644
index 1392b8e28..000000000
--- a/lib/sqlalchemy/dialects/drizzle/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from sqlalchemy.dialects.drizzle import base, mysqldb
-
-base.dialect = mysqldb.dialect
-
-from sqlalchemy.dialects.drizzle.base import \
- BIGINT, BINARY, BLOB, \
- BOOLEAN, CHAR, DATE, \
- DATETIME, DECIMAL, DOUBLE, \
- ENUM, FLOAT, INTEGER, \
- NUMERIC, REAL, TEXT, \
- TIME, TIMESTAMP, VARBINARY, \
- VARCHAR, dialect
-
-__all__ = (
- 'BIGINT', 'BINARY', 'BLOB',
- 'BOOLEAN', 'CHAR', 'DATE',
- 'DATETIME', 'DECIMAL', 'DOUBLE',
- 'ENUM', 'FLOAT', 'INTEGER',
- 'NUMERIC', 'REAL', 'TEXT',
- 'TIME', 'TIMESTAMP', 'VARBINARY',
- 'VARCHAR', 'dialect'
-)
diff --git a/lib/sqlalchemy/dialects/drizzle/base.py b/lib/sqlalchemy/dialects/drizzle/base.py
deleted file mode 100644
index b5addb422..000000000
--- a/lib/sqlalchemy/dialects/drizzle/base.py
+++ /dev/null
@@ -1,498 +0,0 @@
-# drizzle/base.py
-# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
-# Copyright (C) 2010-2011 Monty Taylor <mordred@inaugust.com>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-
-
-"""
-
-.. dialect:: drizzle
- :name: Drizzle
-
-Drizzle is a variant of MySQL. Unlike MySQL, Drizzle's default storage engine
-is InnoDB (transactions, foreign-keys) rather than MyISAM. For more
-`Notable Differences <http://docs.drizzle.org/mysql_differences.html>`_, visit
-the `Drizzle Documentation <http://docs.drizzle.org/index.html>`_.
-
-The SQLAlchemy Drizzle dialect leans heavily on the MySQL dialect, so much of
-the :doc:`SQLAlchemy MySQL <mysql>` documentation is also relevant.
-
-
-"""
-
-from sqlalchemy import exc
-from sqlalchemy import log
-from sqlalchemy import types as sqltypes
-from sqlalchemy.engine import reflection
-from sqlalchemy.dialects.mysql import base as mysql_dialect
-from sqlalchemy.types import DATE, DATETIME, BOOLEAN, TIME, \
- BLOB, BINARY, VARBINARY
-
-
-class _NumericType(object):
- """Base for Drizzle numeric types."""
-
- def __init__(self, **kw):
- super(_NumericType, self).__init__(**kw)
-
-
-class _FloatType(_NumericType, sqltypes.Float):
- def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
- if isinstance(self, (REAL, DOUBLE)) and \
- (
- (precision is None and scale is not None) or
- (precision is not None and scale is None)
- ):
- raise exc.ArgumentError(
- "You must specify both precision and scale or omit "
- "both altogether.")
-
- super(_FloatType, self).__init__(precision=precision,
- asdecimal=asdecimal, **kw)
- self.scale = scale
-
-
-class _StringType(mysql_dialect._StringType):
- """Base for Drizzle string types."""
-
- def __init__(self, collation=None, binary=False, **kw):
- kw['national'] = False
- super(_StringType, self).__init__(collation=collation, binary=binary,
- **kw)
-
-
-class NUMERIC(_NumericType, sqltypes.NUMERIC):
- """Drizzle NUMERIC type."""
-
- __visit_name__ = 'NUMERIC'
-
- def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
- """Construct a NUMERIC.
-
- :param precision: Total digits in this number. If scale and precision
- are both None, values are stored to limits allowed by the server.
-
- :param scale: The number of digits after the decimal point.
-
- """
-
- super(NUMERIC, self).__init__(precision=precision, scale=scale,
- asdecimal=asdecimal, **kw)
-
-
-class DECIMAL(_NumericType, sqltypes.DECIMAL):
- """Drizzle DECIMAL type."""
-
- __visit_name__ = 'DECIMAL'
-
- def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
- """Construct a DECIMAL.
-
- :param precision: Total digits in this number. If scale and precision
- are both None, values are stored to limits allowed by the server.
-
- :param scale: The number of digits after the decimal point.
-
- """
- super(DECIMAL, self).__init__(precision=precision, scale=scale,
- asdecimal=asdecimal, **kw)
-
-
-class DOUBLE(_FloatType):
- """Drizzle DOUBLE type."""
-
- __visit_name__ = 'DOUBLE'
-
- def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
- """Construct a DOUBLE.
-
- :param precision: Total digits in this number. If scale and precision
- are both None, values are stored to limits allowed by the server.
-
- :param scale: The number of digits after the decimal point.
-
- """
-
- super(DOUBLE, self).__init__(precision=precision, scale=scale,
- asdecimal=asdecimal, **kw)
-
-
-class REAL(_FloatType, sqltypes.REAL):
- """Drizzle REAL type."""
-
- __visit_name__ = 'REAL'
-
- def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
- """Construct a REAL.
-
- :param precision: Total digits in this number. If scale and precision
- are both None, values are stored to limits allowed by the server.
-
- :param scale: The number of digits after the decimal point.
-
- """
-
- super(REAL, self).__init__(precision=precision, scale=scale,
- asdecimal=asdecimal, **kw)
-
-
-class FLOAT(_FloatType, sqltypes.FLOAT):
- """Drizzle FLOAT type."""
-
- __visit_name__ = 'FLOAT'
-
- def __init__(self, precision=None, scale=None, asdecimal=False, **kw):
- """Construct a FLOAT.
-
- :param precision: Total digits in this number. If scale and precision
- are both None, values are stored to limits allowed by the server.
-
- :param scale: The number of digits after the decimal point.
-
- """
-
- super(FLOAT, self).__init__(precision=precision, scale=scale,
- asdecimal=asdecimal, **kw)
-
- def bind_processor(self, dialect):
- return None
-
-
-class INTEGER(sqltypes.INTEGER):
- """Drizzle INTEGER type."""
-
- __visit_name__ = 'INTEGER'
-
- def __init__(self, **kw):
- """Construct an INTEGER."""
-
- super(INTEGER, self).__init__(**kw)
-
-
-class BIGINT(sqltypes.BIGINT):
- """Drizzle BIGINTEGER type."""
-
- __visit_name__ = 'BIGINT'
-
- def __init__(self, **kw):
- """Construct a BIGINTEGER."""
-
- super(BIGINT, self).__init__(**kw)
-
-
-class TIME(mysql_dialect.TIME):
- """Drizzle TIME type."""
-
-
-class TIMESTAMP(sqltypes.TIMESTAMP):
- """Drizzle TIMESTAMP type."""
-
- __visit_name__ = 'TIMESTAMP'
-
-
-class TEXT(_StringType, sqltypes.TEXT):
- """Drizzle TEXT type, for text up to 2^16 characters."""
-
- __visit_name__ = 'TEXT'
-
- def __init__(self, length=None, **kw):
- """Construct a TEXT.
-
- :param length: Optional, if provided the server may optimize storage
- by substituting the smallest TEXT type sufficient to store
- ``length`` characters.
-
- :param collation: Optional, a column-level collation for this string
- value. Takes precedence to 'binary' short-hand.
-
- :param binary: Defaults to False: short-hand, pick the binary
- collation type that matches the column's character set. Generates
- BINARY in schema. This does not affect the type of data stored,
- only the collation of character data.
-
- """
-
- super(TEXT, self).__init__(length=length, **kw)
-
-
-class VARCHAR(_StringType, sqltypes.VARCHAR):
- """Drizzle VARCHAR type, for variable-length character data."""
-
- __visit_name__ = 'VARCHAR'
-
- def __init__(self, length=None, **kwargs):
- """Construct a VARCHAR.
-
- :param collation: Optional, a column-level collation for this string
- value. Takes precedence to 'binary' short-hand.
-
- :param binary: Defaults to False: short-hand, pick the binary
- collation type that matches the column's character set. Generates
- BINARY in schema. This does not affect the type of data stored,
- only the collation of character data.
-
- """
-
- super(VARCHAR, self).__init__(length=length, **kwargs)
-
-
-class CHAR(_StringType, sqltypes.CHAR):
- """Drizzle CHAR type, for fixed-length character data."""
-
- __visit_name__ = 'CHAR'
-
- def __init__(self, length=None, **kwargs):
- """Construct a CHAR.
-
- :param length: Maximum data length, in characters.
-
- :param binary: Optional, use the default binary collation for the
- national character set. This does not affect the type of data
- stored, use a BINARY type for binary data.
-
- :param collation: Optional, request a particular collation. Must be
- compatible with the national character set.
-
- """
-
- super(CHAR, self).__init__(length=length, **kwargs)
-
-
-class ENUM(mysql_dialect.ENUM):
- """Drizzle ENUM type."""
-
- def __init__(self, *enums, **kw):
- """Construct an ENUM.
-
- Example:
-
- Column('myenum', ENUM("foo", "bar", "baz"))
-
- :param enums: The range of valid values for this ENUM. Values will be
- quoted when generating the schema according to the quoting flag (see
- below).
-
- :param strict: Defaults to False: ensure that a given value is in this
- ENUM's range of permissible values when inserting or updating rows.
- Note that Drizzle will not raise a fatal error if you attempt to
- store an out of range value- an alternate value will be stored
- instead.
- (See Drizzle ENUM documentation.)
-
- :param collation: Optional, a column-level collation for this string
- value. Takes precedence to 'binary' short-hand.
-
- :param binary: Defaults to False: short-hand, pick the binary
- collation type that matches the column's character set. Generates
- BINARY in schema. This does not affect the type of data stored,
- only the collation of character data.
-
- :param quoting: Defaults to 'auto': automatically determine enum value
- quoting. If all enum values are surrounded by the same quoting
- character, then use 'quoted' mode. Otherwise, use 'unquoted' mode.
-
- 'quoted': values in enums are already quoted, they will be used
- directly when generating the schema - this usage is deprecated.
-
- 'unquoted': values in enums are not quoted, they will be escaped and
- surrounded by single quotes when generating the schema.
-
- Previous versions of this type always required manually quoted
- values to be supplied; future versions will always quote the string
- literals for you. This is a transitional option.
-
- """
-
- super(ENUM, self).__init__(*enums, **kw)
-
-
-class _DrizzleBoolean(sqltypes.Boolean):
- def get_dbapi_type(self, dbapi):
- return dbapi.NUMERIC
-
-
-colspecs = {
- sqltypes.Numeric: NUMERIC,
- sqltypes.Float: FLOAT,
- sqltypes.Time: TIME,
- sqltypes.Enum: ENUM,
- sqltypes.Boolean: _DrizzleBoolean,
-}
-
-
-# All the types we have in Drizzle
-ischema_names = {
- 'BIGINT': BIGINT,
- 'BINARY': BINARY,
- 'BLOB': BLOB,
- 'BOOLEAN': BOOLEAN,
- 'CHAR': CHAR,
- 'DATE': DATE,
- 'DATETIME': DATETIME,
- 'DECIMAL': DECIMAL,
- 'DOUBLE': DOUBLE,
- 'ENUM': ENUM,
- 'FLOAT': FLOAT,
- 'INT': INTEGER,
- 'INTEGER': INTEGER,
- 'NUMERIC': NUMERIC,
- 'TEXT': TEXT,
- 'TIME': TIME,
- 'TIMESTAMP': TIMESTAMP,
- 'VARBINARY': VARBINARY,
- 'VARCHAR': VARCHAR,
-}
-
-
-class DrizzleCompiler(mysql_dialect.MySQLCompiler):
-
- def visit_typeclause(self, typeclause):
- type_ = typeclause.type.dialect_impl(self.dialect)
- if isinstance(type_, sqltypes.Integer):
- return 'INTEGER'
- else:
- return super(DrizzleCompiler, self).visit_typeclause(typeclause)
-
- def visit_cast(self, cast, **kwargs):
- type_ = self.process(cast.typeclause)
- if type_ is None:
- return self.process(cast.clause)
-
- return 'CAST(%s AS %s)' % (self.process(cast.clause), type_)
-
-
-class DrizzleDDLCompiler(mysql_dialect.MySQLDDLCompiler):
- pass
-
-
-class DrizzleTypeCompiler(mysql_dialect.MySQLTypeCompiler):
- def _extend_numeric(self, type_, spec):
- return spec
-
- def _extend_string(self, type_, defaults, spec):
- """Extend a string-type declaration with standard SQL
- COLLATE annotations and Drizzle specific extensions.
-
- """
-
- def attr(name):
- return getattr(type_, name, defaults.get(name))
-
- if attr('collation'):
- collation = 'COLLATE %s' % type_.collation
- elif attr('binary'):
- collation = 'BINARY'
- else:
- collation = None
-
- return ' '.join([c for c in (spec, collation)
- if c is not None])
-
- def visit_NCHAR(self, type):
- raise NotImplementedError("Drizzle does not support NCHAR")
-
- def visit_NVARCHAR(self, type):
- raise NotImplementedError("Drizzle does not support NVARCHAR")
-
- def visit_FLOAT(self, type_):
- if type_.scale is not None and type_.precision is not None:
- return "FLOAT(%s, %s)" % (type_.precision, type_.scale)
- else:
- return "FLOAT"
-
- def visit_BOOLEAN(self, type_):
- return "BOOLEAN"
-
- def visit_BLOB(self, type_):
- return "BLOB"
-
-
-class DrizzleExecutionContext(mysql_dialect.MySQLExecutionContext):
- pass
-
-
-class DrizzleIdentifierPreparer(mysql_dialect.MySQLIdentifierPreparer):
- pass
-
-
-@log.class_logger
-class DrizzleDialect(mysql_dialect.MySQLDialect):
- """Details of the Drizzle dialect.
-
- Not used directly in application code.
- """
-
- name = 'drizzle'
-
- _supports_cast = True
- supports_sequences = False
- supports_native_boolean = True
- supports_views = False
-
- default_paramstyle = 'format'
- colspecs = colspecs
-
- statement_compiler = DrizzleCompiler
- ddl_compiler = DrizzleDDLCompiler
- type_compiler = DrizzleTypeCompiler
- ischema_names = ischema_names
- preparer = DrizzleIdentifierPreparer
-
- def on_connect(self):
- """Force autocommit - Drizzle Bug#707842 doesn't set this properly"""
-
- def connect(conn):
- conn.autocommit(False)
- return connect
-
- @reflection.cache
- def get_table_names(self, connection, schema=None, **kw):
- """Return a Unicode SHOW TABLES from a given schema."""
-
- if schema is not None:
- current_schema = schema
- else:
- current_schema = self.default_schema_name
-
- charset = 'utf8'
- rp = connection.execute("SHOW TABLES FROM %s" %
- self.identifier_preparer.quote_identifier(current_schema))
- return [row[0] for row in self._compat_fetchall(rp, charset=charset)]
-
- @reflection.cache
- def get_view_names(self, connection, schema=None, **kw):
- raise NotImplementedError
-
- def _detect_casing(self, connection):
- """Sniff out identifier case sensitivity.
-
- Cached per-connection. This value can not change without a server
- restart.
- """
-
- return 0
-
- def _detect_collations(self, connection):
- """Pull the active COLLATIONS list from the server.
-
- Cached per-connection.
- """
-
- collations = {}
- charset = self._connection_charset
- rs = connection.execute(
- 'SELECT CHARACTER_SET_NAME, COLLATION_NAME FROM'
- ' data_dictionary.COLLATIONS')
- for row in self._compat_fetchall(rs, charset):
- collations[row[0]] = row[1]
- return collations
-
- def _detect_ansiquotes(self, connection):
- """Detect and adjust for the ANSI_QUOTES sql mode."""
-
- self._server_ansiquotes = False
- self._backslash_escapes = False
-
-
diff --git a/lib/sqlalchemy/dialects/drizzle/mysqldb.py b/lib/sqlalchemy/dialects/drizzle/mysqldb.py
deleted file mode 100644
index 7d91cc368..000000000
--- a/lib/sqlalchemy/dialects/drizzle/mysqldb.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-.. dialect:: drizzle+mysqldb
- :name: MySQL-Python
- :dbapi: mysqldb
- :connectstring: drizzle+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
- :url: http://sourceforge.net/projects/mysql-python
-
-
-"""
-
-from sqlalchemy.dialects.drizzle.base import (
- DrizzleDialect,
- DrizzleExecutionContext,
- DrizzleCompiler,
- DrizzleIdentifierPreparer)
-from sqlalchemy.connectors.mysqldb import (
- MySQLDBExecutionContext,
- MySQLDBCompiler,
- MySQLDBIdentifierPreparer,
- MySQLDBConnector)
-
-
-class DrizzleExecutionContext_mysqldb(MySQLDBExecutionContext,
- DrizzleExecutionContext):
- pass
-
-
-class DrizzleCompiler_mysqldb(MySQLDBCompiler, DrizzleCompiler):
- pass
-
-
-class DrizzleIdentifierPreparer_mysqldb(MySQLDBIdentifierPreparer,
- DrizzleIdentifierPreparer):
- pass
-
-
-class DrizzleDialect_mysqldb(MySQLDBConnector, DrizzleDialect):
- execution_ctx_cls = DrizzleExecutionContext_mysqldb
- statement_compiler = DrizzleCompiler_mysqldb
- preparer = DrizzleIdentifierPreparer_mysqldb
-
- def _detect_charset(self, connection):
- """Sniff out the character set in use for connection results."""
-
- return 'utf8'
-
-
-dialect = DrizzleDialect_mysqldb
diff --git a/lib/sqlalchemy/dialects/mysql/mysqldb.py b/lib/sqlalchemy/dialects/mysql/mysqldb.py
index 7fb63f13b..19e287541 100644
--- a/lib/sqlalchemy/dialects/mysql/mysqldb.py
+++ b/lib/sqlalchemy/dialects/mysql/mysqldb.py
@@ -41,44 +41,71 @@ It is strongly advised to use the latest version of MySQL-Python.
"""
from .base import (MySQLDialect, MySQLExecutionContext,
- MySQLCompiler, MySQLIdentifierPreparer)
-from ...connectors.mysqldb import (
- MySQLDBExecutionContext,
- MySQLDBCompiler,
- MySQLDBIdentifierPreparer,
- MySQLDBConnector
- )
+ MySQLCompiler, MySQLIdentifierPreparer)
from .base import TEXT
from ... import sql
+from ... import util
+import re
-class MySQLExecutionContext_mysqldb(MySQLDBExecutionContext, MySQLExecutionContext):
- pass
+class MySQLExecutionContext_mysqldb(MySQLExecutionContext):
-class MySQLCompiler_mysqldb(MySQLDBCompiler, MySQLCompiler):
- pass
+ @property
+ def rowcount(self):
+ if hasattr(self, '_rowcount'):
+ return self._rowcount
+ else:
+ return self.cursor.rowcount
+
+class MySQLCompiler_mysqldb(MySQLCompiler):
+ def visit_mod_binary(self, binary, operator, **kw):
+ return self.process(binary.left, **kw) + " %% " + \
+ self.process(binary.right, **kw)
+
+ def post_process_text(self, text):
+ return text.replace('%', '%%')
+class MySQLIdentifierPreparer_mysqldb(MySQLIdentifierPreparer):
-class MySQLIdentifierPreparer_mysqldb(MySQLDBIdentifierPreparer, MySQLIdentifierPreparer):
- pass
+ def _escape_identifier(self, value):
+ value = value.replace(self.escape_quote, self.escape_to_quote)
+ return value.replace("%", "%%")
-class MySQLDialect_mysqldb(MySQLDBConnector, MySQLDialect):
+class MySQLDialect_mysqldb(MySQLDialect):
+ driver = 'mysqldb'
+ supports_unicode_statements = False
+ supports_sane_rowcount = True
+ supports_sane_multi_rowcount = True
+
+ supports_native_decimal = True
+
+ default_paramstyle = 'format'
execution_ctx_cls = MySQLExecutionContext_mysqldb
statement_compiler = MySQLCompiler_mysqldb
preparer = MySQLIdentifierPreparer_mysqldb
+
+ @classmethod
+ def dbapi(cls):
+ return __import__('MySQLdb')
+
+ def do_executemany(self, cursor, statement, parameters, context=None):
+ rowcount = cursor.executemany(statement, parameters)
+ if context is not None:
+ context._rowcount = rowcount
+
def _check_unicode_returns(self, connection):
# work around issue fixed in
# https://github.com/farcepest/MySQLdb1/commit/cd44524fef63bd3fcb71947392326e9742d520e8
# specific issue w/ the utf8_bin collation and unicode returns
has_utf8_bin = connection.scalar(
- "show collation where %s = 'utf8' and %s = 'utf8_bin'"
- % (
- self.identifier_preparer.quote("Charset"),
- self.identifier_preparer.quote("Collation")
- ))
+ "show collation where %s = 'utf8' and %s = 'utf8_bin'"
+ % (
+ self.identifier_preparer.quote("Charset"),
+ self.identifier_preparer.quote("Collation")
+ ))
if has_utf8_bin:
additional_tests = [
sql.collate(sql.cast(
@@ -88,7 +115,83 @@ class MySQLDialect_mysqldb(MySQLDBConnector, MySQLDialect):
]
else:
additional_tests = []
- return super(MySQLDBConnector, self)._check_unicode_returns(
+ return super(MySQLDialect_mysqldb, self)._check_unicode_returns(
connection, additional_tests)
+
+ def create_connect_args(self, url):
+ opts = url.translate_connect_args(database='db', username='user',
+ password='passwd')
+ opts.update(url.query)
+
+ util.coerce_kw_type(opts, 'compress', bool)
+ util.coerce_kw_type(opts, 'connect_timeout', int)
+ util.coerce_kw_type(opts, 'read_timeout', int)
+ util.coerce_kw_type(opts, 'client_flag', int)
+ util.coerce_kw_type(opts, 'local_infile', int)
+ # Note: using either of the below will cause all strings to be returned
+ # as Unicode, both in raw SQL operations and with column types like
+ # String and MSString.
+ util.coerce_kw_type(opts, 'use_unicode', bool)
+ util.coerce_kw_type(opts, 'charset', str)
+
+ # Rich values 'cursorclass' and 'conv' are not supported via
+ # query string.
+
+ ssl = {}
+ keys = ['ssl_ca', 'ssl_key', 'ssl_cert', 'ssl_capath', 'ssl_cipher']
+ for key in keys:
+ if key in opts:
+ ssl[key[4:]] = opts[key]
+ util.coerce_kw_type(ssl, key[4:], str)
+ del opts[key]
+ if ssl:
+ opts['ssl'] = ssl
+
+ # FOUND_ROWS must be set in CLIENT_FLAGS to enable
+ # supports_sane_rowcount.
+ client_flag = opts.get('client_flag', 0)
+ if self.dbapi is not None:
+ try:
+ CLIENT_FLAGS = __import__(
+ self.dbapi.__name__ + '.constants.CLIENT'
+ ).constants.CLIENT
+ client_flag |= CLIENT_FLAGS.FOUND_ROWS
+ except (AttributeError, ImportError):
+ self.supports_sane_rowcount = False
+ opts['client_flag'] = client_flag
+ 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_info()):
+ try:
+ version.append(int(n))
+ except ValueError:
+ version.append(n)
+ return tuple(version)
+
+ def _extract_error_code(self, exception):
+ return exception.args[0]
+
+ def _detect_charset(self, connection):
+ """Sniff out the character set in use for connection results."""
+
+ try:
+ # note: the SQL here would be
+ # "SHOW VARIABLES LIKE 'character_set%%'"
+ cset_name = connection.connection.character_set_name
+ except AttributeError:
+ util.warn(
+ "No 'character_set_name' can be detected with "
+ "this MySQL-Python version; "
+ "please upgrade to a recent version of MySQL-Python. "
+ "Assuming latin1.")
+ return 'latin1'
+ else:
+ return cset_name()
+
+
dialect = MySQLDialect_mysqldb