Skip to content

Commit 26eacdb

Browse files
committed
WL13531: Remove xplugin namespace
MySQL 8.0.19 removes the deprecated namespace 'xplugin', used in Mysqlx.Sql.StmtExecute messages. This worklog replaces the 'xplugin' namespace by 'mysqlx'. This change breaks compatibility with versions of MySQL older than 5.7.14 with X protocol enabled. Since the 'xplugin' namespace is removed by MySQL 8.0.19, users should upgrade Connector/Python to 8.0.19. The internal API was changed and tests skipped for this replacement.
1 parent 474eec1 commit 26eacdb

File tree

8 files changed

+41
-47
lines changed

8 files changed

+41
-47
lines changed

lib/mysqlx/connection.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -640,12 +640,11 @@ def _execute_prepared_pipeline(self, msg_type, msg, statement):
640640
statement.increment_exec_counter()
641641

642642
@catch_network_exception
643-
def send_sql(self, statement, *args):
643+
def send_sql(self, statement):
644644
"""Execute a SQL statement.
645645
646646
Args:
647647
sql (str): The SQL statement.
648-
*args: Arbitrary arguments.
649648
650649
Raises:
651650
:class:`mysqlx.ProgrammingError`: If the SQL statement is not a
@@ -658,10 +657,10 @@ def send_sql(self, statement, *args):
658657
raise ProgrammingError("The SQL statement is not a valid string")
659658
if not PY3 and isinstance(sql, UNICODE_TYPES):
660659
msg_type, msg = self.protocol.build_execute_statement(
661-
"sql", bytes(bytearray(sql, "utf-8")), args)
660+
"sql", bytes(bytearray(sql, "utf-8")))
662661
else:
663662
msg_type, msg = self.protocol.build_execute_statement(
664-
"sql", sql, args)
663+
"sql", sql)
665664
self.protocol.send_msg_without_ps(msg_type, msg, statement)
666665
return SqlResult(self)
667666

@@ -732,14 +731,14 @@ def send_update(self, statement):
732731
return Result(self)
733732

734733
@catch_network_exception
735-
def execute_nonquery(self, namespace, cmd, raise_on_fail, *args):
734+
def execute_nonquery(self, namespace, cmd, raise_on_fail, fields=None):
736735
"""Execute a non query command.
737736
738737
Args:
739738
namespace (str): The namespace.
740739
cmd (str): The command.
741740
raise_on_fail (bool): `True` to raise on fail.
742-
*args: Arbitrary arguments.
741+
fields (Optional[dict]): The message fields.
743742
744743
Raises:
745744
:class:`mysqlx.OperationalError`: On errors.
@@ -749,28 +748,27 @@ def execute_nonquery(self, namespace, cmd, raise_on_fail, *args):
749748
"""
750749
try:
751750
msg_type, msg = \
752-
self.protocol.build_execute_statement(namespace, cmd, args)
751+
self.protocol.build_execute_statement(namespace, cmd, fields)
753752
self.protocol.send_msg(msg_type, msg)
754753
return Result(self)
755754
except OperationalError:
756755
if raise_on_fail:
757756
raise
758757

759758
@catch_network_exception
760-
def execute_sql_scalar(self, sql, *args):
759+
def execute_sql_scalar(self, sql):
761760
"""Execute a SQL scalar.
762761
763762
Args:
764763
sql (str): The SQL statement.
765-
*args: Arbitrary arguments.
766764
767765
Raises:
768766
:class:`mysqlx.InterfaceError`: If no data found.
769767
770768
Returns:
771769
:class:`mysqlx.Result`: The result.
772770
"""
773-
msg_type, msg = self.protocol.build_execute_statement("sql", sql, args)
771+
msg_type, msg = self.protocol.build_execute_statement("sql", sql)
774772
self.protocol.send_msg(msg_type, msg)
775773
result = RowResult(self)
776774
result.fetch_all()
@@ -779,18 +777,18 @@ def execute_sql_scalar(self, sql, *args):
779777
return result[0][0]
780778

781779
@catch_network_exception
782-
def get_row_result(self, cmd, *args):
780+
def get_row_result(self, cmd, fields):
783781
"""Returns the row result.
784782
785783
Args:
786784
cmd (str): The command.
787-
*args: Arbitrary arguments.
785+
fields (dict): The message fields.
788786
789787
Returns:
790788
:class:`mysqlx.RowResult`: The result object.
791789
"""
792790
msg_type, msg = \
793-
self.protocol.build_execute_statement("xplugin", cmd, args)
791+
self.protocol.build_execute_statement("mysqlx", cmd, fields)
794792
self.protocol.send_msg(msg_type, msg)
795793
return RowResult(self)
796794

lib/mysqlx/crud.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ def get_collections(self):
177177
Returns:
178178
`list`: List of Collection objects.
179179
"""
180-
rows = self._connection.get_row_result("list_objects", self._name)
180+
rows = self._connection.get_row_result("list_objects",
181+
{"schema": self._name})
181182
rows.fetch_all()
182183
collections = []
183184
for row in rows:
@@ -205,7 +206,8 @@ def get_tables(self):
205206
Returns:
206207
`list`: List of Table objects.
207208
"""
208-
rows = self._connection.get_row_result("list_objects", self._name)
209+
rows = self._connection.get_row_result("list_objects",
210+
{"schema": self._name})
209211
rows.fetch_all()
210212
tables = []
211213
object_types = ("TABLE", "VIEW",)
@@ -283,8 +285,9 @@ def create_collection(self, name, reuse=False):
283285
raise ProgrammingError("Collection name is invalid")
284286
collection = Collection(self, name)
285287
if not collection.exists_in_database():
286-
self._connection.execute_nonquery("xplugin", "create_collection",
287-
True, self._name, name)
288+
self._connection.execute_nonquery("mysqlx", "create_collection",
289+
True, {"schema": self._name,
290+
"name": name})
288291
elif not reuse:
289292
raise ProgrammingError("Collection already exists")
290293
return collection
@@ -413,9 +416,10 @@ def drop_index(self, index_name):
413416
Args:
414417
index_name (str): Index name.
415418
"""
416-
self._connection.execute_nonquery("xplugin", "drop_collection_index",
417-
False, self._schema.name, self._name,
418-
index_name)
419+
self._connection.execute_nonquery("mysqlx", "drop_collection_index",
420+
False, {"schema": self._schema.name,
421+
"collection": self._name,
422+
"name": index_name})
419423

420424
def replace_one(self, doc_id, doc):
421425
"""Replaces the Document matching the document ID with a new document

lib/mysqlx/protocol.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -679,13 +679,13 @@ def build_delete(self, stmt):
679679
self._apply_filter(msg, stmt)
680680
return "Mysqlx.ClientMessages.Type.CRUD_DELETE", msg
681681

682-
def build_execute_statement(self, namespace, stmt, args):
682+
def build_execute_statement(self, namespace, stmt, fields=None):
683683
"""Build execute statement.
684684
685685
Args:
686686
namespace (str): The namespace.
687687
stmt (Statement): A `Statement` based type object.
688-
args (iterable): An iterable object.
688+
fields (Optional[dict]): The message fields.
689689
690690
Returns:
691691
(tuple): Tuple containing:
@@ -698,23 +698,15 @@ def build_execute_statement(self, namespace, stmt, args):
698698
msg = Message("Mysqlx.Sql.StmtExecute", namespace=namespace, stmt=stmt,
699699
compact_metadata=False)
700700

701-
if namespace == "mysqlx":
702-
# mysqlx namespace behavior: one object with a list of arguments
703-
items = args[0].items() if isinstance(args, (list, tuple)) else \
704-
args.items()
701+
if fields:
705702
obj_flds = []
706-
for key, value in items:
703+
for key, value in fields.items():
707704
obj_fld = Message("Mysqlx.Datatypes.Object.ObjectField",
708705
key=key, value=self._create_any(value))
709706
obj_flds.append(obj_fld.get_message())
710707
msg_obj = Message("Mysqlx.Datatypes.Object", fld=obj_flds)
711708
msg_any = Message("Mysqlx.Datatypes.Any", type=2, obj=msg_obj)
712709
msg["args"] = [msg_any.get_message()]
713-
else:
714-
# xplugin namespace behavior: list of arguments
715-
for arg in args:
716-
value = self._create_any(arg)
717-
msg["args"].extend([value.get_message()])
718710

719711
return "Mysqlx.ClientMessages.Type.SQL_STMT_EXECUTE", msg
720712

tests/test_mysqlx_connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def send_auth_ok(self):
262262
"Mysqlx.ServerMessages.Type.SESS_AUTHENTICATE_OK"), msg)
263263

264264

265-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
265+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
266266
class MySQLxSessionTests(tests.MySQLxTests):
267267

268268
def setUp(self):
@@ -1174,7 +1174,7 @@ def test_connection_attributes(self):
11741174
"while was specified to not do so: {}".format(rows))
11751175

11761176

1177-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
1177+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
11781178
class MySQLxInnitialNoticeTests(tests.MySQLxTests):
11791179

11801180
def setUp(self):

tests/test_mysqlx_crud.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def drop_view(schema, view_name):
7373
schema.get_session().sql(query).execute()
7474

7575

76-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
76+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
7777
class MySQLxDbDocTests(tests.MySQLxTests):
7878

7979
def setUp(self):
@@ -115,7 +115,7 @@ def test_dbdoc_creation(self):
115115
doc_6 = doc_5.copy()
116116

117117

118-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
118+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
119119
class MySQLxSchemaTests(tests.MySQLxTests):
120120

121121
def setUp(self):
@@ -254,7 +254,7 @@ def test_drop_collection(self):
254254
self.schema.drop_collection(collection_name)
255255

256256

257-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
257+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
258258
class MySQLxCollectionTests(tests.MySQLxTests):
259259

260260
def setUp(self):
@@ -2305,7 +2305,7 @@ def test_prepared_statements(self):
23052305
session.close()
23062306

23072307

2308-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
2308+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
23092309
class MySQLxTableTests(tests.MySQLxTests):
23102310

23112311
def setUp(self):
@@ -2882,7 +2882,7 @@ def test_prepared_statements(self):
28822882
session.close()
28832883

28842884

2885-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
2885+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
28862886
class MySQLxViewTests(tests.MySQLxTests):
28872887

28882888
def setUp(self):

tests/test_mysqlx_errorcode.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
22
#
33
# This program is free software; you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License, version 2.0, as
@@ -36,7 +36,7 @@
3636
from mysqlx import errorcode
3737

3838

39-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
39+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
4040
class ErrorCodeTests(tests.MySQLxTests):
4141

4242
def test__MYSQL_VERSION(self):

tests/test_mysqlx_errors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
22
#
33
# This program is free software; you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License, version 2.0, as
@@ -36,7 +36,7 @@
3636
from mysqlx import errors
3737

3838

39-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
39+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
4040
class ErrorsTests(tests.MySQLxTests):
4141

4242
def test_get_mysql_exception(self):

tests/test_mysqlx_pooling.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def get_current_connections(session):
6464
return connections
6565

6666

67-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
67+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
6868
class MySQLxClientTests(tests.MySQLxTests):
6969
def setUp(self):
7070
self.connect_kwargs = tests.get_mysqlx_config()
@@ -423,7 +423,7 @@ def test_reset_get_new_connection(self):
423423
# No errors should raise from closing client.
424424
client.close()
425425

426-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
426+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
427427
class MySQLxClientPoolingTests(tests.MySQLxTests):
428428
def setUp(self):
429429
settings = tests.get_mysqlx_config()
@@ -510,7 +510,7 @@ def test_pools_are_not_shared(self):
510510
client2.close()
511511

512512

513-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
513+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
514514
class MySQLxConnectionPoolingTests(tests.MySQLxTests):
515515
def setUp(self):
516516
settings = tests.get_mysqlx_config()
@@ -739,7 +739,7 @@ def test_connection_attributes(self):
739739
expected_attrs[row.get_string('ATTR_NAME')]))
740740

741741

742-
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 12), "XPlugin not compatible")
742+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 7, 14), "XPlugin not compatible")
743743
class MySQLxPoolingSessionTests(tests.MySQLxTests):
744744
def setUp(self):
745745
self.connect_kwargs = tests.get_mysqlx_config()

0 commit comments

Comments
 (0)