]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add check for blank string coming from MySQL's enum
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 28 Oct 2016 18:13:31 +0000 (14:13 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 28 Oct 2016 18:17:40 +0000 (14:17 -0400)
MySQL's native ENUM type supports any non-valid value being sent, and
in response will return a blank string.  A hardcoded rule to check for
"is returning the blank string" has been added to the  MySQL
implementation for ENUM so that this blank string is returned to the
application rather than being rejected as a non-valid value.  Note that
if your MySQL enum is linking values to objects, you still get the
blank string back.

Change-Id: I61f85c20293a48b0c11a31f2a19f6756c206bd20
Fixes: #3841
doc/build/changelog/changelog_11.rst
lib/sqlalchemy/dialects/mysql/enumerated.py
test/dialect/mysql/test_types.py

index dd260d260cb7f29cbee4c9b402c12178a9aed633..05c4529bb71c9e61c9ab027348171434e4fc691d 100644 (file)
 .. changelog::
     :version: 1.1.4
 
+    .. change::
+        :tags: bug, mysql
+        :tickets: 3841
+
+        MySQL's native ENUM type supports any non-valid value being sent, and
+        in response will return a blank string.  A hardcoded rule to check for
+        "is returning the blank string" has been added to the  MySQL
+        implementation for ENUM so that this blank string is returned to the
+        application rather than being rejected as a non-valid value.  Note that
+        if your MySQL enum is linking values to objects, you still get the
+        blank string back.
+
 .. changelog::
     :version: 1.1.3
     :released: October 27, 2016
index a47d94ca751b873a18b000be132b9dc5d08a57a9..a7cd8911c1368485a06ca6f5df3d37c9b394bc01 100644 (file)
@@ -130,6 +130,16 @@ class ENUM(sqltypes.Enum, _EnumeratedValues):
         values, length = self._init_values(values, kw)
         return sqltypes.Enum._setup_for_values(self, values, objects, kw)
 
+    def _object_value_for_elem(self, elem):
+        # mysql sends back a blank string for any value that
+        # was persisted that was not in the enums; that is, it does no
+        # validation on the incoming data, it "truncates" it to be
+        # the blank string.  Return it straight.
+        if elem == "":
+            return elem
+        else:
+            return super(ENUM, self)._object_value_for_elem(elem)
+
     def __repr__(self):
         return util.generic_repr(
             self, to_inspect=[ENUM, _StringType, sqltypes.Enum])
index 0cbb507c54c1f7d77a81e0ca6c8d9be187b3ec3a..48663da25de3791e8dda27f2b7ac7ff08f9ae343 100644 (file)
@@ -1063,6 +1063,30 @@ class EnumSetTest(
             eq_(t.c.e6.type.values, ("", "a"))
             eq_(t.c.e7.type.values, ("", "'a'", "b'b", "'"))
 
+    @testing.provide_metadata
+    def test_broken_enum_returns_blanks(self):
+        t = Table(
+            'enum_missing',
+            self.metadata,
+            Column('id', Integer, primary_key=True),
+            Column('e1', sqltypes.Enum('one', 'two', 'three')),
+            Column('e2', mysql.ENUM('one', 'two', 'three'))
+        )
+        t.create()
+
+        with testing.db.connect() as conn:
+            conn.execute(t.insert(), {"e1": "nonexistent", "e2": "nonexistent"})
+            conn.execute(t.insert(), {"e1": "", "e2": ""})
+            conn.execute(t.insert(), {"e1": "two", "e2": "two"})
+            conn.execute(t.insert(), {"e1": None, "e2": None})
+
+            eq_(
+                conn.execute(
+                    select([t.c.e1, t.c.e2]).order_by(t.c.id)
+                ).fetchall(),
+                [("", ""), ("", ""), ("two", "two"), (None, None)]
+            )
+
 
 def colspec(c):
     return testing.db.dialect.ddl_compiler(