Path: blob/main/singlestoredb/mysql/tests/test_connection.py
469 views
# type: ignore1import datetime2import ssl3import time4from unittest import mock56import pytest78import singlestoredb.mysql as sv9from singlestoredb.mysql.constants import CLIENT10from singlestoredb.mysql.tests import base111213class TempUser:1415def __init__(self, c, user, db, auth=None, authdata=None, password=None):16self._c = c17self._user = user18self._db = db19create = 'CREATE USER ' + user20if password is not None:21create += " IDENTIFIED BY '%s'" % password22elif auth is not None:23create += ' IDENTIFIED WITH %s' % auth24if authdata is not None:25create += " AS '%s'" % authdata26try:27c.execute(create)28self._created = True29except sv.err.InternalError:30# already exists - TODO need to check the same plugin applies31self._created = False32try:33c.execute('GRANT SELECT ON %s.* TO %s' % (db, user))34self._grant = True35except sv.err.InternalError:36self._grant = False3738def __enter__(self):39return self4041def __exit__(self, exc_type, exc_value, traceback):42if self._grant:43self._c.execute('REVOKE SELECT ON %s.* FROM %s' % (self._db, self._user))44if self._created:45self._c.execute('DROP USER %s' % self._user)464748class TestAuthentication(base.PyMySQLTestCase):4950socket_auth = False51socket_found = False52two_questions_found = False53three_attempts_found = False54pam_found = False55mysql_old_password_found = False56sha256_password_found = False57ed25519_found = False5859import os6061osuser = os.environ.get('USER')6263# # socket auth requires the current user and for the connection to be a socket64# # rest do grants @localhost due to incomplete logic - TODO change to @% then65# db = base.PyMySQLTestCase.databases[0].copy()6667# socket_auth = db.get('unix_socket') is not None and db.get('host') in (68# 'localhost',69# '127.0.0.1',70# )7172# dbname = db['database']7374# cur = sv.connect(**db).cursor()75# db.pop('user', None)76# cur.execute('SHOW PLUGINS')77# for r in cur:78# if (r[1], r[2]) != ('ACTIVE', 'AUTHENTICATION'):79# continue80# if r[3] == 'auth_socket.so' or r[0] == 'unix_socket':81# socket_plugin_name = r[0]82# socket_found = True83# elif r[3] == 'dialog_examples.so':84# if r[0] == 'two_questions':85# two_questions_found = True86# elif r[0] == 'three_attempts':87# three_attempts_found = True88# elif r[0] == 'pam':89# pam_found = True90# pam_plugin_name = r[3].split('.')[0]91# if pam_plugin_name == 'auth_pam':92# pam_plugin_name = 'pam'93# # MySQL: authentication_pam94# # https://dev.mysql.com/doc/refman/5.5/en/pam-authentication-plugin.html9596# # MariaDB: pam97# # https://mariadb.com/kb/en/mariadb/pam-authentication-plugin/9899# # Names differ but functionality is close100# elif r[0] == 'mysql_old_password':101# mysql_old_password_found = True102# elif r[0] == 'sha256_password':103# sha256_password_found = True104# elif r[0] == 'ed25519':105# ed25519_found = True106# # else:107# # print("plugin: %r" % r[0])108109@pytest.mark.skip(reason='not currently supported in SingleStoreDB')110def test_plugin(self):111conn = self.connect()112cur = conn.cursor()113cur.execute(114"select plugin from mysql.user where concat(user, '@', host)=current_user()",115)116for r in cur:117self.assertIn(conn._auth_plugin_name, (r[0], 'mysql_native_password'))118119@pytest.mark.skip(reason='not currently supported by SingleStoreDB')120@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')121@pytest.mark.skipif(socket_found, reason='socket plugin already installed')122def testSocketAuthInstallPlugin(self):123# needs plugin. lets install it.124cur = self.connect().cursor()125try:126cur.execute("install plugin auth_socket soname 'auth_socket.so'")127TestAuthentication.socket_found = True128self.socket_plugin_name = 'auth_socket'129self.realtestSocketAuth()130except sv.err.InternalError:131try:132cur.execute("install soname 'auth_socket'")133TestAuthentication.socket_found = True134self.socket_plugin_name = 'unix_socket'135self.realtestSocketAuth()136except sv.err.InternalError:137TestAuthentication.socket_found = False138pytest.skip("we couldn't install the socket plugin")139finally:140if TestAuthentication.socket_found:141cur.execute('uninstall plugin %s' % self.socket_plugin_name)142143@pytest.mark.skip(reason='not currently supported by SingleStoreDB')144@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')145@pytest.mark.skipif(not socket_found, reason='no socket plugin')146def testSocketAuth(self):147self.realtestSocketAuth()148149def realtestSocketAuth(self):150with TempUser(151self.connect().cursor(),152TestAuthentication.osuser + '@localhost',153self.databases[0]['database'],154self.socket_plugin_name,155) as _:156sv.connect(user=TestAuthentication.osuser, **self.db)157158class Dialog:159fail = False160161def __init__(self, con):162self.fail = TestAuthentication.Dialog.fail163pass164165def prompt(self, echo, prompt):166if self.fail:167self.fail = False168return b'bad guess at a password'169return self.m.get(prompt)170171class DialogHandler:172def __init__(self, con):173self.con = con174175def authenticate(self, pkt):176while True:177flag = pkt.read_uint8()178echo = (flag & 0x06) == 0x02 # noqa: F841179last = (flag & 0x01) == 0x01180prompt = pkt.read_all()181182if prompt == b'Password, please:':183self.con.write_packet(b'stillnotverysecret\0')184else:185self.con.write_packet(b'no idea what to do with this prompt\0')186pkt = self.con._read_packet()187pkt.check_error()188if pkt.is_ok_packet() or last:189break190return pkt191192class DefectiveHandler:193def __init__(self, con):194self.con = con195196@pytest.mark.skip(reason='not currently supported by SingleStoreDB')197@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')198@pytest.mark.skipif(199two_questions_found, reason='two_questions plugin already installed',200)201def testDialogAuthTwoQuestionsInstallPlugin(self):202# needs plugin. lets install it.203cur = self.connect().cursor()204try:205cur.execute("install plugin two_questions soname 'dialog_examples.so'")206TestAuthentication.two_questions_found = True207self.realTestDialogAuthTwoQuestions()208except sv.err.InternalError:209pytest.skip("we couldn't install the two_questions plugin")210finally:211if TestAuthentication.two_questions_found:212cur.execute('uninstall plugin two_questions')213214@pytest.mark.skip(reason='not currently supported by SingleStoreDB')215@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')216@pytest.mark.skipif(not two_questions_found, reason='no two questions auth plugin')217def testDialogAuthTwoQuestions(self):218self.realTestDialogAuthTwoQuestions()219220def realTestDialogAuthTwoQuestions(self):221TestAuthentication.Dialog.fail = False222TestAuthentication.Dialog.m = {223b'Password, please:': b'notverysecret',224b'Are you sure ?': b'yes, of course',225}226with TempUser(227self.connect().cursor(),228'singlestoredb_2q@localhost',229self.databases[0]['database'],230'two_questions',231'notverysecret',232) as _:233with self.assertRaises(sv.err.OperationalError):234sv.connect(user='singlestoredb_2q', **self.db)235sv.connect(236user='singlestoredb_2q',237auth_plugin_map={b'dialog': TestAuthentication.Dialog},238**self.db,239)240241@pytest.mark.skip(reason='not currently supported by SingleStoreDB')242@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')243@pytest.mark.skipif(244three_attempts_found, reason='three_attempts plugin already installed',245)246def testDialogAuthThreeAttemptsQuestionsInstallPlugin(self):247# needs plugin. lets install it.248cur = self.connect().cursor()249try:250cur.execute("install plugin three_attempts soname 'dialog_examples.so'")251TestAuthentication.three_attempts_found = True252self.realTestDialogAuthThreeAttempts()253except sv.err.InternalError:254pytest.skip("we couldn't install the three_attempts plugin")255finally:256if TestAuthentication.three_attempts_found:257cur.execute('uninstall plugin three_attempts')258259@pytest.mark.skip(reason='not currently supported by SingleStoreDB')260@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')261@pytest.mark.skipif(not three_attempts_found, reason='no three attempts plugin')262def testDialogAuthThreeAttempts(self):263self.realTestDialogAuthThreeAttempts()264265def realTestDialogAuthThreeAttempts(self):266TestAuthentication.Dialog.m = {b'Password, please:': b'stillnotverysecret'}267TestAuthentication.Dialog.fail = (268True # fail just once. We've got three attempts after all269)270with TempUser(271self.connect().cursor(),272'singlestoredb_3a@localhost',273self.databases[0]['database'],274'three_attempts',275'stillnotverysecret',276) as _:277sv.connect(278user='singlestoredb_3a',279auth_plugin_map={b'dialog': TestAuthentication.Dialog},280**self.db,281)282sv.connect(283user='singlestoredb_3a',284auth_plugin_map={b'dialog': TestAuthentication.DialogHandler},285**self.db,286)287with self.assertRaises(sv.err.OperationalError):288sv.connect(289user='singlestoredb_3a',290auth_plugin_map={b'dialog': object},291**self.db,292)293294with self.assertRaises(sv.err.OperationalError):295sv.connect(296user='singlestoredb_3a',297auth_plugin_map={b'dialog': TestAuthentication.DefectiveHandler},298**self.db,299)300with self.assertRaises(sv.err.OperationalError):301sv.connect(302user='singlestoredb_3a',303auth_plugin_map={b'notdialogplugin': TestAuthentication.Dialog},304**self.db,305)306TestAuthentication.Dialog.m = {b'Password, please:': b'I do not know'}307with self.assertRaises(sv.err.OperationalError):308sv.connect(309user='singlestoredb_3a',310auth_plugin_map={b'dialog': TestAuthentication.Dialog},311**self.db,312)313TestAuthentication.Dialog.m = {b'Password, please:': None}314with self.assertRaises(sv.err.OperationalError):315sv.connect(316user='singlestoredb_3a',317auth_plugin_map={b'dialog': TestAuthentication.Dialog},318**self.db,319)320321@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')322@pytest.mark.skipif(pam_found, reason='pam plugin already installed')323@pytest.mark.skipif(324os.environ.get('PASSWORD') is None, reason='PASSWORD env var required',325)326@pytest.mark.skipif(327os.environ.get('PAMSERVICE') is None, reason='PAMSERVICE env var required',328)329def testPamAuthInstallPlugin(self):330# needs plugin. lets install it.331cur = self.connect().cursor()332try:333cur.execute("install plugin pam soname 'auth_pam.so'")334TestAuthentication.pam_found = True335self.realTestPamAuth()336except sv.err.InternalError:337pytest.skip("we couldn't install the auth_pam plugin")338finally:339if TestAuthentication.pam_found:340cur.execute('uninstall plugin pam')341342@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')343@pytest.mark.skipif(not pam_found, reason='no pam plugin')344@pytest.mark.skipif(345os.environ.get('PASSWORD') is None, reason='PASSWORD env var required',346)347@pytest.mark.skipif(348os.environ.get('PAMSERVICE') is None, reason='PAMSERVICE env var required',349)350def testPamAuth(self):351self.realTestPamAuth()352353@pytest.mark.skip(reason='skip PAM tests on SingleStoreDB')354def realTestPamAuth(self):355db = self.db.copy()356import os357358db['password'] = os.environ.get('PASSWORD')359cur = self.connect().cursor()360try:361cur.execute('show grants for ' + TestAuthentication.osuser + '@localhost')362grants = cur.fetchone()[0]363cur.execute('drop user ' + TestAuthentication.osuser + '@localhost')364except sv.OperationalError as e:365# assuming the user doesn't exist which is ok too366self.assertEqual(1045, e.args[0])367grants = None368with TempUser(369cur,370TestAuthentication.osuser + '@localhost',371self.databases[0]['database'],372'pam',373os.environ.get('PAMSERVICE'),374) as _:375try:376c = sv.connect(user=TestAuthentication.osuser, **db) # noqa: F841377db['password'] = 'very bad guess at password'378with self.assertRaises(sv.err.OperationalError):379sv.connect(380user=TestAuthentication.osuser,381auth_plugin_map={382b'mysql_cleartext_password':383TestAuthentication.DefectiveHandler,384},385**self.db,386)387except sv.OperationalError as e:388self.assertEqual(1045, e.args[0])389# we had 'bad guess at password' work with pam. Well at least390# we get a permission denied here391with self.assertRaises(sv.err.OperationalError):392sv.connect(393user=TestAuthentication.osuser,394auth_plugin_map={395b'mysql_cleartext_password':396TestAuthentication.DefectiveHandler,397},398**self.db,399)400if grants:401# recreate the user402cur.execute(grants)403404@pytest.mark.skip(reason='not currently supported by SingleStoreDB')405@pytest.mark.skipif(not socket_auth, reason='connection to unix_socket required')406@pytest.mark.skipif(407not sha256_password_found,408reason='no sha256 password authentication plugin found',409)410def testAuthSHA256(self):411conn = self.connect()412c = conn.cursor()413with TempUser(414c,415'singlestoredb_sha256@localhost',416self.databases[0]['database'],417'sha256_password',418) as _:419c.execute("SET PASSWORD FOR 'singlestoredb_sha256'@'localhost' ='Sh@256Pa33'")420c.execute('FLUSH PRIVILEGES')421db = self.db.copy()422db['password'] = 'Sh@256Pa33'423# Although SHA256 is supported, need the configuration of public424# key of the mysql server. Currently will get error by this test.425with self.assertRaises(sv.err.OperationalError):426sv.connect(user='singlestoredb_sha256', **db)427428@pytest.mark.skipif(not ed25519_found, reason='no ed25519 authention plugin')429def testAuthEd25519(self):430db = self.db.copy()431db.pop('password', None)432conn = self.connect()433c = conn.cursor()434c.execute("select ed25519_password(''), ed25519_password('ed25519_password')")435for r in c:436empty_pass = r[0].decode('ascii')437non_empty_pass = r[1].decode('ascii')438439with TempUser(440c,441'singlestoredb_ed25519',442self.databases[0]['database'],443'ed25519',444empty_pass,445) as _:446sv.connect(user='singlestoredb_ed25519', password='', **db)447448with TempUser(449c,450'singlestoredb_ed25519',451self.databases[0]['database'],452'ed25519',453non_empty_pass,454) as _:455sv.connect(user='singlestoredb_ed25519', password='ed25519_password', **db)456457458class TestConnection(base.PyMySQLTestCase):459460def test_utf8mb4(self):461"""This test requires MySQL >= 5.5."""462arg = self.databases[0].copy()463arg['charset'] = 'utf8mb4'464conn = sv.connect(**arg) # noqa: F841465466def test_largedata(self):467"""Large query and response (>=16MB)."""468cur = self.connect().cursor()469cur.execute('SELECT @@max_allowed_packet')470if cur.fetchone()[0] < 16 * 1024 * 1024 + 10:471print('Set max_allowed_packet to bigger than 17MB')472return473t = 'a' * (16 * 1024 * 1024)474cur.execute("SELECT '" + t + "'")475assert cur.fetchone()[0] == t476477def test_autocommit(self):478con = self.connect()479self.assertFalse(con.get_autocommit())480481cur = con.cursor()482cur.execute('SET AUTOCOMMIT=1')483self.assertTrue(con.get_autocommit())484485con.autocommit(False)486self.assertFalse(con.get_autocommit())487cur.execute('SELECT @@AUTOCOMMIT')488self.assertEqual(cur.fetchone()[0], 0)489490def test_select_db(self):491con = self.connect()492current_db = self.databases[0]['database']493other_db = self.databases[1]['database']494495cur = con.cursor()496cur.execute('SELECT database()')497self.assertEqual(cur.fetchone()[0], current_db)498499con.select_db(other_db)500cur.execute('SELECT database()')501self.assertEqual(cur.fetchone()[0], other_db)502503@pytest.mark.skip(reason='wait_timeout= does not work')504def test_connection_gone_away(self):505"""506http://dev.mysql.com/doc/refman/5.0/en/gone-away.html507http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html#error_cr_server_gone_error508509"""510con = self.connect()511cur = con.cursor()512cur.execute('SET wait_timeout=1')513time.sleep(2)514with self.assertRaises(sv.OperationalError) as cm:515cur.execute('SELECT 1+1')516# error occures while reading, not writing because of socket buffer.517# self.assertEqual(cm.exception.args[0], 2006)518self.assertIn(cm.exception.args[0], (2006, 2013))519520def test_init_command(self):521conn = self.connect(522init_command='SELECT "bar"; SELECT "baz"',523client_flag=CLIENT.MULTI_STATEMENTS,524)525c = conn.cursor()526c.execute('select "foobar";')527self.assertEqual(('foobar',), c.fetchone())528conn.close()529with self.assertRaises(sv.err.Error):530conn.ping(reconnect=False)531532def test_read_default_group(self):533conn = self.connect(534read_default_group='client',535)536self.assertTrue(conn.open)537538def test_set_charset(self):539c = self.connect()540c.set_charset('utf8mb4')541# TODO validate setting here542543def test_defer_connect(self):544import socket545546d = self.databases[0].copy()547try:548sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)549sock.connect(d['unix_socket'])550except KeyError:551sock.close()552sock = socket.create_connection(553(d.get('host', 'localhost'), d.get('port', 3306)),554)555for k in ['unix_socket', 'host', 'port']:556try:557del d[k]558except KeyError:559pass560561c = sv.connect(defer_connect=True, **d)562self.assertFalse(c.open)563c.connect(sock)564c.close()565sock.close()566567@pytest.mark.skip(reason='disable local user tests')568def test_ssl_connect(self):569dummy_ssl_context = mock.Mock(options=0)570with mock.patch(571'singlestoredb.connections.Connection.connect',572) as _, mock.patch(573'singlestoredb.connections.ssl.create_default_context',574new=mock.Mock(return_value=dummy_ssl_context),575) as create_default_context:576sv.connect(577ssl={578'ca': 'ca',579'cert': 'cert',580'key': 'key',581'cipher': 'cipher',582},583)584assert create_default_context.called585assert dummy_ssl_context.check_hostname586assert dummy_ssl_context.verify_mode == ssl.CERT_REQUIRED587dummy_ssl_context.load_cert_chain.assert_called_with('cert', keyfile='key')588dummy_ssl_context.set_ciphers.assert_called_with('cipher')589590dummy_ssl_context = mock.Mock(options=0)591with mock.patch(592'singlestoredb.connections.Connection.connect',593) as _, mock.patch(594'singelstoredb.connections.ssl.create_default_context',595new=mock.Mock(return_value=dummy_ssl_context),596) as create_default_context:597sv.connect(598ssl={599'ca': 'ca',600'cert': 'cert',601'key': 'key',602},603)604assert create_default_context.called605assert dummy_ssl_context.check_hostname606assert dummy_ssl_context.verify_mode == ssl.CERT_REQUIRED607dummy_ssl_context.load_cert_chain.assert_called_with('cert', keyfile='key')608dummy_ssl_context.set_ciphers.assert_not_called609610dummy_ssl_context = mock.Mock(options=0)611with mock.patch(612'singelstoredb.connections.Connection.connect',613) as _, mock.patch(614'singlestoredb.connections.ssl.create_default_context',615new=mock.Mock(return_value=dummy_ssl_context),616) as create_default_context:617sv.connect(618ssl_ca='ca',619)620assert create_default_context.called621assert not dummy_ssl_context.check_hostname622assert dummy_ssl_context.verify_mode == ssl.CERT_NONE623dummy_ssl_context.load_cert_chain.assert_not_called624dummy_ssl_context.set_ciphers.assert_not_called625626dummy_ssl_context = mock.Mock(options=0)627with mock.patch(628'singlestoredb.connections.Connection.connect',629) as _, mock.patch(630'singlestoredb.connections.ssl.create_default_context',631new=mock.Mock(return_value=dummy_ssl_context),632) as create_default_context:633sv.connect(634ssl_ca='ca',635ssl_cert='cert',636ssl_key='key',637)638assert create_default_context.called639assert not dummy_ssl_context.check_hostname640assert dummy_ssl_context.verify_mode == ssl.CERT_NONE641dummy_ssl_context.load_cert_chain.assert_called_with('cert', keyfile='key')642dummy_ssl_context.set_ciphers.assert_not_called643644for ssl_verify_cert in (True, '1', 'yes', 'true'):645dummy_ssl_context = mock.Mock(options=0)646with mock.patch(647'singlestoredb.connections.Connection.connect',648) as _, mock.patch(649'singlestoredb.connections.ssl.create_default_context',650new=mock.Mock(return_value=dummy_ssl_context),651) as create_default_context:652sv.connect(653ssl_cert='cert',654ssl_key='key',655ssl_verify_cert=ssl_verify_cert,656)657assert create_default_context.called658assert not dummy_ssl_context.check_hostname659assert dummy_ssl_context.verify_mode == ssl.CERT_REQUIRED660dummy_ssl_context.load_cert_chain.assert_called_with(661'cert', keyfile='key',662)663dummy_ssl_context.set_ciphers.assert_not_called664665for ssl_verify_cert in (None, False, '0', 'no', 'false'):666dummy_ssl_context = mock.Mock(options=0)667with mock.patch(668'singlestoredb.connections.Connection.connect',669) as _, mock.patch(670'singlestoredb.connections.ssl.create_default_context',671new=mock.Mock(return_value=dummy_ssl_context),672) as create_default_context:673sv.connect(674ssl_cert='cert',675ssl_key='key',676ssl_verify_cert=ssl_verify_cert,677)678assert create_default_context.called679assert not dummy_ssl_context.check_hostname680assert dummy_ssl_context.verify_mode == ssl.CERT_NONE681dummy_ssl_context.load_cert_chain.assert_called_with(682'cert', keyfile='key',683)684dummy_ssl_context.set_ciphers.assert_not_called685686for ssl_ca in ('ca', None):687for ssl_verify_cert in ('foo', 'bar', ''):688dummy_ssl_context = mock.Mock(options=0)689with mock.patch(690'singlestoredb.connections.Connection.connect',691) as _, mock.patch(692'singlestoredb.connections.ssl.create_default_context',693new=mock.Mock(return_value=dummy_ssl_context),694) as create_default_context:695sv.connect(696ssl_ca=ssl_ca,697ssl_cert='cert',698ssl_key='key',699ssl_verify_cert=ssl_verify_cert,700)701assert create_default_context.called702assert not dummy_ssl_context.check_hostname703assert dummy_ssl_context.verify_mode == (704ssl.CERT_REQUIRED if ssl_ca is not None else ssl.CERT_NONE705), (ssl_ca, ssl_verify_cert)706dummy_ssl_context.load_cert_chain.assert_called_with(707'cert', keyfile='key',708)709dummy_ssl_context.set_ciphers.assert_not_called710711dummy_ssl_context = mock.Mock(options=0)712with mock.patch(713'singlestoredb.connections.Connection.connect',714) as _, mock.patch(715'singlestoredb.connections.ssl.create_default_context',716new=mock.Mock(return_value=dummy_ssl_context),717) as create_default_context:718sv.connect(719ssl_ca='ca',720ssl_cert='cert',721ssl_key='key',722ssl_verify_identity=True,723)724assert create_default_context.called725assert dummy_ssl_context.check_hostname726assert dummy_ssl_context.verify_mode == ssl.CERT_NONE727dummy_ssl_context.load_cert_chain.assert_called_with('cert', keyfile='key')728dummy_ssl_context.set_ciphers.assert_not_called729730dummy_ssl_context = mock.Mock(options=0)731with mock.patch(732'singlestoredb.connections.Connection.connect',733) as _, mock.patch(734'singlestoredb.connections.ssl.create_default_context',735new=mock.Mock(return_value=dummy_ssl_context),736) as create_default_context:737sv.connect(738ssl_disabled=True,739ssl={740'ca': 'ca',741'cert': 'cert',742'key': 'key',743},744)745assert not create_default_context.called746747dummy_ssl_context = mock.Mock(options=0)748with mock.patch(749'singlestoredb.connections.Connection.connect',750) as _, mock.patch(751'singlestoredb.connections.ssl.create_default_context',752new=mock.Mock(return_value=dummy_ssl_context),753) as create_default_context:754sv.connect(755ssl_disabled=True,756ssl_ca='ca',757ssl_cert='cert',758ssl_key='key',759)760assert not create_default_context.called761762763# A custom type and function to escape it764class Foo:765value = 'bar'766767768def escape_foo(x, d):769return x.value770771772class TestEscape(base.PyMySQLTestCase):773774def test_escape_string(self):775con = self.connect()776cur = con.cursor() # noqa: F841777778self.assertEqual(con.escape("foo'bar"), "'foo\\'bar'")779# # added NO_AUTO_CREATE_USER as not including it in 5.7 generates warnings780# # mysql-8.0 removes the option however781# if self.mysql_server_is(con, (8, 0, 0)):782# cur.execute("SET sql_mode='NO_BACKSLASH_ESCAPES'")783# else:784# cur.execute("SET sql_mode='NO_BACKSLASH_ESCAPES,NO_AUTO_CREATE_USER'")785# self.assertEqual(con.escape("foo'bar"), "'foo''bar'")786787def test_escape_builtin_encoders(self):788con = self.connect()789cur = con.cursor() # noqa: F841790791val = datetime.datetime(2012, 3, 4, 5, 6)792self.assertEqual(con.escape(val, con.encoders), "'2012-03-04 05:06:00'")793794def test_escape_custom_object(self):795con = self.connect()796cur = con.cursor() # noqa: F841797798mapping = {Foo: escape_foo}799self.assertEqual(con.escape(Foo(), mapping), 'bar')800801def test_escape_fallback_encoder(self):802con = self.connect()803cur = con.cursor() # noqa: F841804805class Custom(str):806pass807808mapping = {str: sv.converters.escape_string}809self.assertEqual(con.escape(Custom('foobar'), mapping), "'foobar'")810811def test_escape_no_default(self):812con = self.connect()813cur = con.cursor() # noqa: F841814815self.assertRaises(TypeError, con.escape, 42, {})816817def test_escape_dict_value(self):818con = self.connect()819cur = con.cursor() # noqa: F841820821mapping = con.encoders.copy()822mapping[Foo] = escape_foo823self.assertEqual(con.escape({'foo': Foo()}, mapping), {'foo': 'bar'})824825def test_escape_list_item(self):826con = self.connect()827cur = con.cursor() # noqa: F841828829mapping = con.encoders.copy()830mapping[Foo] = escape_foo831self.assertEqual(con.escape([Foo()], mapping), '(bar)')832833def test_previous_cursor_not_closed(self):834con = self.connect(835init_command='SELECT "bar"; SELECT "baz"',836client_flag=CLIENT.MULTI_STATEMENTS,837)838cur1 = con.cursor()839cur1.execute('SELECT 1; SELECT 2')840cur2 = con.cursor()841cur2.execute('SELECT 3')842self.assertEqual(cur2.fetchone()[0], 3)843844def test_commit_during_multi_result(self):845con = self.connect(client_flag=CLIENT.MULTI_STATEMENTS)846cur = con.cursor()847cur.execute('SELECT 1; SELECT 2')848con.commit()849cur.execute('SELECT 3')850self.assertEqual(cur.fetchone()[0], 3)851852853