Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 3ddbfe94 authored by Bruno Guillon's avatar Bruno Guillon
Browse files

column_origins ⟶ selected_columns

parent 845d9120
Branches
No related tags found
1 merge request!79Make `selected_columns` be the term used for internal columns of Select.
Pipeline #869475 passed
from querybuilder.schemas import table, schema
from querybuilder.schemas.helper import table, schema
from querybuilder.queries.algebra.columns import (
Value,
Constant,
......
......@@ -109,14 +109,14 @@ def name_column(
schema_name: Optional[str] = None,
preserve_table_column: bool = False,
) -> Column:
if not name:
name = getattr(column, "name", None)
if preserve_table_column and isinstance(column, TableColumn):
return column.buildfrom(
column, relation_name=relation_name, schema_name=schema_name
column, name=name, relation_name=relation_name, schema_name=schema_name
)
if not name:
name = getattr(column, "name", None)
if name:
return Named(
sqltype=column.sqltype,
......
......@@ -126,7 +126,7 @@ class Fromable(Relation):
# assert isinstance(c, (int, str))
columns_to_select.append(self.columns.resolve(c))
return querybuilder.queries.dql.Select(
from_=self, columns=columns_to_select, **kwargs
from_=self, selected_columns=columns_to_select, **kwargs
)
@method_accepting_lambdas
......
......@@ -151,7 +151,7 @@ class Values(DQLQuery):
class Select(DQLQuery):
__slots__: tuple[str, ...] = (
"column_origins",
"selected_columns",
"aliases",
"from_",
"where",
......@@ -167,7 +167,7 @@ class Select(DQLQuery):
def __init__(
self,
columns: Sequence[qbcolumns.Column | qbcolumns.Star],
selected_columns: Sequence[qbcolumns.Column | qbcolumns.Star],
aliases: Mapping[int, str] | Iterable[tuple[int, str]] = (),
distinct: Optional[qbcolumns.Column | bool] = None,
from_: Optional[qbrelations.Fromable] = None,
......@@ -191,12 +191,12 @@ class Select(DQLQuery):
self.offset = offset
self.from_ = from_
super().__init__(columns=columns, **kwargs)
super().__init__(columns=selected_columns, **kwargs)
@property
def columns(self):
columns = []
for i, c in enumerate(self.column_origins):
for i, c in enumerate(self.selected_columns):
if isinstance(c, qbcolumns.Star):
columns.extend(self.from_.columns)
else:
......@@ -208,19 +208,21 @@ class Select(DQLQuery):
return self._column_store_factory(columns)
def _init_columns(self, columns):
def _init_columns(self, selected_columns):
for k in self.aliases.keys():
if 0 <= k < len(columns) and isinstance(columns[k], qbcolumns.Star):
if 0 <= k < len(selected_columns) and isinstance(
selected_columns[k], qbcolumns.Star
):
raise KeyError("Cannot alias a * column")
for c in columns:
for c in selected_columns:
if isinstance(c, qbcolumns.Star):
assert self.from_ # TODO: Maybe another exception would be better
break
self.column_origins = self._column_store_factory(columns)
self.selected_columns = self._column_store_factory(selected_columns)
def accept(self, visitor, /):
for col in self.column_origins:
for col in self.selected_columns:
visitor = col.accept(visitor)
for res in (self.from_, self.where, self.groupby, self.having, self.orderby):
if res:
......@@ -230,7 +232,7 @@ class Select(DQLQuery):
def descend(self, accumulator):
accumulator = super().descend(accumulator)
for ocol in self.column_origins:
for ocol in self.selected_columns:
accumulator = ocol.descend(accumulator)
for res in (self.from_, self.groupby, self.having, self.orderby, self.where):
......@@ -240,16 +242,20 @@ class Select(DQLQuery):
return accumulator
def set_selected_columns(
self, columns: Iterable[str | int | qbcolumns.Column | qbcolumns.Star], /
self,
selected_columns: Iterable[str | int | qbcolumns.Column | qbcolumns.Star],
/,
) -> Select:
"""Method for resetting selected columns of the query
Aliases are not preserved and Star is allowed.
Parameters
----------
columns: Iterable[str | int | qbcolumns.Column | qbcolumns.Star]
selected_columns: Iterable[str | int | qbcolumns.Column | qbcolumns.Star]
the specifications of the columns to select. Each specification might be an
index (int), a name (str), an explicit column, or the special operator Star.
In the two first cases, it is resolved within the `column_origins` store,
In the two first cases, it is resolved within the `selected_columns` store,
using the `resolve` method. You may want to consider using `set_columns` for
setting columns using similar specifications that are resolved within the
`columns` store context instead. A side effect of the method is that all
......@@ -276,22 +282,35 @@ class Select(DQLQuery):
'SELECT t.y, 2, t.x FROM t'
Indeed, in the list parameter `[2, Constant(int, 2), "x"]`, `2` stands for the
origin column of index 2 (ie, `sq.column_origins[2]`), and `"x"` stands for the
origin column that is unambiguously named "x" (ie, `sq.column_origins._["x"]`).
selected column of index 2 (ie, `sq.selected_columns[2]`), and `"x"` stands for the
selected column that is unambiguously named "x" (ie, `sq.selected_columns._["x"]`).
It is also possible to use `querybuilder.queries.algebra.columns.Star()`, or the
`"*"` shorthand, in the sequence of selected columns.
>>> from querybuilder.queries.algebra.columns import Star
>>> str(sq.set_selected_columns(["*", 2, Star(), "x"]))
'SELECT *, t.y, *, t.x FROM t'
"""
cols: list[qbcolumns.Column | qbcolumns.Star] = []
for c in columns:
for c in selected_columns:
if c == "*":
assert self.from_
cols.append(qbcolumns.Star())
elif isinstance(c, (qbcolumns.Column, qbcolumns.Star)):
elif isinstance(c, qbcolumns.Star):
assert self.from_
cols.append(c)
elif isinstance(c, qbcolumns.Column):
cols.append(c)
else:
# assert isinstance(c, (int, str))
cols.append(self.column_origins.resolve(c))
cols.append(self.selected_columns.resolve(c))
return self.set_aliases(())._set_columns(cols)
def set_columns(self, columns: Iterable[str | int | qbcolumns.Column], /) -> Select:
"""Method for resetting exposed (thus selected) columns of the query
"""Method for resetting exposed columns of the query
Aliases are kept; Star is forbidden.
Parameters
----------
......@@ -301,7 +320,7 @@ class Select(DQLQuery):
is resolved within the `columns` store, using the `resolve` method. If the
corresponding column was aliased, then the alias is kept. The special Star
operator is not allowed here, since the specifications are resolved according
to the exposed columns, and not their origins.
to the exposed columns, and not their selected origins.
Related methods
---------------
......@@ -344,8 +363,9 @@ class Select(DQLQuery):
else:
orig_c = c
index = None
if index is not None:
orig_c = self.column_origins[index]
orig_c = self.selected_columns[index]
alias = self.aliases.get(index)
if alias:
aliases[i] = alias
......@@ -426,7 +446,7 @@ class Select(DQLQuery):
clauses.AliasedColumn(c, self.aliases.get(i, None)).subtokenize(tokenizer)
if isinstance(c, qbcolumns.Column)
else c.subtokenize(tokenizer)
for i, c in enumerate(self.column_origins)
for i, c in enumerate(self.selected_columns)
)
if self.distinct:
add_kwargs["distinct"] = clauses.DistinctColumn(self.distinct).subtokenize(
......@@ -469,16 +489,12 @@ class Select(DQLQuery):
return self.buildfrom(self, columns=columns)
def _post_getstate(self, state):
state["columns"] = state.pop("column_origins")
state["columns"] = state.pop("selected_columns")
return state
def _substitute(self, substitutions: Mapping):
columns = map(
operator.methodcaller("substitute", substitutions),
self.column_origins,
)
d = dict(columns=list(columns))
columns = [c.substitute(substitutions) for c in self.selected_columns]
d = dict(columns=columns)
for k in ("where", "distinct", "groupby", "orderby", "having", "from_"):
res = getattr(self, k)
......
......@@ -42,7 +42,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_columns_unaliased_columns_with_list_of_strings(self):
......@@ -55,7 +55,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_columns_unaliased_columns_with_list_of_columns(self):
......@@ -68,7 +68,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_columns_unaliased_columns_with_list_of_mixed_specifications(self):
......@@ -88,7 +88,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
expected_columns.append(qbcolumns.Constant(int, 3))
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_columns_aliased_columns_with_list_of_integers(self):
......@@ -103,7 +103,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
expected_aliases = {0: "a0", 1: "a4", 3: "a0"}
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert expected_aliases == proj.aliases
def test_set_columns_aliased_columns_with_list_of_strings(self):
......@@ -118,7 +118,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
expected_aliases = {0: "a0", 1: "a4", 3: "a0"}
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert expected_aliases == proj.aliases
def test_set_columns_aliased_columns_with_list_of_columns(self):
......@@ -134,7 +134,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
expected_aliases = {0: "a0", 1: "a4", 3: "a0"}
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert expected_aliases == proj.aliases
def test_set_columns_aliased_columns_with_list_of_mixed_specifications(self):
......@@ -151,7 +151,7 @@ class TestSelect(TestDQL):
expected_columns.append(qbcolumns.Constant(int, 3))
expected_aliases = {0: "a0", 1: "a4", 3: "a0"}
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert expected_aliases == proj.aliases
def test_set_selected_columns_unaliased_columns_with_list_of_integers(self):
......@@ -164,7 +164,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_selected_columns_unaliased_columns_with_list_of_strings(self):
......@@ -177,7 +177,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_selected_columns_unaliased_columns_with_list_of_columns(self):
......@@ -190,7 +190,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_selected_columns_unaliased_columns_with_list_of_mixed_specifications(
......@@ -212,7 +212,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
expected_columns.append(qbcolumns.Constant(int, 3))
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert {} == proj.aliases
def test_set_selected_columns_aliased_columns_with_list_of_integers(self):
......@@ -226,7 +226,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert not proj.aliases
def test_set_selected_columns_aliased_columns_with_list_of_strings(self):
......@@ -240,7 +240,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert not proj.aliases
def test_set_selected_columns_aliased_columns_with_list_of_columns(self):
......@@ -255,7 +255,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert not proj.aliases
def test_set_selected_columns_aliased_columns_with_list_of_mixed_specifications(
......@@ -273,7 +273,7 @@ class TestSelect(TestDQL):
expected_columns = [qbcolumns.Named(int, f"c{i}") for i in proj_indices]
expected_columns.append(qbcolumns.Constant(int, 3))
assert expected_columns == list(proj.column_origins)
assert expected_columns == list(proj.selected_columns)
assert not proj.aliases
def test_column_property(self):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment