summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/compiler.py51
-rw-r--r--lib/sqlalchemy/sql/expression.py185
-rw-r--r--lib/sqlalchemy/sql/util.py7
3 files changed, 163 insertions, 80 deletions
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 59e46de12..b902f9ffc 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -423,7 +423,7 @@ class SQLCompiler(engine.Compiled):
name = orig_name = column.name
if name is None:
raise exc.CompileError("Cannot compile Column object until "
- "it's 'name' is assigned.")
+ "its 'name' is assigned.")
is_literal = column.is_literal
if not is_literal and isinstance(name, sql._truncated_label):
@@ -787,14 +787,14 @@ class SQLCompiler(engine.Compiled):
existing = self.binds[name]
if existing is not bindparam:
if (existing.unique or bindparam.unique) and \
- not existing.proxy_set.intersection(bindparam.proxy_set):
+ not existing.proxy_set.intersection(
+ bindparam.proxy_set):
raise exc.CompileError(
"Bind parameter '%s' conflicts with "
"unique bind parameter of the same name" %
bindparam.key
)
- elif getattr(existing, '_is_crud', False) or \
- getattr(bindparam, '_is_crud', False):
+ elif existing._is_crud or bindparam._is_crud:
raise exc.CompileError(
"bindparam() name '%s' is reserved "
"for automatic usage in the VALUES or SET "
@@ -992,13 +992,15 @@ class SQLCompiler(engine.Compiled):
else:
self.result_map[keyname] = name, objects, type_
- def _label_select_column(self, select, column, populate_result_map,
+ def _label_select_column(self, select, column,
+ populate_result_map,
asfrom, column_clause_args,
+ name=None,
within_columns_clause=True):
"""produce labeled columns present in a select()."""
if column.type._has_column_expression and \
- populate_result_map:
+ populate_result_map:
col_expr = column.type.column_expression(column)
add_to_result_map = lambda keyname, name, objects, type_: \
self._add_to_result_map(
@@ -1023,13 +1025,11 @@ class SQLCompiler(engine.Compiled):
else:
result_expr = col_expr
- elif select is not None and \
- select.use_labels and \
- column._label:
+ elif select is not None and name:
result_expr = _CompileLabel(
col_expr,
- column._label,
- alt_names=(column._key_label, )
+ name,
+ alt_names=(column._key_label,)
)
elif \
@@ -1037,7 +1037,7 @@ class SQLCompiler(engine.Compiled):
isinstance(column, sql.ColumnClause) and \
not column.is_literal and \
column.table is not None and \
- not isinstance(column.table, sql.Select):
+ not isinstance(column.table, sql.Select):
result_expr = _CompileLabel(col_expr,
sql._as_truncated(column.name),
alt_names=(column.key,))
@@ -1086,14 +1086,9 @@ class SQLCompiler(engine.Compiled):
positional_names=None, **kwargs):
entry = self.stack and self.stack[-1] or {}
- if not asfrom:
- existingfroms = entry.get('from', None)
- else:
- # don't render correlations if we're rendering a FROM list
- # entry
- existingfroms = []
+ existingfroms = entry.get('from', None)
- froms = select._get_display_froms(existingfroms)
+ froms = select._get_display_froms(existingfroms, asfrom=asfrom)
correlate_froms = set(sql._from_objects(*froms))
@@ -1103,11 +1098,11 @@ class SQLCompiler(engine.Compiled):
# correlate_froms.union(existingfroms)
populate_result_map = force_result_map or (
- compound_index == 0 and (
- not entry or \
- entry.get('iswrapper', False)
- )
- )
+ compound_index == 0 and (
+ not entry or \
+ entry.get('iswrapper', False)
+ )
+ )
self.stack.append({'from': correlate_froms,
'iswrapper': iswrapper})
@@ -1122,10 +1117,12 @@ class SQLCompiler(engine.Compiled):
# the actual list of columns to print in the SELECT column list.
inner_columns = [
c for c in [
- self._label_select_column(select, column,
+ self._label_select_column(select,
+ column,
populate_result_map, asfrom,
- column_clause_args)
- for column in util.unique_list(select.inner_columns)
+ column_clause_args,
+ name=name)
+ for name, column in select._columns_plus_names
]
if c is not None
]
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 490004e39..28b1c6ddd 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -181,10 +181,10 @@ def select(columns=None, whereclause=None, from_obj=[], **kwargs):
string arguments, which will be converted as appropriate into
either :func:`text()` or :func:`literal_column()` constructs.
- See also:
+ .. seealso::
- :ref:`coretutorial_selecting` - Core Tutorial description of
- :func:`.select`.
+ :ref:`coretutorial_selecting` - Core Tutorial description of
+ :func:`.select`.
:param columns:
A list of :class:`.ClauseElement` objects, typically
@@ -464,7 +464,7 @@ def update(table, whereclause=None, values=None, inline=False, **kwargs):
as_scalar()
)
- See also:
+ .. seealso::
:ref:`inserts_and_updates` - SQL Expression
Language Tutorial
@@ -493,7 +493,7 @@ def delete(table, whereclause=None, **kwargs):
condition of the ``UPDATE`` statement. Note that the
:meth:`~Delete.where()` generative method may be used instead.
- See also:
+ .. seealso::
:ref:`deletes` - SQL Expression Tutorial
@@ -2873,6 +2873,8 @@ class BindParameter(ColumnElement):
__visit_name__ = 'bindparam'
quote = None
+ _is_crud = False
+
def __init__(self, key, value, type_=None, unique=False,
callable_=None,
isoutparam=False, required=False,
@@ -3073,7 +3075,7 @@ class Executable(Generative):
See :meth:`.Connection.execution_options` for a full list of
possible options.
- See also:
+ .. seealso::
:meth:`.Connection.execution_options()`
@@ -3444,15 +3446,15 @@ class Case(ColumnElement):
class FunctionElement(Executable, ColumnElement, FromClause):
"""Base for SQL function-oriented constructs.
- See also:
+ .. seealso::
- :class:`.Function` - named SQL function.
+ :class:`.Function` - named SQL function.
- :data:`.func` - namespace which produces registered or ad-hoc
- :class:`.Function` instances.
+ :data:`.func` - namespace which produces registered or ad-hoc
+ :class:`.Function` instances.
- :class:`.GenericFunction` - allows creation of registered function
- types.
+ :class:`.GenericFunction` - allows creation of registered function
+ types.
"""
@@ -3571,15 +3573,13 @@ class Function(FunctionElement):
See the superclass :class:`.FunctionElement` for a description
of public methods.
- See also:
-
- See also:
+ .. seealso::
- :data:`.func` - namespace which produces registered or ad-hoc
- :class:`.Function` instances.
+ :data:`.func` - namespace which produces registered or ad-hoc
+ :class:`.Function` instances.
- :class:`.GenericFunction` - allows creation of registered function
- types.
+ :class:`.GenericFunction` - allows creation of registered function
+ types.
"""
@@ -4725,7 +4725,9 @@ class SelectBase(Executable, FromClause):
"""return a 'scalar' representation of this selectable, embedded as a
subquery with a label.
- See also :meth:`~.SelectBase.as_scalar`.
+ .. seealso::
+
+ :meth:`~.SelectBase.as_scalar`.
"""
return self.as_scalar().label(name)
@@ -4843,9 +4845,9 @@ class SelectBase(Executable, FromClause):
result = conn.execute(statement).fetchall()
- See also:
+ .. seealso::
- :meth:`.orm.query.Query.cte` - ORM version of :meth:`.SelectBase.cte`.
+ :meth:`.orm.query.Query.cte` - ORM version of :meth:`.SelectBase.cte`.
"""
return CTE(self, name=name, recursive=recursive)
@@ -4914,6 +4916,10 @@ class SelectBase(Executable, FromClause):
The criterion will be appended to any pre-existing ORDER BY criterion.
+ This is an **in-place** mutation method; the
+ :meth:`~.SelectBase.order_by` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
if len(clauses) == 1 and clauses[0] is None:
self._order_by_clause = ClauseList()
@@ -4927,6 +4933,10 @@ class SelectBase(Executable, FromClause):
The criterion will be appended to any pre-existing GROUP BY criterion.
+ This is an **in-place** mutation method; the
+ :meth:`~.SelectBase.group_by` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
if len(clauses) == 1 and clauses[0] is None:
self._group_by_clause = ClauseList()
@@ -4980,7 +4990,7 @@ class CompoundSelect(SelectBase):
INTERSECT_ALL = util.symbol('INTERSECT ALL')
def __init__(self, keyword, *selects, **kwargs):
- self._should_correlate = kwargs.pop('correlate', False)
+ self._auto_correlate = kwargs.pop('correlate', False)
self.keyword = keyword
self.selects = []
@@ -5120,13 +5130,13 @@ class HasPrefixes(object):
class Select(HasPrefixes, SelectBase):
"""Represents a ``SELECT`` statement.
- See also:
+ .. seealso::
- :func:`~.expression.select` - the function which creates
- a :class:`.Select` object.
+ :func:`~.expression.select` - the function which creates
+ a :class:`.Select` object.
- :ref:`coretutorial_selecting` - Core Tutorial description
- of :func:`.select`.
+ :ref:`coretutorial_selecting` - Core Tutorial description
+ of :func:`.select`.
"""
@@ -5159,7 +5169,7 @@ class Select(HasPrefixes, SelectBase):
:class:`SelectBase` superclass.
"""
- self._should_correlate = correlate
+ self._auto_correlate = correlate
if distinct is not False:
if distinct is True:
self._distinct = True
@@ -5232,7 +5242,7 @@ class Select(HasPrefixes, SelectBase):
return froms
- def _get_display_froms(self, existing_froms=None):
+ def _get_display_froms(self, existing_froms=None, asfrom=False):
"""Return the full list of 'from' clauses to be displayed.
Takes into account a set of existing froms which may be
@@ -5258,18 +5268,29 @@ class Select(HasPrefixes, SelectBase):
# using a list to maintain ordering
froms = [f for f in froms if f not in toremove]
- if len(froms) > 1 or self._correlate or self._correlate_except:
+ if not asfrom:
if self._correlate:
- froms = [f for f in froms if f not in
- _cloned_intersection(froms,
- self._correlate)]
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(
+ _cloned_intersection(froms, existing_froms or ()),
+ self._correlate
+ )
+ ]
if self._correlate_except:
- froms = [f for f in froms if f in _cloned_intersection(froms,
- self._correlate_except)]
- if self._should_correlate and existing_froms:
- froms = [f for f in froms if f not in
- _cloned_intersection(froms,
- existing_froms)]
+ froms = [
+ f for f in froms if f in
+ _cloned_intersection(
+ froms,
+ self._correlate_except
+ )
+ ]
+
+ if self._auto_correlate and existing_froms and len(froms) > 1:
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(froms, existing_froms)
+ ]
if not len(froms):
raise exc.InvalidRequestError("Select statement '%s"
@@ -5642,7 +5663,7 @@ class Select(HasPrefixes, SelectBase):
:ref:`correlated_subqueries`
"""
- self._should_correlate = False
+ self._auto_correlate = False
if fromclauses and fromclauses[0] is None:
self._correlate = ()
else:
@@ -5662,7 +5683,7 @@ class Select(HasPrefixes, SelectBase):
:ref:`correlated_subqueries`
"""
- self._should_correlate = False
+ self._auto_correlate = False
if fromclauses and fromclauses[0] is None:
self._correlate_except = ()
else:
@@ -5671,9 +5692,15 @@ class Select(HasPrefixes, SelectBase):
def append_correlation(self, fromclause):
"""append the given correlation expression to this select()
- construct."""
+ construct.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.correlate` method is preferred, as it provides standard
+ :term:`method chaining`.
- self._should_correlate = False
+ """
+
+ self._auto_correlate = False
self._correlate = set(self._correlate).union(
_interpret_as_from(f) for f in fromclause)
@@ -5681,6 +5708,10 @@ class Select(HasPrefixes, SelectBase):
"""append the given column expression to the columns clause of this
select() construct.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.column` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
self._reset_exported()
column = _interpret_as_column_or_from(column)
@@ -5694,6 +5725,10 @@ class Select(HasPrefixes, SelectBase):
"""append the given columns clause prefix expression to this select()
construct.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.prefix_with` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
clause = _literal_as_text(clause)
self._prefixes = self._prefixes + (clause,)
@@ -5704,6 +5739,10 @@ class Select(HasPrefixes, SelectBase):
The expression will be joined to existing WHERE criterion via AND.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.where` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
self._reset_exported()
whereclause = _literal_as_text(whereclause)
@@ -5719,6 +5758,10 @@ class Select(HasPrefixes, SelectBase):
The expression will be joined to existing HAVING criterion via AND.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.having` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
if self._having is not None:
self._having = and_(self._having, _literal_as_text(having))
@@ -5729,18 +5772,56 @@ class Select(HasPrefixes, SelectBase):
"""append the given FromClause expression to this select() construct's
FROM clause.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.select_from` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
self._reset_exported()
fromclause = _interpret_as_from(fromclause)
self._from_obj = self._from_obj.union([fromclause])
+
+ @_memoized_property
+ def _columns_plus_names(self):
+ if self.use_labels:
+ names = set()
+ def name_for_col(c):
+ if c._label is None:
+ return (None, c)
+ name = c._label
+ if name in names:
+ name = c.anon_label
+ else:
+ names.add(name)
+ return name, c
+
+ return [
+ name_for_col(c)
+ for c in util.unique_list(_select_iterables(self._raw_columns))
+ ]
+ else:
+ return [
+ (None, c)
+ for c in util.unique_list(_select_iterables(self._raw_columns))
+ ]
+
def _populate_column_collection(self):
- for c in self.inner_columns:
- if hasattr(c, '_make_proxy'):
- c._make_proxy(self,
- name=c._label if self.use_labels else None,
- key=c._key_label if self.use_labels else None,
- name_is_truncatable=True)
+ for name, c in self._columns_plus_names:
+ if not hasattr(c, '_make_proxy'):
+ continue
+ if name is None:
+ key = None
+ elif self.use_labels:
+ key = c._key_label
+ if key is not None and key in self.c:
+ key = c.anon_label
+ else:
+ key = None
+
+ c._make_proxy(self, key=key,
+ name=name,
+ name_is_truncatable=True)
def _refresh_for_new_column(self, column):
for fromclause in self._froms:
@@ -6124,9 +6205,9 @@ class Insert(ValuesBase):
The :class:`.Insert` object is created using the
:func:`~.expression.insert()` function.
- See also:
+ .. seealso::
- :ref:`coretutorial_insert_expressions`
+ :ref:`coretutorial_insert_expressions`
"""
__visit_name__ = 'insert'
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index fd138cfec..520c90f99 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -13,12 +13,14 @@ from collections import deque
"""Utility functions that build upon SQL and Schema constructs."""
-def sort_tables(tables, skip_fn=None):
+def sort_tables(tables, skip_fn=None, extra_dependencies=None):
"""sort a collection of Table objects in order of
their foreign-key dependency."""
tables = list(tables)
tuples = []
+ if extra_dependencies is not None:
+ tuples.extend(extra_dependencies)
def visit_foreign_key(fkey):
if fkey.use_alter:
@@ -507,6 +509,9 @@ class AnnotatedColumnElement(Annotated):
"""pull 'key' from parent, if not present"""
return self._Annotated__element.key
+ @util.memoized_property
+ def info(self):
+ return self._Annotated__element.info
# hard-generate Annotated subclasses. this technique
# is used instead of on-the-fly types (i.e. type.__new__())