]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- add migration notes for [ticket:2838]
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 24 Oct 2013 20:13:32 +0000 (16:13 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 24 Oct 2013 20:13:32 +0000 (16:13 -0400)
- have TypeDecorator use process_bind_param for literal values if no
process_literal_param is set

doc/build/changelog/changelog_09.rst
doc/build/changelog/migration_09.rst
lib/sqlalchemy/sql/type_api.py
test/sql/test_types.py

index 24bf6eb0956d9cf80858f66ab1a316ebb6b130a2..38ca115e49662e10ae5717b97144dcdbcc707b04 100644 (file)
         :meth:`.TypeDecorator.process_literal_param` is added to allow wrapping
         of a native literal rendering method.
 
+        .. seealso::
+
+            :ref:`change_2838`
+
     .. change::
         :tags: feature, sql
         :tickets: 2716
index 4bc929f580573508d70025c7ed5c2a7cccbd86d8..b7fa41ac76ef72b3af34fd6f25875c2358511e4c 100644 (file)
@@ -372,6 +372,32 @@ to the column being assigned towards will no longer function in that way.
 
 :ticket:`2850`
 
+.. _change_2838:
+
+The typing system now handles the task of rendering "literal bind" values
+-------------------------------------------------------------------------
+
+A new method is added to :class:`.TypeEngine` :meth:`.TypeEngine.literal_processor`
+as well as :meth:`.TypeDecorator.process_literal_param` for :class:`.TypeDecorator`
+which take on the task of rendering so-called "inline literal paramters" - parameters
+that normally render as "bound" values, but are instead being rendered inline
+into the SQL statement due to the compiler configuration.  This feature is used
+when generating DDL for constructs such as :class:`.CheckConstraint`, as well
+as by Alembic when using constructs such as ``op.inline_literal()``.   Previously,
+a simple "isinstance" check checked for a few basic types, and the "bind processor"
+was used unconditionally, leading to such issues as strings being encoded into utf-8
+prematurely.
+
+Custom types written with :class:`.TypeDecorator` should continue to work in
+"inline literal" scenarios, as the :meth:`.TypeDecorator.process_literal_param`
+falls back to :meth:`.TypeDecorator.process_bind_param` by default, as these methods
+usually handle a data manipulation, not as much how the data is presented to the
+database.  :meth:`.TypeDecorator.process_literal_param` can be specified to
+specifically produce a string representing how a value should be rendered
+into an inline DDL statement.
+
+:ticket:`2838`
+
 .. _change_2812:
 
 Schema identifiers now carry along their own quoting information
index 698e17472ba6331a105456072f1e8efb1a000354..5d81e4a0cbbbdc14c877559acf37de16c44eb2cd 100644 (file)
@@ -793,11 +793,29 @@ class TypeDecorator(TypeEngine):
         Subclasses here will typically override :meth:`.TypeDecorator.process_literal_param`
         instead of this method directly.
 
+        By default, this method makes use of :meth:`.TypeDecorator.process_bind_param`
+        if that method is implemented, where :meth:`.TypeDecorator.process_literal_param`
+        is not.  The rationale here is that :class:`.TypeDecorator` typically deals
+        with Python conversions of data that are above the layer of database
+        presentation.  With the value converted by :meth:`.TypeDecorator.process_bind_param`,
+        the underlying type will then handle whether it needs to be presented to the
+        DBAPI as a bound parameter or to the database as an inline SQL value.
+
         .. versionadded:: 0.9.0
 
         """
         if self._has_literal_processor:
             process_param = self.process_literal_param
+        elif self._has_bind_processor:
+            # the bind processor should normally be OK
+            # for TypeDecorator since it isn't doing DB-level
+            # handling, the handling here won't be different for bound vs.
+            # literals.
+            process_param = self.process_bind_param
+        else:
+            process_param = None
+
+        if process_param:
             impl_processor = self.impl.literal_processor(dialect)
             if impl_processor:
                 def process(value):
index 30a00ca5649135235a64ddc35a5777c0ad88302a..7fc5dc35c5875242d50d559272883c8f7697475f 100644 (file)
@@ -287,6 +287,22 @@ class UserDefinedTest(fixtures.TablesTest, AssertsCompiledSQL):
             literal_binds=True
         )
 
+    def test_typedecorator_literal_render_fallback_bound(self):
+        # fall back to process_bind_param for literal
+        # value rendering.
+        class MyType(types.TypeDecorator):
+            impl = String
+
+            def process_bind_param(self, value, dialect):
+                return "HI->%s<-THERE" % value
+
+        self.assert_compile(
+            select([literal("test", MyType)]),
+            "SELECT 'HI->test<-THERE' AS anon_1",
+            dialect='default',
+            literal_binds=True
+        )
+
     def test_typedecorator_impl(self):
         for impl_, exp, kw in [
             (Float, "FLOAT", {}),