Skip to content

Commit 84adac8

Browse files
committed
BUG25614860: Fix defined_as method in the view creation
The defined_as(statement) method used to create views does not allow SelectStatement objects, generated by Table.select(). This patch fix this issue and also prevents the select statement to be modified after passed to the defined_as() method. Test were added for regression.
1 parent 64d72dc commit 84adac8

File tree

2 files changed

+63
-10
lines changed

2 files changed

+63
-10
lines changed

lib/mysqlx/statement.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# MySQL Connector/Python - MySQL driver written in Python.
2-
# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
33

44
# MySQL Connector/Python is licensed under the terms of the GPLv2
55
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
@@ -23,6 +23,7 @@
2323

2424
"""Implementation of Statements."""
2525

26+
import copy
2627
import json
2728
import re
2829

@@ -580,8 +581,7 @@ def get_sql(self):
580581
self._limit_offset) if self._has_limit else ""
581582

582583
stmt = ("SELECT {select} FROM {schema}.{table}{where}{group}{having}"
583-
"{order}{limit}".format(
584-
select=getattr(self, '_projection_str', "*"),
584+
"{order}{limit}".format(select=self._projection_str or "*",
585585
schema=self.schema.name, table=self.target.name, limit=limit,
586586
where=where, group=group_by, having=having, order=order_by))
587587

@@ -852,7 +852,11 @@ def defined_as(self, statement):
852852
Returns:
853853
mysqlx.CreateViewStatement: CreateViewStatement object.
854854
"""
855-
self._defined_as = statement
855+
if not isinstance(statement, SelectStatement) and \
856+
not isinstance(statement, STRING_TYPES):
857+
raise ProgrammingError("The statement must be an instance of "
858+
"SelectStatement or a SQL string.")
859+
self._defined_as = copy.copy(statement) # Prevent modifications
856860
return self
857861

858862
def with_check_option(self, check_option):
@@ -878,6 +882,9 @@ def execute(self):
878882
if self._definer else ""
879883
columns = " ({0})".format(", ".join(self._columns)) \
880884
if self._columns else ""
885+
defined_as = self._defined_as.get_sql() \
886+
if isinstance(self._defined_as, SelectStatement) \
887+
else self._defined_as
881888
view_name = quote_multipart_identifier((self._schema.name, self._name))
882889
check_option = " WITH {0} CHECK OPTION".format(self._check_option) \
883890
if self._check_option else ""
@@ -887,8 +894,7 @@ def execute(self):
887894
"".format(replace=replace, algorithm=self._algorithm,
888895
definer=definer, security=self._security,
889896
view_name=view_name, columns=columns,
890-
defined_as=self._defined_as,
891-
check_option=check_option))
897+
defined_as=defined_as, check_option=check_option))
892898

893899
self._connection.execute_nonquery("sql", sql)
894900
return self._view
@@ -913,6 +919,9 @@ def execute(self):
913919
if self._definer else ""
914920
columns = " ({0})".format(", ".join(self._columns)) \
915921
if self._columns else ""
922+
defined_as = self._defined_as.get_sql() \
923+
if isinstance(self._defined_as, SelectStatement) \
924+
else self._defined_as
916925
view_name = quote_multipart_identifier((self._schema.name, self._name))
917926
check_option = " WITH {0} CHECK OPTION".format(self._check_option) \
918927
if self._check_option else ""
@@ -921,7 +930,7 @@ def execute(self):
921930
"AS {defined_as}{check_option}"
922931
"".format(algorithm=self._algorithm, definer=definer,
923932
security=self._security, view_name=view_name,
924-
columns=columns, defined_as=self._defined_as,
933+
columns=columns, defined_as=defined_as,
925934
check_option=check_option))
926935

927936
self._connection.execute_nonquery("sql", sql)

tests/test_mysqlx_crud.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def test_create_view(self):
116116
.defined_as(defined_as) \
117117
.execute()
118118

119-
self.schema.drop_table(view_name)
119+
self.schema.drop_view(view_name)
120120

121121
# using a non-updatable view
122122
defined_as = ("SELECT COLUMN_TYPE, COLUMN_COMMENT FROM "
@@ -126,8 +126,30 @@ def test_create_view(self):
126126
.defined_as(defined_as) \
127127
.execute()
128128

129+
self.schema.drop_view(view_name)
130+
131+
# create view from Table.select()
132+
table = self.schema.get_table(table_name)
133+
table.insert("id").values(2).values(3).execute()
134+
select = table.select()
135+
view = self.schema.create_view(view_name).defined_as(select).execute()
136+
self.assertEqual(3, view.count())
137+
138+
self.schema.drop_view(view_name)
139+
140+
# ensure that the object passed to defined_as() does not affect the
141+
# view if changed later
142+
select = table.select()
143+
view = self.schema.create_view(view_name).defined_as(select)
144+
select = select.where("id > 1")
145+
self.assertEqual(3, view.execute().count())
146+
147+
# defined_as() should only accepts SelectStatement and strings
148+
view = self.schema.create_view(view_name)
149+
self.assertRaises(mysqlx.ProgrammingError, view.defined_as, 123)
150+
151+
self.schema.drop_view(view_name)
129152
self.schema.drop_table(table_name)
130-
self.schema.drop_table(view_name)
131153

132154
def test_alter_view(self):
133155
table_name = "table_test"
@@ -162,8 +184,30 @@ def test_alter_view(self):
162184
.defined_as(defined_as) \
163185
.execute()
164186

187+
self.schema.drop_view(view_name)
188+
189+
# create view from Table.select()
190+
table = self.schema.get_table(table_name)
191+
table.insert("id").values(2).values(3).execute()
192+
select = table.select()
193+
view = self.schema.create_view(view_name).defined_as(select).execute()
194+
self.assertEqual(12, view.count())
195+
196+
self.schema.drop_view(view_name)
197+
198+
# ensure that the object passed to defined_as() does not affect the
199+
# view if changed later
200+
select = table.select()
201+
view = self.schema.create_view(view_name).defined_as(select)
202+
select = select.where("id > 1")
203+
self.assertEqual(12, view.execute().count())
204+
205+
# defined_as() should only accepts SelectStatement and strings
206+
view = self.schema.create_view(view_name)
207+
self.assertRaises(mysqlx.ProgrammingError, view.defined_as, 123)
208+
209+
self.schema.drop_view(view_name)
165210
self.schema.drop_table(table_name)
166-
self.schema.drop_table(view_name)
167211

168212
def test_get_collection(self):
169213
collection_name = "collection_test"

0 commit comments

Comments
 (0)