We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
We need a way to specify Preproc function for base_grouping_column
## Description
In partial summarization, when the `grouping_expressions` also include the `base_grouping_column`, "guessing" does not work.
* We need a place to specify `preproc` functions for the `base_grouping_column`. We should either allow additionally specifying it within `grouping_expressions` or extend `base_grouping_column` to include `preproc`.
* If we do not allow it within `grouping_expressions`, the "guessed" response should not include it within `grouping_expressions`. Currently, it is also included.

cc: @dmos62  @mathemancer 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `db/transforms/operations/finish_specifying.py`
Content:
```
1 from db.tables.operations import select as tables_select
2 from db.tables.operations.select import get_joinable_tables
3 from db.transforms.base import Summarize, SelectSubsetOfColumns
4 from db.columns.operations.select import get_column_from_oid_and_attnum
5 
6 
7 def finish_specifying_summarize_transform(
8     db_query, ix_of_summarize_transform, engine, metadata
9 ):
10     """
11     Adds missing input aliases to the summarize transform.
12 
13     Will find input aliases that are not mentioned in the summarize_transform and will add each
14     of them to its group-by set and/or aggregate-on set.
15 
16     If the user selected input alias (summarize's base grouping column) is not unique-constrained,
17     will put the unmentioned input aliases in the aggregation set.
18 
19     If the user selected input alias (summarize's base grouping column) is unique-constrained,
20     then it might put at least some input aliases in the grouping set, depending on what
21     _should_group_by returns.
22     """
23     summarize_transform = db_query.transformations[ix_of_summarize_transform]
24     assert type(summarize_transform) is Summarize
25     aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on = \
26         _split_missing_input_aliases_into_group_and_agg_lists(
27             db_query, ix_of_summarize_transform, engine, metadata
28         )
29     there_is_something_to_add = \
30         aliases_to_be_added_to_group_by or aliases_to_be_added_to_group_by
31     if there_is_something_to_add:
32         summarize_transform = \
33             summarize_transform.get_new_with_aliases_added_to_agg_on(
34                 aliases_to_be_added_to_agg_on
35             )
36         summarize_transform = \
37             summarize_transform.get_new_with_aliases_added_to_group_by(
38                 aliases_to_be_added_to_group_by
39             )
40     return summarize_transform
41 
42 
43 def _split_missing_input_aliases_into_group_and_agg_lists(
44     db_query, ix_of_summarize_transform, engine, metadata,
45 ):
46     aliases_to_be_added_to_group_by = []
47     aliases_to_be_added_to_agg_on = []
48     summarize_transform = db_query.transformations[ix_of_summarize_transform]
49     initial_columns_not_in_summarize = \
50         _get_initial_columns_not_in_summarize(db_query, summarize_transform)
51     if not initial_columns_not_in_summarize:
52         # If all input aliases for summarize transform are in the transform's group-by or
53         # aggregate-on sets, there's nothing to do.
54         return aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on
55     # A summarize transform has a base_grouping_column (which is an alias) around
56     # which our suggestions will be based.
57     base_grouping_alias = summarize_transform.base_grouping_column
58     base_grouping_initial_column = \
59         _get_initial_column_by_alias(
60             db_query.initial_columns, base_grouping_alias
61         )
62     # We'll always want user-selected alias (base_grouping_column) in the "group-by set";
63     if base_grouping_initial_column in initial_columns_not_in_summarize:
64         aliases_to_be_added_to_group_by.append(base_grouping_alias)
65         initial_columns_not_in_summarize.remove(base_grouping_initial_column)
66     # Most of logic in the rest of method is around whether or not we can add some of the other
67     # input aliases to the "group-by set"; otherwise we'll put them in "aggregate-on set".
68     can_add_other_aliases_to_group_by = (
69         _is_first_alias_generating_transform(
70             db_query, ix_of_summarize_transform
71         )
72         and _is_initial_column_unique_constrained(
73             base_grouping_initial_column, engine, metadata
74         )
75     )
76     if can_add_other_aliases_to_group_by:
77         oids_of_joinable_tables_with_single_results = \
78             _get_oids_of_joinable_tables_with_single_results(
79                 db_query, engine, metadata
80             )
81         oid_of_base_grouping_initial_column = \
82             _get_oid_of_initial_column(base_grouping_initial_column)
83         for initial_column in initial_columns_not_in_summarize:
84             if _should_group_by(
85                 _get_oid_of_initial_column(initial_column),
86                 oid_of_base_grouping_initial_column,
87                 oids_of_joinable_tables_with_single_results,
88             ):
89                 alias_set_to_add_to = aliases_to_be_added_to_group_by
90             else:
91                 alias_set_to_add_to = aliases_to_be_added_to_agg_on
92             alias_set_to_add_to.append(initial_column.alias)
93     else:
94         aliases_to_be_added_to_agg_on = list(
95             initial_column.alias
96             for initial_column
97             in initial_columns_not_in_summarize
98         )
99     return aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on
100 
101 
102 def _is_first_alias_generating_transform(db_query, ix_of_summarize_transform):
103     """
104     Checks if the transform is the first alias-generating transform. An alias-generating transform
105     means that it itroduces new aliases (columns) to the transform pipeline. We want to know when
106     a given alias-generating transform is the first in the pipeline, because then we can consider
107     its input aliases to be fully described by initial columns, which can be a useful
108     simplification.
109     """
110     prior_transforms = db_query.transformations[:ix_of_summarize_transform]
111     for prior_transform in prior_transforms:
112         alias_generating_transforms = {Summarize, SelectSubsetOfColumns}
113         is_alias_generating = \
114             type(prior_transform) in alias_generating_transforms
115         if is_alias_generating:
116             return False
117     return True
118 
119 
120 def _get_initial_columns_not_in_summarize(db_query, summarize_transform):
121     initial_columns = db_query.initial_columns
122     group_by_aliases = summarize_transform.grouping_input_aliases
123     agg_on_aliases = summarize_transform.aggregation_input_aliases
124     aliases_in_summarize = group_by_aliases + agg_on_aliases
125     return [
126         initial_column
127         for initial_column in
128         initial_columns
129         if initial_column.alias not in aliases_in_summarize
130     ]
131 
132 
133 def _get_initial_column_by_alias(initial_columns, alias):
134     for initial_column in initial_columns:
135         if initial_column.alias == alias:
136             return initial_column
137 
138 
139 def _should_group_by(
140     oid_of_initial_column,
141     oid_of_base_grouping_group_by_col,
142     oids_of_joinable_tables_with_single_results,
143 ):
144     """
145     For the sake of efficiency, we're not checking here that base_grouping_group_by_col is unique
146     constrained: it is presumed that that is the case.
147     """
148     is_on_table_of_base_grouping_column = \
149         oid_of_initial_column == oid_of_base_grouping_group_by_col
150     is_single_result = \
151         oid_of_initial_column in oids_of_joinable_tables_with_single_results
152     should_group_by = \
153         is_on_table_of_base_grouping_column or is_single_result
154     return should_group_by
155 
156 
157 def _get_oids_of_joinable_tables_with_single_results(
158     db_query, engine, metadata,
159 ):
160     joinable_tables = \
161         get_joinable_tables(engine, metadata, db_query.base_table_oid)
162     return set(
163         _get_oid_of_joinable_table(joinable_table)
164         for joinable_table
165         in joinable_tables
166         if _has_single_result(joinable_table)
167     )
168 
169 
170 def _is_initial_column_unique_constrained(initial_column, engine, metadata):
171     oid = _get_oid_of_initial_column(initial_column)
172     attnum = initial_column.attnum
173     sa_column = get_column_from_oid_and_attnum(
174         table_oid=oid,
175         attnum=attnum,
176         engine=engine,
177         metadata=metadata,
178     )
179     return _is_sa_column_unique_constrained(sa_column)
180 
181 
182 def _is_sa_column_unique_constrained(sa_column):
183     return sa_column.primary_key or sa_column.unique
184 
185 
186 def _get_oid_of_initial_column(initial_column):
187     return initial_column.reloid
188 
189 
190 def _get_oid_of_joinable_table(joinable_table):
191     joinable_table_oid = joinable_table[tables_select.TARGET]
192     return joinable_table_oid
193 
194 
195 def _has_single_result(joinable_table):
196     has_multiple_results = joinable_table[tables_select.MULTIPLE_RESULTS]
197     assert type(has_multiple_results) is bool
198     return not has_multiple_results
199
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/db/transforms/operations/finish_specifying.py b/db/transforms/operations/finish_specifying.py
--- a/db/transforms/operations/finish_specifying.py
+++ b/db/transforms/operations/finish_specifying.py
@@ -26,17 +26,16 @@
         _split_missing_input_aliases_into_group_and_agg_lists(
             db_query, ix_of_summarize_transform, engine, metadata
         )
-    there_is_something_to_add = \
-        aliases_to_be_added_to_group_by or aliases_to_be_added_to_group_by
-    if there_is_something_to_add:
-        summarize_transform = \
-            summarize_transform.get_new_with_aliases_added_to_agg_on(
-                aliases_to_be_added_to_agg_on
-            )
+    if aliases_to_be_added_to_group_by:
         summarize_transform = \
             summarize_transform.get_new_with_aliases_added_to_group_by(
                 aliases_to_be_added_to_group_by
             )
+    if aliases_to_be_added_to_agg_on:
+        summarize_transform = \
+            summarize_transform.get_new_with_aliases_added_to_agg_on(
+                aliases_to_be_added_to_agg_on
+            )
     return summarize_transform
 
 
@@ -180,7 +179,10 @@
 
 
 def _is_sa_column_unique_constrained(sa_column):
-    return sa_column.primary_key or sa_column.unique
+    return bool(
+        sa_column.primary_key
+        or sa_column.unique
+    )
 
 
 def _get_oid_of_initial_column(initial_column):

{"golden_diff": "diff --git a/db/transforms/operations/finish_specifying.py b/db/transforms/operations/finish_specifying.py\n--- a/db/transforms/operations/finish_specifying.py\n+++ b/db/transforms/operations/finish_specifying.py\n@@ -26,17 +26,16 @@\n         _split_missing_input_aliases_into_group_and_agg_lists(\n             db_query, ix_of_summarize_transform, engine, metadata\n         )\n-    there_is_something_to_add = \\\n-        aliases_to_be_added_to_group_by or aliases_to_be_added_to_group_by\n-    if there_is_something_to_add:\n-        summarize_transform = \\\n-            summarize_transform.get_new_with_aliases_added_to_agg_on(\n-                aliases_to_be_added_to_agg_on\n-            )\n+    if aliases_to_be_added_to_group_by:\n         summarize_transform = \\\n             summarize_transform.get_new_with_aliases_added_to_group_by(\n                 aliases_to_be_added_to_group_by\n             )\n+    if aliases_to_be_added_to_agg_on:\n+        summarize_transform = \\\n+            summarize_transform.get_new_with_aliases_added_to_agg_on(\n+                aliases_to_be_added_to_agg_on\n+            )\n     return summarize_transform\n \n \n@@ -180,7 +179,10 @@\n \n \n def _is_sa_column_unique_constrained(sa_column):\n-    return sa_column.primary_key or sa_column.unique\n+    return bool(\n+        sa_column.primary_key\n+        or sa_column.unique\n+    )\n \n \n def _get_oid_of_initial_column(initial_column):\n", "issue": "We need a way to specify Preproc function for base_grouping_column\n## Description\r\nIn partial summarization, when the `grouping_expressions` also include the `base_grouping_column`, \"guessing\" does not work.\r\n* We need a place to specify `preproc` functions for the `base_grouping_column`. We should either allow additionally specifying it within `grouping_expressions` or extend `base_grouping_column` to include `preproc`.\r\n* If we do not allow it within `grouping_expressions`, the \"guessed\" response should not include it within `grouping_expressions`. Currently, it is also included.\r\n\r\ncc: @dmos62  @mathemancer \n", "before_files": [{"content": "from db.tables.operations import select as tables_select\nfrom db.tables.operations.select import get_joinable_tables\nfrom db.transforms.base import Summarize, SelectSubsetOfColumns\nfrom db.columns.operations.select import get_column_from_oid_and_attnum\n\n\ndef finish_specifying_summarize_transform(\n    db_query, ix_of_summarize_transform, engine, metadata\n):\n    \"\"\"\n    Adds missing input aliases to the summarize transform.\n\n    Will find input aliases that are not mentioned in the summarize_transform and will add each\n    of them to its group-by set and/or aggregate-on set.\n\n    If the user selected input alias (summarize's base grouping column) is not unique-constrained,\n    will put the unmentioned input aliases in the aggregation set.\n\n    If the user selected input alias (summarize's base grouping column) is unique-constrained,\n    then it might put at least some input aliases in the grouping set, depending on what\n    _should_group_by returns.\n    \"\"\"\n    summarize_transform = db_query.transformations[ix_of_summarize_transform]\n    assert type(summarize_transform) is Summarize\n    aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on = \\\n        _split_missing_input_aliases_into_group_and_agg_lists(\n            db_query, ix_of_summarize_transform, engine, metadata\n        )\n    there_is_something_to_add = \\\n        aliases_to_be_added_to_group_by or aliases_to_be_added_to_group_by\n    if there_is_something_to_add:\n        summarize_transform = \\\n            summarize_transform.get_new_with_aliases_added_to_agg_on(\n                aliases_to_be_added_to_agg_on\n            )\n        summarize_transform = \\\n            summarize_transform.get_new_with_aliases_added_to_group_by(\n                aliases_to_be_added_to_group_by\n            )\n    return summarize_transform\n\n\ndef _split_missing_input_aliases_into_group_and_agg_lists(\n    db_query, ix_of_summarize_transform, engine, metadata,\n):\n    aliases_to_be_added_to_group_by = []\n    aliases_to_be_added_to_agg_on = []\n    summarize_transform = db_query.transformations[ix_of_summarize_transform]\n    initial_columns_not_in_summarize = \\\n        _get_initial_columns_not_in_summarize(db_query, summarize_transform)\n    if not initial_columns_not_in_summarize:\n        # If all input aliases for summarize transform are in the transform's group-by or\n        # aggregate-on sets, there's nothing to do.\n        return aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on\n    # A summarize transform has a base_grouping_column (which is an alias) around\n    # which our suggestions will be based.\n    base_grouping_alias = summarize_transform.base_grouping_column\n    base_grouping_initial_column = \\\n        _get_initial_column_by_alias(\n            db_query.initial_columns, base_grouping_alias\n        )\n    # We'll always want user-selected alias (base_grouping_column) in the \"group-by set\";\n    if base_grouping_initial_column in initial_columns_not_in_summarize:\n        aliases_to_be_added_to_group_by.append(base_grouping_alias)\n        initial_columns_not_in_summarize.remove(base_grouping_initial_column)\n    # Most of logic in the rest of method is around whether or not we can add some of the other\n    # input aliases to the \"group-by set\"; otherwise we'll put them in \"aggregate-on set\".\n    can_add_other_aliases_to_group_by = (\n        _is_first_alias_generating_transform(\n            db_query, ix_of_summarize_transform\n        )\n        and _is_initial_column_unique_constrained(\n            base_grouping_initial_column, engine, metadata\n        )\n    )\n    if can_add_other_aliases_to_group_by:\n        oids_of_joinable_tables_with_single_results = \\\n            _get_oids_of_joinable_tables_with_single_results(\n                db_query, engine, metadata\n            )\n        oid_of_base_grouping_initial_column = \\\n            _get_oid_of_initial_column(base_grouping_initial_column)\n        for initial_column in initial_columns_not_in_summarize:\n            if _should_group_by(\n                _get_oid_of_initial_column(initial_column),\n                oid_of_base_grouping_initial_column,\n                oids_of_joinable_tables_with_single_results,\n            ):\n                alias_set_to_add_to = aliases_to_be_added_to_group_by\n            else:\n                alias_set_to_add_to = aliases_to_be_added_to_agg_on\n            alias_set_to_add_to.append(initial_column.alias)\n    else:\n        aliases_to_be_added_to_agg_on = list(\n            initial_column.alias\n            for initial_column\n            in initial_columns_not_in_summarize\n        )\n    return aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on\n\n\ndef _is_first_alias_generating_transform(db_query, ix_of_summarize_transform):\n    \"\"\"\n    Checks if the transform is the first alias-generating transform. An alias-generating transform\n    means that it itroduces new aliases (columns) to the transform pipeline. We want to know when\n    a given alias-generating transform is the first in the pipeline, because then we can consider\n    its input aliases to be fully described by initial columns, which can be a useful\n    simplification.\n    \"\"\"\n    prior_transforms = db_query.transformations[:ix_of_summarize_transform]\n    for prior_transform in prior_transforms:\n        alias_generating_transforms = {Summarize, SelectSubsetOfColumns}\n        is_alias_generating = \\\n            type(prior_transform) in alias_generating_transforms\n        if is_alias_generating:\n            return False\n    return True\n\n\ndef _get_initial_columns_not_in_summarize(db_query, summarize_transform):\n    initial_columns = db_query.initial_columns\n    group_by_aliases = summarize_transform.grouping_input_aliases\n    agg_on_aliases = summarize_transform.aggregation_input_aliases\n    aliases_in_summarize = group_by_aliases + agg_on_aliases\n    return [\n        initial_column\n        for initial_column in\n        initial_columns\n        if initial_column.alias not in aliases_in_summarize\n    ]\n\n\ndef _get_initial_column_by_alias(initial_columns, alias):\n    for initial_column in initial_columns:\n        if initial_column.alias == alias:\n            return initial_column\n\n\ndef _should_group_by(\n    oid_of_initial_column,\n    oid_of_base_grouping_group_by_col,\n    oids_of_joinable_tables_with_single_results,\n):\n    \"\"\"\n    For the sake of efficiency, we're not checking here that base_grouping_group_by_col is unique\n    constrained: it is presumed that that is the case.\n    \"\"\"\n    is_on_table_of_base_grouping_column = \\\n        oid_of_initial_column == oid_of_base_grouping_group_by_col\n    is_single_result = \\\n        oid_of_initial_column in oids_of_joinable_tables_with_single_results\n    should_group_by = \\\n        is_on_table_of_base_grouping_column or is_single_result\n    return should_group_by\n\n\ndef _get_oids_of_joinable_tables_with_single_results(\n    db_query, engine, metadata,\n):\n    joinable_tables = \\\n        get_joinable_tables(engine, metadata, db_query.base_table_oid)\n    return set(\n        _get_oid_of_joinable_table(joinable_table)\n        for joinable_table\n        in joinable_tables\n        if _has_single_result(joinable_table)\n    )\n\n\ndef _is_initial_column_unique_constrained(initial_column, engine, metadata):\n    oid = _get_oid_of_initial_column(initial_column)\n    attnum = initial_column.attnum\n    sa_column = get_column_from_oid_and_attnum(\n        table_oid=oid,\n        attnum=attnum,\n        engine=engine,\n        metadata=metadata,\n    )\n    return _is_sa_column_unique_constrained(sa_column)\n\n\ndef _is_sa_column_unique_constrained(sa_column):\n    return sa_column.primary_key or sa_column.unique\n\n\ndef _get_oid_of_initial_column(initial_column):\n    return initial_column.reloid\n\n\ndef _get_oid_of_joinable_table(joinable_table):\n    joinable_table_oid = joinable_table[tables_select.TARGET]\n    return joinable_table_oid\n\n\ndef _has_single_result(joinable_table):\n    has_multiple_results = joinable_table[tables_select.MULTIPLE_RESULTS]\n    assert type(has_multiple_results) is bool\n    return not has_multiple_results\n", "path": "db/transforms/operations/finish_specifying.py"}], "after_files": [{"content": "from db.tables.operations import select as tables_select\nfrom db.tables.operations.select import get_joinable_tables\nfrom db.transforms.base import Summarize, SelectSubsetOfColumns\nfrom db.columns.operations.select import get_column_from_oid_and_attnum\n\n\ndef finish_specifying_summarize_transform(\n    db_query, ix_of_summarize_transform, engine, metadata\n):\n    \"\"\"\n    Adds missing input aliases to the summarize transform.\n\n    Will find input aliases that are not mentioned in the summarize_transform and will add each\n    of them to its group-by set and/or aggregate-on set.\n\n    If the user selected input alias (summarize's base grouping column) is not unique-constrained,\n    will put the unmentioned input aliases in the aggregation set.\n\n    If the user selected input alias (summarize's base grouping column) is unique-constrained,\n    then it might put at least some input aliases in the grouping set, depending on what\n    _should_group_by returns.\n    \"\"\"\n    summarize_transform = db_query.transformations[ix_of_summarize_transform]\n    assert type(summarize_transform) is Summarize\n    aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on = \\\n        _split_missing_input_aliases_into_group_and_agg_lists(\n            db_query, ix_of_summarize_transform, engine, metadata\n        )\n    if aliases_to_be_added_to_group_by:\n        summarize_transform = \\\n            summarize_transform.get_new_with_aliases_added_to_group_by(\n                aliases_to_be_added_to_group_by\n            )\n    if aliases_to_be_added_to_agg_on:\n        summarize_transform = \\\n            summarize_transform.get_new_with_aliases_added_to_agg_on(\n                aliases_to_be_added_to_agg_on\n            )\n    return summarize_transform\n\n\ndef _split_missing_input_aliases_into_group_and_agg_lists(\n    db_query, ix_of_summarize_transform, engine, metadata,\n):\n    aliases_to_be_added_to_group_by = []\n    aliases_to_be_added_to_agg_on = []\n    summarize_transform = db_query.transformations[ix_of_summarize_transform]\n    initial_columns_not_in_summarize = \\\n        _get_initial_columns_not_in_summarize(db_query, summarize_transform)\n    if not initial_columns_not_in_summarize:\n        # If all input aliases for summarize transform are in the transform's group-by or\n        # aggregate-on sets, there's nothing to do.\n        return aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on\n    # A summarize transform has a base_grouping_column (which is an alias) around\n    # which our suggestions will be based.\n    base_grouping_alias = summarize_transform.base_grouping_column\n    base_grouping_initial_column = \\\n        _get_initial_column_by_alias(\n            db_query.initial_columns, base_grouping_alias\n        )\n    # We'll always want user-selected alias (base_grouping_column) in the \"group-by set\";\n    if base_grouping_initial_column in initial_columns_not_in_summarize:\n        aliases_to_be_added_to_group_by.append(base_grouping_alias)\n        initial_columns_not_in_summarize.remove(base_grouping_initial_column)\n    # Most of logic in the rest of method is around whether or not we can add some of the other\n    # input aliases to the \"group-by set\"; otherwise we'll put them in \"aggregate-on set\".\n    can_add_other_aliases_to_group_by = (\n        _is_first_alias_generating_transform(\n            db_query, ix_of_summarize_transform\n        )\n        and _is_initial_column_unique_constrained(\n            base_grouping_initial_column, engine, metadata\n        )\n    )\n    if can_add_other_aliases_to_group_by:\n        oids_of_joinable_tables_with_single_results = \\\n            _get_oids_of_joinable_tables_with_single_results(\n                db_query, engine, metadata\n            )\n        oid_of_base_grouping_initial_column = \\\n            _get_oid_of_initial_column(base_grouping_initial_column)\n        for initial_column in initial_columns_not_in_summarize:\n            if _should_group_by(\n                _get_oid_of_initial_column(initial_column),\n                oid_of_base_grouping_initial_column,\n                oids_of_joinable_tables_with_single_results,\n            ):\n                alias_set_to_add_to = aliases_to_be_added_to_group_by\n            else:\n                alias_set_to_add_to = aliases_to_be_added_to_agg_on\n            alias_set_to_add_to.append(initial_column.alias)\n    else:\n        aliases_to_be_added_to_agg_on = list(\n            initial_column.alias\n            for initial_column\n            in initial_columns_not_in_summarize\n        )\n    return aliases_to_be_added_to_group_by, aliases_to_be_added_to_agg_on\n\n\ndef _is_first_alias_generating_transform(db_query, ix_of_summarize_transform):\n    \"\"\"\n    Checks if the transform is the first alias-generating transform. An alias-generating transform\n    means that it itroduces new aliases (columns) to the transform pipeline. We want to know when\n    a given alias-generating transform is the first in the pipeline, because then we can consider\n    its input aliases to be fully described by initial columns, which can be a useful\n    simplification.\n    \"\"\"\n    prior_transforms = db_query.transformations[:ix_of_summarize_transform]\n    for prior_transform in prior_transforms:\n        alias_generating_transforms = {Summarize, SelectSubsetOfColumns}\n        is_alias_generating = \\\n            type(prior_transform) in alias_generating_transforms\n        if is_alias_generating:\n            return False\n    return True\n\n\ndef _get_initial_columns_not_in_summarize(db_query, summarize_transform):\n    initial_columns = db_query.initial_columns\n    group_by_aliases = summarize_transform.grouping_input_aliases\n    agg_on_aliases = summarize_transform.aggregation_input_aliases\n    aliases_in_summarize = group_by_aliases + agg_on_aliases\n    return [\n        initial_column\n        for initial_column in\n        initial_columns\n        if initial_column.alias not in aliases_in_summarize\n    ]\n\n\ndef _get_initial_column_by_alias(initial_columns, alias):\n    for initial_column in initial_columns:\n        if initial_column.alias == alias:\n            return initial_column\n\n\ndef _should_group_by(\n    oid_of_initial_column,\n    oid_of_base_grouping_group_by_col,\n    oids_of_joinable_tables_with_single_results,\n):\n    \"\"\"\n    For the sake of efficiency, we're not checking here that base_grouping_group_by_col is unique\n    constrained: it is presumed that that is the case.\n    \"\"\"\n    is_on_table_of_base_grouping_column = \\\n        oid_of_initial_column == oid_of_base_grouping_group_by_col\n    is_single_result = \\\n        oid_of_initial_column in oids_of_joinable_tables_with_single_results\n    should_group_by = \\\n        is_on_table_of_base_grouping_column or is_single_result\n    return should_group_by\n\n\ndef _get_oids_of_joinable_tables_with_single_results(\n    db_query, engine, metadata,\n):\n    joinable_tables = \\\n        get_joinable_tables(engine, metadata, db_query.base_table_oid)\n    return set(\n        _get_oid_of_joinable_table(joinable_table)\n        for joinable_table\n        in joinable_tables\n        if _has_single_result(joinable_table)\n    )\n\n\ndef _is_initial_column_unique_constrained(initial_column, engine, metadata):\n    oid = _get_oid_of_initial_column(initial_column)\n    attnum = initial_column.attnum\n    sa_column = get_column_from_oid_and_attnum(\n        table_oid=oid,\n        attnum=attnum,\n        engine=engine,\n        metadata=metadata,\n    )\n    return _is_sa_column_unique_constrained(sa_column)\n\n\ndef _is_sa_column_unique_constrained(sa_column):\n    return bool(\n        sa_column.primary_key\n        or sa_column.unique\n    )\n\n\ndef _get_oid_of_initial_column(initial_column):\n    return initial_column.reloid\n\n\ndef _get_oid_of_joinable_table(joinable_table):\n    joinable_table_oid = joinable_table[tables_select.TARGET]\n    return joinable_table_oid\n\n\ndef _has_single_result(joinable_table):\n    has_multiple_results = joinable_table[tables_select.MULTIPLE_RESULTS]\n    assert type(has_multiple_results) is bool\n    return not has_multiple_results\n", "path": "db/transforms/operations/finish_specifying.py"}]}