diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-12-17 15:38:35 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-12-17 15:38:35 -0500 |
commit | 80b6591dbea86f87cc845c67ea11c1449ee75eee (patch) | |
tree | 4f49591d79138718c02b733f24d565a75de339b7 | |
parent | d98fcca0b3441e09c3d56ad69c93b41f9b240f0f (diff) | |
download | sqlalchemy-80b6591dbea86f87cc845c67ea11c1449ee75eee.tar.gz |
- The :func:`.cast` function, when given a plain literal value,
will now apply the given type to the given literal value on the
bind parameter side according
to the type given to the cast. This essentially replaces what would
normally be the detected type of the literal value. This only
takes effect if the auto-detected type of the literal value is either
"nulltype" (e.g. couldn't detect)
or a type that is of the same "affinity" as the cast type.
The net change here is that the :func:`.cast` function includes more
of the functionality already present in the :func:`.type_coerce` function.
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 14 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/elements.py | 5 | ||||
-rw-r--r-- | test/sql/test_types.py | 52 |
3 files changed, 55 insertions, 16 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 2cfa424e8..cbcd10fff 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -15,6 +15,20 @@ :version: 0.9.0b2 .. change:: + :tags: bug, sql + + The :func:`.cast` function, when given a plain literal value, + will now apply the given type to the given literal value on the + bind parameter side according + to the type given to the cast. This essentially replaces what would + normally be the detected type of the literal value. This only + takes effect if the auto-detected type of the literal value is either + "nulltype" (e.g. couldn't detect) + or a type that is of the same "affinity" as the cast type. + The net change here is that the :func:`.cast` function includes more + of the functionality already present in the :func:`.type_coerce` function. + + .. change:: :tags: bug, postgresql Now using psycopg2 UNICODEARRAY extension for handling unicode arrays diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 69e365bd3..91ce0a090 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -1753,7 +1753,10 @@ class Cast(ColumnElement): """ self.type = type_api.to_instance(totype) self.clause = _literal_as_binds(clause, None) - if isinstance(self.clause, BindParameter) and self.clause.type._isnull: + if isinstance(self.clause, BindParameter) and ( + self.clause.type._isnull + or self.clause.type._type_affinity is self.type._type_affinity + ): self.clause = self.clause._clone() self.clause.type = self.type diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 7fc5dc35c..09176c000 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -416,12 +416,21 @@ class UserDefinedTest(fixtures.TablesTest, AssertsCompiledSQL): def test_type_coerce(self): """test ad-hoc usage of custom types with type_coerce().""" + self._test_type_coerce_cast(type_coerce) + + @testing.provide_metadata + def test_cast(self): + """test ad-hoc usage of custom types with cast().""" + + self._test_type_coerce_cast(cast) + + def _test_type_coerce_cast(self, coerce_fn): metadata = self.metadata class MyType(types.TypeDecorator): impl = String def process_bind_param(self, value, dialect): - return value[0:-8] + return util.text_type(value)[0:-8] def process_result_value(self, value, dialect): return value + "BIND_OUT" @@ -429,55 +438,68 @@ class UserDefinedTest(fixtures.TablesTest, AssertsCompiledSQL): t = Table('t', metadata, Column('data', String(50))) metadata.create_all() - t.insert().values(data=type_coerce('d1BIND_OUT', MyType)).execute() + t.insert().values(data=coerce_fn('d1BIND_OUT', MyType)).execute() eq_( - select([type_coerce(t.c.data, MyType)]).execute().fetchall(), + select([coerce_fn(t.c.data, MyType)]).execute().fetchall(), [('d1BIND_OUT', )] ) + # test coerce from nulltype - e.g. use an object that + # doens't match to a known type + class MyObj(object): + def __str__(self): + return "THISISMYOBJ" + + eq_( + testing.db.execute( + select([coerce_fn(MyObj(), MyType)]) + ).fetchall(), + [('THIBIND_OUT',)] + ) + eq_( - select([t.c.data, type_coerce(t.c.data, MyType)]).execute().fetchall(), + select([t.c.data, coerce_fn(t.c.data, MyType)]).execute().fetchall(), [('d1', 'd1BIND_OUT')] ) eq_( - select([t.c.data, type_coerce(t.c.data, MyType)]). + select([t.c.data, coerce_fn(t.c.data, MyType)]). alias().select().execute().fetchall(), [('d1', 'd1BIND_OUT')] ) eq_( - select([t.c.data, type_coerce(t.c.data, MyType)]).\ - where(type_coerce(t.c.data, MyType) == 'd1BIND_OUT').\ + select([t.c.data, coerce_fn(t.c.data, MyType)]).\ + where(coerce_fn(t.c.data, MyType) == 'd1BIND_OUT').\ execute().fetchall(), [('d1', 'd1BIND_OUT')] ) eq_( - select([t.c.data, type_coerce(t.c.data, MyType)]).\ - where(t.c.data == type_coerce('d1BIND_OUT', MyType)).\ + select([t.c.data, coerce_fn(t.c.data, MyType)]).\ + where(t.c.data == coerce_fn('d1BIND_OUT', MyType)).\ execute().fetchall(), [('d1', 'd1BIND_OUT')] ) eq_( - select([t.c.data, type_coerce(t.c.data, MyType)]).\ - where(t.c.data == type_coerce(None, MyType)).\ + select([t.c.data, coerce_fn(t.c.data, MyType)]).\ + where(t.c.data == coerce_fn(None, MyType)).\ execute().fetchall(), [] ) eq_( - select([t.c.data, type_coerce(t.c.data, MyType)]).\ - where(type_coerce(t.c.data, MyType) == None).\ + select([t.c.data, coerce_fn(t.c.data, MyType)]).\ + where(coerce_fn(t.c.data, MyType) == None).\ execute().fetchall(), [] ) eq_( testing.db.scalar( - select([type_coerce(literal('d1BIND_OUT'), MyType)]) + select([coerce_fn(literal('d1BIND_OUT'), MyType)]) ), 'd1BIND_OUT' ) @@ -488,7 +510,7 @@ class UserDefinedTest(fixtures.TablesTest, AssertsCompiledSQL): eq_( testing.db.execute( - select([t.c.data, type_coerce(MyFoob(), MyType)]) + select([t.c.data, coerce_fn(MyFoob(), MyType)]) ).fetchall(), [('d1', 'd1BIND_OUT')] ) |