From: Jason Kirtland Date: Tue, 5 Feb 2008 17:26:35 +0000 (+0000) Subject: - Autodetect mysql's ANSI_QUOTES mode, sometimes. [ticket:845] X-Git-Tag: rel_0_4_3~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=96549e6b8fd353a82a77938a754bd168a924385b;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Autodetect mysql's ANSI_QUOTES mode, sometimes. [ticket:845] The dialect needs a hook run on first pool connect to detect this most of the time, and a refactor with Dialect-per-Connection to get it right all of the time. (It's a connection-session scoped setting with dialect-modifying behavior) --- diff --git a/CHANGES b/CHANGES index 4b202edd38..0b0d3c3ca1 100644 --- a/CHANGES +++ b/CHANGES @@ -198,6 +198,11 @@ CHANGES - Warnings are now issued as type exceptions.SAWarning. - dialects + - Auto-detect an unspecified MySQL ANSI_QUOTES mode during + reflection operations, support for changing the mode + midstream. Manual mode setting is still required if no + reflection is used. + - Finally added PGMacAddr type to postgres [ticket:580] - Reflect the sequence associated to a PK field (typically diff --git a/lib/sqlalchemy/databases/mysql.py b/lib/sqlalchemy/databases/mysql.py index e01cec2ab7..314cb8dac8 100644 --- a/lib/sqlalchemy/databases/mysql.py +++ b/lib/sqlalchemy/databases/mysql.py @@ -95,6 +95,16 @@ by supplying ``--sql-mode`` to ``mysqld``. You can also use a ``Pool`` hook to issue a ``SET SESSION sql_mode='...'`` on connect to configure each connection. +If you do not specify 'use_ansiquotes', the regular MySQL quoting style is +used by default. Table reflection operations will query the server + +If you do issue a 'SET sql_mode' through SQLAlchemy, the dialect must be +updated if the quoting style is changed. Again, this change will affect all +connections:: + + connection.execute('SET sql_mode="ansi"') + connection.dialect.use_ansiquotes = True + For normal SQLAlchemy usage, loading this module is unnescesary. It will be loaded on-demand when a MySQL connection is needed. The generic column types like ``String`` and ``Integer`` will automatically be adapted to the optimal @@ -1401,13 +1411,9 @@ class MySQLDialect(default.DefaultDialect): max_identifier_length = 255 supports_sane_rowcount = True - def __init__(self, use_ansiquotes=False, **kwargs): + def __init__(self, use_ansiquotes=None, **kwargs): self.use_ansiquotes = use_ansiquotes kwargs.setdefault('default_paramstyle', 'format') - if self.use_ansiquotes: - self.preparer = MySQLANSIIdentifierPreparer - else: - self.preparer = MySQLIdentifierPreparer default.DefaultDialect.__init__(self, **kwargs) def dbapi(cls): @@ -1546,6 +1552,7 @@ class MySQLDialect(default.DefaultDialect): """Return a Unicode SHOW TABLES from a given schema.""" charset = self._detect_charset(connection) + self._autoset_identifier_style(connection) rp = connection.execute("SHOW TABLES FROM %s" % self.identifier_preparer.quote_identifier(schema)) return [row[0] for row in _compat_fetchall(rp, charset=charset)] @@ -1558,7 +1565,10 @@ class MySQLDialect(default.DefaultDialect): # based on platform. DESCRIBE is slower. # [ticket:726] - # full_name = self.identifier_preparer.format_table(table, use_schema=True) + # full_name = self.identifier_preparer.format_table(table, + # use_schema=True) + + self._autoset_identifier_style(connection) full_name = '.'.join(self.identifier_preparer._quote_free_identifiers( schema, table_name)) @@ -1627,12 +1637,18 @@ class MySQLDialect(default.DefaultDialect): """Load column definitions from the server.""" charset = self._detect_charset(connection) + self._autoset_identifier_style(connection) try: reflector = self.reflector except AttributeError: - self.reflector = reflector = \ - MySQLSchemaReflector(self.identifier_preparer) + preparer = self.identifier_preparer + if (self.server_version_info(connection) < (4, 1) and + self.use_ansiquotes): + # ANSI_QUOTES doesn't affect SHOW CREATE TABLE on < 4.1 + preparer = MySQLIdentifierPreparer(self) + + self.reflector = reflector = MySQLSchemaReflector(preparer) sql = self._show_create_table(connection, table, charset) if sql.startswith('CREATE ALGORITHM'): @@ -1750,6 +1766,49 @@ class MySQLDialect(default.DefaultDialect): connection.info['collations'] = collations return collations + def use_ansiquotes(self, useansi): + self._use_ansiquotes = useansi + if useansi: + self.preparer = MySQLANSIIdentifierPreparer + else: + self.preparer = MySQLIdentifierPreparer + # icky + if hasattr(self, 'identifier_preparer'): + self.identifier_preparer = self.preparer(self) + if hasattr(self, 'reflector'): + del self.reflector + + use_ansiquotes = property(lambda s: s._use_ansiquotes, use_ansiquotes, + doc="True if ANSI_QUOTES is in effect.") + + def _autoset_identifier_style(self, connection, charset=None): + """Detect and adjust for the ANSI_QUOTES sql mode. + + If the dialect's use_ansiquotes is unset, query the server's sql mode + and reset the identifier style. + + Note that this currently *only* runs during reflection. Ideally this + would run the first time a connection pool connects to the database, + but the infrastructure for that is not yet in place. + """ + + if self.use_ansiquotes is not None: + return + + row = _compat_fetchone( + connection.execute("SHOW VARIABLES LIKE 'sql_mode'", + charset=charset)) + if not row: + mode = '' + else: + mode = row[1] or '' + # 4.0 + if mode.isdigit(): + mode_no = int(mode) + mode = (mode_no | 4 == mode_no) and 'ANSI_QUOTES' or '' + + self.use_ansiquotes = 'ANSI_QUOTES' in mode + def _show_create_table(self, connection, table, charset=None, full_name=None): """Run SHOW CREATE TABLE for a ``Table``."""