summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/_typing.py1
-rw-r--r--lib/sqlalchemy/sql/elements.py24
-rw-r--r--lib/sqlalchemy/sql/selectable.py30
3 files changed, 41 insertions, 14 deletions
diff --git a/lib/sqlalchemy/sql/_typing.py b/lib/sqlalchemy/sql/_typing.py
index a828d6a0f..14b1b9594 100644
--- a/lib/sqlalchemy/sql/_typing.py
+++ b/lib/sqlalchemy/sql/_typing.py
@@ -186,6 +186,7 @@ overall which brings in the TextClause object also.
"""
+
_ColumnExpressionOrLiteralArgument = Union[Any, _ColumnExpressionArgument[_T]]
_ColumnExpressionOrStrLabelArgument = Union[str, _ColumnExpressionArgument[_T]]
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index e51b755dd..a416b6ac0 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -99,6 +99,7 @@ if typing.TYPE_CHECKING:
from .selectable import _SelectIterable
from .selectable import FromClause
from .selectable import NamedFromClause
+ from .selectable import TextualSelect
from .sqltypes import TupleType
from .type_api import TypeEngine
from .visitors import _CloneCallableType
@@ -2385,7 +2386,9 @@ class TextClause(
return self
@util.preload_module("sqlalchemy.sql.selectable")
- def columns(self, *cols, **types):
+ def columns(
+ self, *cols: _ColumnExpressionArgument[Any], **types: TypeEngine[Any]
+ ) -> TextualSelect:
r"""Turn this :class:`_expression.TextClause` object into a
:class:`_expression.TextualSelect`
object that serves the same role as a SELECT
@@ -2503,29 +2506,38 @@ class TextClause(
"""
selectable = util.preloaded.sql_selectable
+
+ input_cols: List[NamedColumn[Any]] = [
+ coercions.expect(roles.LabeledColumnExprRole, col) for col in cols
+ ]
+
positional_input_cols = [
ColumnClause(col.key, types.pop(col.key))
if col.key in types
else col
- for col in cols
+ for col in input_cols
]
- keyed_input_cols: List[ColumnClause[Any]] = [
+ keyed_input_cols: List[NamedColumn[Any]] = [
ColumnClause(key, type_) for key, type_ in types.items()
]
- return selectable.TextualSelect(
+ elem = selectable.TextualSelect.__new__(selectable.TextualSelect)
+ elem._init(
self,
positional_input_cols + keyed_input_cols,
positional=bool(positional_input_cols) and not keyed_input_cols,
)
+ return elem
@property
- def type(self):
+ def type(self) -> TypeEngine[Any]:
return type_api.NULLTYPE
@property
def comparator(self):
- return self.type.comparator_factory(self)
+ # TODO: this seems wrong, it seems like we might not
+ # be using this method.
+ return self.type.comparator_factory(self) # type: ignore
def self_group(self, against=None):
if against is operators.in_op:
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index 75b5d09e3..39ef420dd 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -4551,7 +4551,7 @@ class SelectState(util.MemoizedSlots, CompileState):
@classmethod
def from_statement(
- cls, statement: Select[Any], from_statement: ExecutableReturnsRows
+ cls, statement: Select[Any], from_statement: roles.ReturnsRowsRole
) -> ExecutableReturnsRows:
cls._plugin_not_implemented()
@@ -5273,7 +5273,7 @@ class Select(
return meth(self)
def from_statement(
- self, statement: ExecutableReturnsRows
+ self, statement: roles.ReturnsRowsRole
) -> ExecutableReturnsRows:
"""Apply the columns which this :class:`.Select` would select
onto another statement.
@@ -6770,7 +6770,7 @@ class Exists(UnaryExpression[bool]):
return e
-class TextualSelect(SelectBase, Executable, Generative):
+class TextualSelect(SelectBase, ExecutableReturnsRows, Generative):
"""Wrap a :class:`_expression.TextClause` construct within a
:class:`_expression.SelectBase`
interface.
@@ -6815,14 +6815,28 @@ class TextualSelect(SelectBase, Executable, Generative):
def __init__(
self,
text: TextClause,
- columns: List[ColumnClause[Any]],
+ columns: List[_ColumnExpressionArgument[Any]],
+ positional: bool = False,
+ ) -> None:
+
+ self._init(
+ text,
+ # convert for ORM attributes->columns, etc
+ [
+ coercions.expect(roles.LabeledColumnExprRole, c)
+ for c in columns
+ ],
+ positional,
+ )
+
+ def _init(
+ self,
+ text: TextClause,
+ columns: List[NamedColumn[Any]],
positional: bool = False,
) -> None:
self.element = text
- # convert for ORM attributes->columns, etc
- self.column_args = [
- coercions.expect(roles.ColumnsClauseRole, c) for c in columns
- ]
+ self.column_args = columns
self.positional = positional
@HasMemoized_ro_memoized_attribute