summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/dialects/sqlite/base.py8
-rw-r--r--lib/sqlalchemy/engine/default.py6
-rw-r--r--lib/sqlalchemy/sql/coercions.py5
-rw-r--r--lib/sqlalchemy/sql/compiler.py6
-rw-r--r--lib/sqlalchemy/sql/elements.py16
5 files changed, 29 insertions, 12 deletions
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py
index b6ca8fe3c..c9309cbad 100644
--- a/lib/sqlalchemy/dialects/sqlite/base.py
+++ b/lib/sqlalchemy/dialects/sqlite/base.py
@@ -1023,8 +1023,11 @@ class SQLiteCompiler(compiler.SQLCompiler):
self.process(binary.right, **kw),
)
- def visit_empty_set_expr(self, type_):
- return "SELECT 1 FROM (SELECT 1) WHERE 1!=1"
+ def visit_empty_set_expr(self, element_types):
+ return "SELECT %s FROM (SELECT %s) WHERE 1!=1" % (
+ ", ".join("1" for type_ in element_types or [INTEGER()]),
+ ", ".join("1" for type_ in element_types or [INTEGER()]),
+ )
class SQLiteDDLCompiler(compiler.DDLCompiler):
@@ -1391,6 +1394,7 @@ class SQLiteDialect(default.DefaultDialect):
supports_empty_insert = False
supports_cast = True
supports_multivalues_insert = True
+ tuple_in_values = True
default_paramstyle = "qmark"
execution_ctx_cls = SQLiteExecutionContext
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index f6c30cbf4..b56755d62 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -75,6 +75,8 @@ class DefaultDialect(interfaces.Dialect):
supports_simple_order_by_label = True
+ tuple_in_values = False
+
engine_config_types = util.immutabledict(
[
("convert_unicode", util.bool_or_str("force")),
@@ -812,7 +814,9 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
for i, tuple_element in enumerate(values, 1)
for j, value in enumerate(tuple_element, 1)
]
- replacement_expressions[name] = ", ".join(
+ replacement_expressions[name] = (
+ "VALUES " if self.dialect.tuple_in_values else ""
+ ) + ", ".join(
"(%s)"
% ", ".join(
self.compiled.bindtemplate
diff --git a/lib/sqlalchemy/sql/coercions.py b/lib/sqlalchemy/sql/coercions.py
index 64d9f0f96..8a9f0b979 100644
--- a/lib/sqlalchemy/sql/coercions.py
+++ b/lib/sqlalchemy/sql/coercions.py
@@ -332,7 +332,9 @@ class InElementImpl(RoleImpl, roles.InElementRole):
o = expr._bind_param(operator, o)
args.append(o)
- return elements.ClauseList(*args)
+ return elements.ClauseList(
+ _tuple_values=isinstance(expr, elements.Tuple), *args
+ )
else:
self._raise_for_expected(element, **kw)
@@ -354,7 +356,6 @@ class InElementImpl(RoleImpl, roles.InElementRole):
return element.self_group(against=operator)
elif isinstance(element, elements.BindParameter) and element.expanding:
-
if isinstance(expr, elements.Tuple):
element = element._with_expanding_in_types(
[elem.type for elem in expr]
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index ea7e890e7..740abeb3d 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -966,13 +966,17 @@ class SQLCompiler(Compiled):
sep = " "
else:
sep = OPERATORS[clauselist.operator]
- return sep.join(
+
+ text = sep.join(
s
for s in (
c._compiler_dispatch(self, **kw) for c in clauselist.clauses
)
if s
)
+ if clauselist._tuple_values and self.dialect.tuple_in_values:
+ text = "VALUES " + text
+ return text
def visit_case(self, clause, **kwargs):
x = "CASE "
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 6d1174d20..e2df1adc2 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -1945,7 +1945,7 @@ class ClauseList(
self.operator = kwargs.pop("operator", operators.comma_op)
self.group = kwargs.pop("group", True)
self.group_contents = kwargs.pop("group_contents", True)
-
+ self._tuple_values = kwargs.pop("_tuple_values", False)
self._text_converter_role = text_converter_role = kwargs.pop(
"_literal_as_text_role", roles.WhereHavingRole
)
@@ -2011,6 +2011,8 @@ class ClauseList(
class BooleanClauseList(ClauseList, ColumnElement):
__visit_name__ = "clauselist"
+ _tuple_values = False
+
def __init__(self, *arg, **kw):
raise NotImplementedError(
"BooleanClauseList has a private constructor"
@@ -2162,13 +2164,15 @@ class Tuple(ClauseList, ColumnElement):
[(1, 2), (5, 12), (10, 19)]
)
+ .. versionchanged:: 1.3.6 Added support for SQLite IN tuples.
+
.. warning::
- The composite IN construct is not supported by all backends,
- and is currently known to work on PostgreSQL and MySQL,
- but not SQLite. Unsupported backends will raise
- a subclass of :class:`~sqlalchemy.exc.DBAPIError` when such
- an expression is invoked.
+ The composite IN construct is not supported by all backends, and is
+ currently known to work on PostgreSQL, MySQL, and SQLite.
+ Unsupported backends will raise a subclass of
+ :class:`~sqlalchemy.exc.DBAPIError` when such an expression is
+ invoked.
"""