diff options
| -rw-r--r-- | CHANGES | 8 | ||||
| -rw-r--r-- | lib/sqlalchemy/databases/mysql.py | 5 | ||||
| -rw-r--r-- | lib/sqlalchemy/databases/postgres.py | 5 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 5 | ||||
| -rw-r--r-- | test/sql/query.py | 31 |
5 files changed, 52 insertions, 2 deletions
@@ -319,6 +319,10 @@ CHANGES behavior. [ticket:1243] - postgres + - "%" signs in text() constructs are automatically escaped to "%%". + Because of the backwards incompatible nature of this change, + a warning is emitted if '%%' is detected in the string. [ticket:1267] + - Calling alias.execute() in conjunction with server_side_cursors won't raise AttributeError. @@ -339,6 +343,10 @@ CHANGES convert_unicode=True flags. [ticket:1233] - mysql + - "%" signs in text() constructs are automatically escaped to "%%". + Because of the backwards incompatible nature of this change, + a warning is emitted if '%%' is detected in the string. + - Fixed bug in exception raise when FK columns not present during reflection. [ticket:1241] diff --git a/lib/sqlalchemy/databases/mysql.py b/lib/sqlalchemy/databases/mysql.py index 86fb7b247..1b977dce9 100644 --- a/lib/sqlalchemy/databases/mysql.py +++ b/lib/sqlalchemy/databases/mysql.py @@ -1952,6 +1952,11 @@ class MySQLCompiler(compiler.DefaultCompiler): return 'CAST(%s AS %s)' % (self.process(cast.clause), type_) + def post_process_text(self, text): + if '%%' in text: + util.warn("The SQLAlchemy MySQLDB dialect now automatically escapes '%' in text() expressions to '%%'.") + return text.replace('%', '%%') + def get_select_precolumns(self, select): if isinstance(select._distinct, basestring): return select._distinct.upper() + " " diff --git a/lib/sqlalchemy/databases/postgres.py b/lib/sqlalchemy/databases/postgres.py index 273f5859e..6fd0662de 100644 --- a/lib/sqlalchemy/databases/postgres.py +++ b/lib/sqlalchemy/databases/postgres.py @@ -719,6 +719,11 @@ class PGCompiler(compiler.DefaultCompiler): else: return "nextval('%s')" % self.preparer.format_sequence(seq) + def post_process_text(self, text): + if '%%' in text: + util.warn("The SQLAlchemy psycopg2 dialect now automatically escapes '%' in text() expressions to '%%'.") + return text.replace('%', '%%') + def limit_clause(self, select): text = "" if select._limit is not None: diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 0430f053b..b286398bf 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -294,6 +294,9 @@ class DefaultCompiler(engine.Compiled): def visit_typeclause(self, typeclause, **kwargs): return typeclause.type.dialect_impl(self.dialect).get_col_spec() + def post_process_text(self, text): + return text + def visit_textclause(self, textclause, **kwargs): if textclause.typemap is not None: for colname, type_ in textclause.typemap.iteritems(): @@ -308,7 +311,7 @@ class DefaultCompiler(engine.Compiled): # un-escape any \:params return BIND_PARAMS_ESC.sub(lambda m: m.group(1), - BIND_PARAMS.sub(do_bindparam, textclause.text) + BIND_PARAMS.sub(do_bindparam, self.post_process_text(textclause.text)) ) def visit_null(self, null, **kwargs): diff --git a/test/sql/query.py b/test/sql/query.py index e62dfa076..275bbe78c 100644 --- a/test/sql/query.py +++ b/test/sql/query.py @@ -4,7 +4,7 @@ from sqlalchemy import * from sqlalchemy import exc, sql from sqlalchemy.engine import default from testlib import * - +from testlib.testing import eq_ class QueryTest(TestBase): @@ -235,6 +235,35 @@ class QueryTest(TestBase): l.append(row) self.assert_(len(l) == 2, "fetchmany(size=2) got %s rows" % len(l)) + def test_like_ops(self): + users.insert().execute( + {'user_id':1, 'user_name':'apples'}, + {'user_id':2, 'user_name':'oranges'}, + {'user_id':3, 'user_name':'bananas'}, + {'user_id':4, 'user_name':'legumes'}, + {'user_id':5, 'user_name':'hi % there'}, + ) + + for expr, result in ( + (select([users.c.user_id]).where(users.c.user_name.startswith('apple')), [(1,)]), + (select([users.c.user_id]).where(users.c.user_name.contains('i % t')), [(5,)]), + (select([users.c.user_id]).where(users.c.user_name.endswith('anas')), [(3,)]), + ): + eq_(expr.execute().fetchall(), result) + + + @testing.emits_warning('.*now automatically escapes.*') + def test_percents_in_text(self): + for expr, result in ( + (text("select 6 % 10"), 6), + (text("select 17 % 10"), 7), + (text("select '%'"), '%'), + (text("select '%%'"), '%%'), + (text("select '%%%'"), '%%%'), + (text("select 'hello % world'"), "hello % world") + ): + eq_(testing.db.scalar(expr), result) + def test_ilike(self): users.insert().execute( {'user_id':1, 'user_name':'one'}, |
