summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-10-24 16:13:32 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-10-24 16:13:32 -0400
commit3a4567a718c2f9f3d8b65acb81f0caefb4f1a2b5 (patch)
treef73ded16750da5e0f3cdafab6109e484708d237f
parentf0678a6e54e9598b884b82f43e57f44661693cca (diff)
downloadsqlalchemy-3a4567a718c2f9f3d8b65acb81f0caefb4f1a2b5.tar.gz
- add migration notes for [ticket:2838]
- have TypeDecorator use process_bind_param for literal values if no process_literal_param is set
-rw-r--r--doc/build/changelog/changelog_09.rst4
-rw-r--r--doc/build/changelog/migration_09.rst26
-rw-r--r--lib/sqlalchemy/sql/type_api.py18
-rw-r--r--test/sql/test_types.py16
4 files changed, 64 insertions, 0 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index 24bf6eb09..38ca115e4 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -62,6 +62,10 @@
: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
diff --git a/doc/build/changelog/migration_09.rst b/doc/build/changelog/migration_09.rst
index 4bc929f58..b7fa41ac7 100644
--- a/doc/build/changelog/migration_09.rst
+++ b/doc/build/changelog/migration_09.rst
@@ -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
diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py
index 698e17472..5d81e4a0c 100644
--- a/lib/sqlalchemy/sql/type_api.py
+++ b/lib/sqlalchemy/sql/type_api.py
@@ -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):
diff --git a/test/sql/test_types.py b/test/sql/test_types.py
index 30a00ca56..7fc5dc35c 100644
--- a/test/sql/test_types.py
+++ b/test/sql/test_types.py
@@ -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", {}),