From: Mike Bayer Date: Sun, 11 Aug 2019 21:42:59 +0000 (-0400) Subject: Emit SET NAMES for all MySQL connections w charset X-Git-Tag: rel_1_3_7~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d890ce67d877fa2b6a20bbda084eb6c21d166ae7;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Emit SET NAMES for all MySQL connections w charset The MySQL dialects will emit "SET NAMES" at the start of a connection when charset is given to the MySQL driver, to appease an apparent behavior observed in MySQL 8.0 that raises a collation error when a UNION includes string columns unioned against columns of the form CAST(NULL AS CHAR(..)), which is what SQLAlchemy's polymorphic_union function does. The issue seems to have affected PyMySQL for at least a year, however has recently appeared as of mysqlclient 1.4.4 based on changes in how this DBAPI creates a connection. As the presence of this directive impacts three separate MySQL charset settings which each have intricate effects based on their presense, SQLAlchemy will now emit the directive on new connections to ensure correct behavior. Fixes: #4804 Change-Id: If9d7ee00d0ccaf773972b564fe455e8e9edf6627 (cherry picked from commit f5e57f7c311288d892894edcc44d901b5bfbb3d1) --- diff --git a/doc/build/changelog/unreleased_13/4804.rst b/doc/build/changelog/unreleased_13/4804.rst new file mode 100644 index 0000000000..58f521e5b4 --- /dev/null +++ b/doc/build/changelog/unreleased_13/4804.rst @@ -0,0 +1,15 @@ +.. change:: + :tags: bug, mysql + :tickets: 4804 + + The MySQL dialects will emit "SET NAMES" at the start of a connection when + charset is given to the MySQL driver, to appease an apparent behavior + observed in MySQL 8.0 that raises a collation error when a UNION includes + string columns unioned against columns of the form CAST(NULL AS CHAR(..)), + which is what SQLAlchemy's polymorphic_union function does. The issue + seems to have affected PyMySQL for at least a year, however has recently + appeared as of mysqlclient 1.4.4 based on changes in how this DBAPI creates + a connection. As the presence of this directive impacts three separate + MySQL charset settings which each have intricate effects based on their + presense, SQLAlchemy will now emit the directive on new connections to + ensure correct behavior. diff --git a/lib/sqlalchemy/dialects/mysql/mysqldb.py b/lib/sqlalchemy/dialects/mysql/mysqldb.py index 744909170a..1d4dba3c4e 100644 --- a/lib/sqlalchemy/dialects/mysql/mysqldb.py +++ b/lib/sqlalchemy/dialects/mysql/mysqldb.py @@ -117,6 +117,22 @@ class MySQLDialect_mysqldb(MySQLDialect): def dbapi(cls): return __import__("MySQLdb") + def on_connect(self): + super_ = super(MySQLDialect_mysqldb, self).on_connect() + + def on_connect(conn): + if super_ is not None: + super_(conn) + + charset_name = conn.character_set_name() + + if charset_name is not None: + cursor = conn.cursor() + cursor.execute("SET NAMES %s" % charset_name) + cursor.close() + + return on_connect + def do_ping(self, dbapi_connection): try: dbapi_connection.ping(False) diff --git a/lib/sqlalchemy/testing/suite/test_types.py b/lib/sqlalchemy/testing/suite/test_types.py index 3320dd93c7..82a842102c 100644 --- a/lib/sqlalchemy/testing/suite/test_types.py +++ b/lib/sqlalchemy/testing/suite/test_types.py @@ -733,7 +733,7 @@ class JSONTest(_LiteralRoundTripFixture, fixtures.TablesTest): def test_round_trip_custom_json(self): data_table = self.tables.data_table - data_element = self.data1 + data_element = {"key1": "data1"} js = mock.Mock(side_effect=json.dumps) jd = mock.Mock(side_effect=json.loads) diff --git a/test/profiles.txt b/test/profiles.txt index 165c6750cf..67f7c337fa 100644 --- a/test/profiles.txt +++ b/test/profiles.txt @@ -1,15 +1,15 @@ # /home/classic/dev/sqlalchemy/test/profiles.txt # This file is written out on a per-environment basis. -# For each test in aaa_profiling, the corresponding function and +# For each test in aaa_profiling, the corresponding function and # environment is located within this file. If it doesn't exist, # the test is skipped. -# If a callcount does exist, it is compared to what we received. +# If a callcount does exist, it is compared to what we received. # assertions are raised if the counts do not match. -# -# To add a new callcount test, apply the function_call_count -# decorator and re-run the tests using the --write-profiles +# +# To add a new callcount test, apply the function_call_count +# decorator and re-run the tests using the --write-profiles # option - this file will be rewritten including the new count. -# +# # TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert @@ -467,8 +467,6 @@ test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3. test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mssql_pyodbc_dbapiunicode_cextensions 1045 test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mssql_pyodbc_dbapiunicode_nocextensions 1062 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_dbapiunicode_cextensions 1196 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_dbapiunicode_nocextensions 1213 test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_oracle_cx_oracle_dbapiunicode_cextensions 1111 test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 1128 test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_dbapiunicode_cextensions 1137 @@ -488,8 +486,6 @@ test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.7_sqlite_pysqlite_dbapiu test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mssql_pyodbc_dbapiunicode_cextensions 93,19 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mssql_pyodbc_dbapiunicode_nocextensions 93,19 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_dbapiunicode_cextensions 93,19 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_dbapiunicode_nocextensions 93,19 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_oracle_cx_oracle_dbapiunicode_cextensions 93,19 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 93,19 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_dbapiunicode_cextensions 93,19