From: Mike Bayer Date: Wed, 3 Sep 2014 23:42:38 +0000 (-0400) Subject: - ensure literal_binds works with LIMIT clause, FOR UPDATE X-Git-Tag: rel_1_0_0b1~200^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2b10aa45a101acfcc6090a3801af998ef8fc1a2d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - ensure literal_binds works with LIMIT clause, FOR UPDATE --- diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 715936068c..e9b78fe786 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -375,7 +375,11 @@ here is fully backwards compatible with existing third party dialects, however those dialects which implement special LIMIT/OFFSET systems will need modification in order to take advantage of the new - capabilities. Work on this feature is courtesy of Dobes Vandermeer. + capabilities. Limit and offset also support "literal_binds" mode, + where bound parameters are rendered inline as strings based on + a compile-time option. + Work on this feature is courtesy of Dobes Vandermeer. + .. seealso:: diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index d1d4cb9ca6..ba3050ae5d 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -924,7 +924,7 @@ class MSSQLCompiler(compiler.SQLCompiler): def get_crud_hint_text(self, table, text): return text - def limit_clause(self, select): + def limit_clause(self, select, **kw): # Limit in mssql is after the select keyword return "" diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index 012d178e7b..4dccd2760e 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1662,13 +1662,13 @@ class MySQLCompiler(compiler.SQLCompiler): " ON ", self.process(join.onclause, **kwargs))) - def for_update_clause(self, select): + def for_update_clause(self, select, **kw): if select._for_update_arg.read: return " LOCK IN SHARE MODE" else: return " FOR UPDATE" - def limit_clause(self, select): + def limit_clause(self, select, **kw): # MySQL supports: # LIMIT # LIMIT , @@ -1694,15 +1694,15 @@ class MySQLCompiler(compiler.SQLCompiler): # bound as part of MySQL's "syntax" for OFFSET with # no LIMIT return ' \n LIMIT %s, %s' % ( - self.process(offset_clause), + self.process(offset_clause, **kw), "18446744073709551615") else: return ' \n LIMIT %s, %s' % ( - self.process(offset_clause), - self.process(limit_clause)) + self.process(offset_clause, **kw), + self.process(limit_clause, **kw)) else: # No offset provided, so just use the limit - return ' \n LIMIT %s' % (self.process(limit_clause),) + return ' \n LIMIT %s' % (self.process(limit_clause, **kw),) def update_limit_clause(self, update_stmt): limit = update_stmt.kwargs.get('%s_limit' % self.dialect.name, None) diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index 40ba051f7f..81a9f1a957 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -740,10 +740,10 @@ class OracleCompiler(compiler.SQLCompiler): kwargs['iswrapper'] = getattr(select, '_is_wrapper', False) return compiler.SQLCompiler.visit_select(self, select, **kwargs) - def limit_clause(self, select): + def limit_clause(self, select, **kw): return "" - def for_update_clause(self, select): + def for_update_clause(self, select, **kw): if self.is_subquery(): return "" @@ -751,7 +751,7 @@ class OracleCompiler(compiler.SQLCompiler): if select._for_update_arg.of: tmp += ' OF ' + ', '.join( - self.process(elem) for elem in + self.process(elem, **kw) for elem in select._for_update_arg.of ) diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index f1418f9035..575d2a6ddd 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -1309,14 +1309,14 @@ class PGCompiler(compiler.SQLCompiler): def visit_sequence(self, seq): return "nextval('%s')" % self.preparer.format_sequence(seq) - def limit_clause(self, select): + def limit_clause(self, select, **kw): text = "" if select._limit_clause is not None: - text += " \n LIMIT " + self.process(select._limit_clause) + text += " \n LIMIT " + self.process(select._limit_clause, **kw) if select._offset_clause is not None: if select._limit_clause is None: text += " \n LIMIT ALL" - text += " OFFSET " + self.process(select._offset_clause) + text += " OFFSET " + self.process(select._offset_clause, **kw) return text def format_from_hint_text(self, sqltext, table, hint, iscrud): @@ -1337,7 +1337,7 @@ class PGCompiler(compiler.SQLCompiler): else: return "" - def for_update_clause(self, select): + def for_update_clause(self, select, **kw): if select._for_update_arg.read: tmp = " FOR SHARE" @@ -1349,7 +1349,7 @@ class PGCompiler(compiler.SQLCompiler): c.table if isinstance(c, expression.ColumnClause) else c for c in select._for_update_arg.of) tmp += " OF " + ", ".join( - self.process(table, ashint=True) + self.process(table, ashint=True, **kw) for table in tables ) diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 3c8b2d4f7e..af793d2757 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -591,19 +591,19 @@ class SQLiteCompiler(compiler.SQLCompiler): raise exc.CompileError( "%s is not a valid extract argument." % extract.field) - def limit_clause(self, select): + def limit_clause(self, select, **kw): text = "" if select._limit_clause is not None: - text += "\n LIMIT " + self.process(select._limit_clause) + text += "\n LIMIT " + self.process(select._limit_clause, **kw) if select._offset_clause is not None: if select._limit_clause is None: text += "\n LIMIT " + self.process(sql.literal(-1)) - text += " OFFSET " + self.process(select._offset_clause) + text += " OFFSET " + self.process(select._offset_clause, **kw) else: - text += " OFFSET " + self.process(sql.literal(0)) + text += " OFFSET " + self.process(sql.literal(0), **kw) return text - def for_update_clause(self, select): + def for_update_clause(self, select, **kw): # sqlite has no "FOR UPDATE" AFAICT return '' diff --git a/lib/sqlalchemy/dialects/sybase/base.py b/lib/sqlalchemy/dialects/sybase/base.py index 26f5ef04ac..f65a76a271 100644 --- a/lib/sqlalchemy/dialects/sybase/base.py +++ b/lib/sqlalchemy/dialects/sybase/base.py @@ -346,7 +346,7 @@ class SybaseSQLCompiler(compiler.SQLCompiler): def get_from_hint_text(self, table, text): return text - def limit_clause(self, select): + def limit_clause(self, select, **kw): # Limit in sybase is after the select keyword return "" diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 23e5456a7d..e925206208 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -1593,10 +1593,10 @@ class SQLCompiler(Compiled): if (select._limit_clause is not None or select._offset_clause is not None): - text += self.limit_clause(select) + text += self.limit_clause(select, **kwargs) if select._for_update_arg is not None: - text += self.for_update_clause(select) + text += self.for_update_clause(select, **kwargs) if self.ctes and \ compound_index == 0 and toplevel: @@ -1653,7 +1653,7 @@ class SQLCompiler(Compiled): else: return "" - def for_update_clause(self, select): + def for_update_clause(self, select, **kw): return " FOR UPDATE" def returning_clause(self, stmt, returning_cols): @@ -1661,14 +1661,14 @@ class SQLCompiler(Compiled): "RETURNING is not supported by this " "dialect's statement compiler.") - def limit_clause(self, select): + def limit_clause(self, select, **kw): text = "" if select._limit_clause is not None: - text += "\n LIMIT " + self.process(select._limit_clause) + text += "\n LIMIT " + self.process(select._limit_clause, **kw) if select._offset_clause is not None: if select._limit_clause is None: text += "\n LIMIT -1" - text += " OFFSET " + self.process(select._offset_clause) + text += " OFFSET " + self.process(select._offset_clause, **kw) return text def visit_table(self, table, asfrom=False, iscrud=False, ashint=False, diff --git a/lib/sqlalchemy/testing/suite/test_select.py b/lib/sqlalchemy/testing/suite/test_select.py index 3f14ada053..68dadd0a91 100644 --- a/lib/sqlalchemy/testing/suite/test_select.py +++ b/lib/sqlalchemy/testing/suite/test_select.py @@ -136,6 +136,21 @@ class LimitOffsetTest(fixtures.TablesTest): [(2, 2, 3), (3, 3, 4)] ) + def test_limit_offset_nobinds(self): + """test that 'literal binds' mode works - no bound params.""" + + table = self.tables.some_table + stmt = select([table]).order_by(table.c.id).limit(2).offset(1) + sql = stmt.compile( + dialect=config.db.dialect, + compile_kwargs={"literal_binds": True}) + sql = str(sql) + + self._assert_result( + sql, + [(2, 2, 3), (3, 3, 4)] + ) + @testing.requires.bound_limit_offset def test_bound_limit(self): table = self.tables.some_table diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index 4977611c53..4f8ced72c0 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -256,7 +256,7 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL): select._offset)) return result - def limit_clause(self, select): + def limit_clause(self, select, **kw): return "" dialect = default.DefaultDialect()