--- /dev/null
+.. change::
+ :tags: bug, sql
+ :tickets: 9440
+
+ Fixed regression where the :func:`_sql.select` construct would not be able
+ to render if it were given no columns and then used in the context of an
+ EXISTS, raising an internal exception instead. While an empty "SELECT" is
+ not typically valid SQL, in the context of EXISTS databases such as
+ PostgreSQL allow it, and in any case the condition now no longer raises
+ an internal exception.
+
from_linter = None
warn_linting = False
+ # adjust the whitespace for no inner columns, part of #9440,
+ # so that a no-col SELECT comes out as "SELECT WHERE..." or
+ # "SELECT FROM ...".
+ # while it would be better to have built the SELECT starting string
+ # without trailing whitespace first, then add whitespace only if inner
+ # cols were present, this breaks compatibility with various custom
+ # compilation schemes that are currently being tested.
+ if not inner_columns:
+ text = text.rstrip()
+
if froms:
text += " \nFROM "
from .elements import TableValuedColumn
from .elements import UnaryExpression
from .operators import OperatorType
+from .sqltypes import NULLTYPE
from .visitors import _TraverseInternalsType
from .visitors import InternalTraversal
from .visitors import prefix_anon_map
GenerativeSelect.__init__(self)
def _scalar_type(self) -> TypeEngine[Any]:
+ if not self._raw_columns:
+ return NULLTYPE
elem = self._raw_columns[0]
cols = list(elem._select_iterable)
return cols[0].type
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import testing
+from sqlalchemy import true
from sqlalchemy import tuple_
from sqlalchemy import union
from sqlalchemy.sql import column
"WHERE mytable.myid = myothertable.otherid",
)
+ @testing.combinations(
+ (
+ lambda tbl: select().select_from(tbl).where(tbl.c.id == 123),
+ "SELECT FROM tbl WHERE tbl.id = :id_1",
+ ),
+ (lambda tbl: select().where(true()), "SELECT WHERE 1 = 1"),
+ (
+ lambda tbl: select()
+ .select_from(tbl)
+ .where(tbl.c.id == 123)
+ .exists(),
+ "EXISTS (SELECT FROM tbl WHERE tbl.id = :id_1)",
+ ),
+ )
+ def test_select_no_columns(self, stmt, expected):
+ """test #9440"""
+
+ tbl = table("tbl", column("id"))
+
+ stmt = testing.resolve_lambda(stmt, tbl=tbl)
+
+ self.assert_compile(stmt, expected)
+
def test_new_calling_style_clauseelement_thing_that_has_iter(self):
class Thing:
def __clause_element__(self):