mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 17:03:32 +03:00
Improve details attached to No_Output_Columns
reported from various operations (#8528)
- Closes #7635
This commit is contained in:
parent
c265787506
commit
7a05e679c3
@ -298,8 +298,7 @@ type Table
|
||||
select_blank_columns : Blank_Selector -> Boolean -> Table
|
||||
select_blank_columns self (when:Blank_Selector = Blank_Selector.All_Cells) treat_nans_as_blank=False =
|
||||
new_columns = self.columns_helper.select_blank_columns_helper when treat_nans_as_blank
|
||||
if new_columns.length == 0 then Error.throw (No_Output_Columns) else
|
||||
self.updated_columns new_columns
|
||||
self.updated_columns new_columns
|
||||
|
||||
## ALIAS drop_missing_columns, drop_na
|
||||
GROUP Standard.Base.Selections
|
||||
@ -325,8 +324,7 @@ type Table
|
||||
remove_blank_columns : Blank_Selector -> Boolean -> Table
|
||||
remove_blank_columns self (when:Blank_Selector = Blank_Selector.All_Cells) treat_nans_as_blank=False =
|
||||
new_columns = self.columns_helper.select_blank_columns_helper when treat_nans_as_blank invert_selection=True
|
||||
if new_columns.length == 0 then Error.throw (No_Output_Columns) else
|
||||
self.updated_columns new_columns
|
||||
self.updated_columns new_columns
|
||||
|
||||
## GROUP Standard.Base.Selections
|
||||
ICON select_column
|
||||
@ -1568,12 +1566,15 @@ type Table
|
||||
v : Vector -> [self] + (v.map t-> Table.from t)
|
||||
single_table -> [self, single_table]
|
||||
Helpers.ensure_same_connection "table" all_tables <|
|
||||
problem_builder = Problem_Builder.new
|
||||
matched_column_sets = Match_Columns_Helpers.match_columns all_tables match_columns keep_unmatched_columns problem_builder
|
||||
## We keep separate problem builders, because if we are reporting `No_Output_Columns`,
|
||||
we only want to add a cause coming from unification; matching reports problems that would not fit this error.
|
||||
problem_builder_for_matching = Problem_Builder.new
|
||||
problem_builder_for_unification = Problem_Builder.new
|
||||
matched_column_sets = Match_Columns_Helpers.match_columns all_tables match_columns keep_unmatched_columns problem_builder_for_matching
|
||||
dialect = self.connection.dialect
|
||||
type_mapping = dialect.get_type_mapping
|
||||
merged_columns = matched_column_sets.map column_set->
|
||||
case Table_Helpers.unify_result_type_for_union column_set all_tables allow_type_widening problem_builder of
|
||||
case Table_Helpers.unify_result_type_for_union column_set all_tables allow_type_widening problem_builder_for_unification of
|
||||
Nothing -> Nothing
|
||||
result_type : Value_Type ->
|
||||
sql_type = type_mapping.value_type_to_sql result_type Problem_Behavior.Report_Error
|
||||
@ -1582,8 +1583,8 @@ type Table
|
||||
Illegal_State.Error "Unexpected inexact type coercion in Union. The union logic should only operate in types supported by the given backend. This is a bug in the Database library. The coercion was: "+error.to_display_text cause=error
|
||||
[column_set, sql_type, result_type]
|
||||
good_columns = merged_columns.filter r-> r.is_nothing.not
|
||||
if good_columns.is_empty then Error.throw No_Output_Columns else
|
||||
problem_builder.attach_problems_before on_problems <|
|
||||
problem_builder_for_matching.attach_problems_before on_problems <| problem_builder_for_unification.attach_problems_before on_problems <|
|
||||
if good_columns.is_empty then problem_builder_for_unification.raise_no_output_columns_with_cause else
|
||||
queries = all_tables.map_with_index i-> t->
|
||||
columns_to_select = good_columns.map description->
|
||||
column_set = description.first
|
||||
@ -1735,7 +1736,10 @@ type Table
|
||||
problem_builder.attach_problems_before on_problems <|
|
||||
problems = partitioned.first.map .value
|
||||
on_problems.attach_problems_before problems <|
|
||||
if new_columns.is_empty then (Error.throw No_Output_Columns) else
|
||||
handle_no_output_columns =
|
||||
first_problem = if problems.is_empty then Nothing else problems.first
|
||||
Error.throw (No_Output_Columns.Error first_problem)
|
||||
if new_columns.is_empty then handle_no_output_columns else
|
||||
self.updated_context_and_columns new_ctx new_columns subquery=True
|
||||
|
||||
## GROUP Standard.Base.Calculations
|
||||
|
@ -38,7 +38,7 @@ match_columns tables matching_mode keep_unmatched_columns problem_builder = case
|
||||
# This will only include columns that were present in all tables.
|
||||
common_column_names = tables.first.column_names.filter name->
|
||||
column_counts.at name == tables.length
|
||||
if common_column_names.is_empty then Error.throw No_Output_Columns else
|
||||
if common_column_names.is_empty then Error.throw (No_Output_Columns.Error "Unmatched columns are set to be dropped, but no common column names were found.") else
|
||||
common_column_names.map name->
|
||||
column_indices = tables.map table->
|
||||
table.column_names.index_of name
|
||||
|
@ -440,8 +440,7 @@ type Table
|
||||
select_blank_columns : Blank_Selector -> Boolean -> Table ! No_Output_Columns
|
||||
select_blank_columns self (when:Blank_Selector = Blank_Selector.All_Cells) (treat_nans_as_blank : Boolean = False) =
|
||||
new_columns = self.columns_helper.select_blank_columns_helper when treat_nans_as_blank
|
||||
if new_columns.length == 0 then Error.throw (No_Output_Columns) else
|
||||
Table.new new_columns
|
||||
Table.new new_columns
|
||||
|
||||
## ALIAS drop_missing_columns, drop_na
|
||||
GROUP Standard.Base.Selections
|
||||
@ -467,8 +466,7 @@ type Table
|
||||
remove_blank_columns : Blank_Selector -> Boolean -> Table ! No_Output_Columns
|
||||
remove_blank_columns self (when:Blank_Selector = Blank_Selector.All_Cells) (treat_nans_as_blank : Boolean = False) =
|
||||
new_columns = self.columns_helper.select_blank_columns_helper when treat_nans_as_blank invert_selection=True
|
||||
if new_columns.length == 0 then Error.throw (No_Output_Columns) else
|
||||
Table.new new_columns
|
||||
Table.new new_columns
|
||||
|
||||
## GROUP Standard.Base.Selections
|
||||
ICON select_column
|
||||
@ -2122,18 +2120,22 @@ type Table
|
||||
v : Vector -> [self] + (v.map t-> Table.from t)
|
||||
single_table -> [self, single_table]
|
||||
all_tables.if_not_error <|
|
||||
problem_builder = Problem_Builder.new
|
||||
matched_column_sets = Match_Columns_Helpers.match_columns all_tables match_columns keep_unmatched_columns problem_builder
|
||||
## We keep separate problem builders, because if we are reporting `No_Output_Columns`,
|
||||
we only want to add a cause coming from unification; matching reports problems that would not fit this error.
|
||||
problem_builder_for_matching = Problem_Builder.new
|
||||
problem_builder_for_unification = Problem_Builder.new
|
||||
matched_column_sets = Match_Columns_Helpers.match_columns all_tables match_columns keep_unmatched_columns problem_builder_for_matching
|
||||
result_row_count = all_tables.fold 0 c-> t-> c + t.row_count
|
||||
merged_columns = matched_column_sets.map column_set->
|
||||
case Table_Helpers.unify_result_type_for_union column_set all_tables allow_type_widening problem_builder of
|
||||
case Table_Helpers.unify_result_type_for_union column_set all_tables allow_type_widening problem_builder_for_unification of
|
||||
Nothing -> Nothing
|
||||
result_type : Value_Type ->
|
||||
concat_columns column_set all_tables result_type result_row_count on_problems
|
||||
good_columns = merged_columns.filter Filter_Condition.Not_Nothing
|
||||
if good_columns.is_empty then Error.throw No_Output_Columns else
|
||||
problem_builder.attach_problems_before on_problems <|
|
||||
Table.new good_columns
|
||||
problem_builder_for_matching.attach_problems_before on_problems <|
|
||||
problem_builder_for_unification.attach_problems_before on_problems <|
|
||||
if good_columns.is_empty then problem_builder_for_unification.raise_no_output_columns_with_cause else
|
||||
Table.new good_columns
|
||||
|
||||
## ALIAS drop_missing_rows, dropna
|
||||
GROUP Standard.Base.Selections
|
||||
|
@ -99,12 +99,16 @@ type Duplicate_Output_Column_Names
|
||||
|
||||
## No columns in the output result.
|
||||
type No_Output_Columns
|
||||
## PRIVATE
|
||||
Error (cause : Any | Nothing = Nothing)
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Pretty prints the no output columns error.
|
||||
to_display_text : Text
|
||||
to_display_text self =
|
||||
"The result contains no columns."
|
||||
if self.cause.is_nothing then "The result would contain no columns." else
|
||||
"No columns in the result, because of another problem: "+self.cause.to_display_text
|
||||
|
||||
## Indicates that one column has been matched by multiple selectors, resulting
|
||||
in ambiguous new names.
|
||||
|
@ -90,7 +90,7 @@ prepare_aggregate_columns naming_helper aggregates table error_on_missing_column
|
||||
## If none were found, we raise a generic error (this may
|
||||
happen primarily when an empty list is provided to the
|
||||
aggregate method).
|
||||
Error.throw No_Output_Columns
|
||||
Error.throw No_Output_Columns.Error
|
||||
False ->
|
||||
problem_builder.report_unique_name_strategy unique
|
||||
Validated_Aggregate_Columns.Value unique_key_columns renamed_columns problem_builder.get_problemset_throwing_distinguished_errors
|
||||
|
@ -2,7 +2,7 @@ from Standard.Base import all
|
||||
import Standard.Base.Runtime.Ref.Ref
|
||||
|
||||
import project.Internal.Vector_Builder.Vector_Builder
|
||||
from project.Errors import Duplicate_Output_Column_Names, Invalid_Aggregate_Column, Invalid_Column_Names, Missing_Input_Columns, Truncated_Column_Names
|
||||
from project.Errors import Duplicate_Output_Column_Names, Invalid_Aggregate_Column, Invalid_Column_Names, Missing_Input_Columns, Truncated_Column_Names, No_Output_Columns
|
||||
|
||||
## PRIVATE
|
||||
type Problem_Builder
|
||||
@ -78,6 +78,14 @@ type Problem_Builder
|
||||
Nothing -> problems
|
||||
problem -> Error.throw problem
|
||||
|
||||
## PRIVATE
|
||||
Raises `No_Output_Columns` error, with cause set to the first problem
|
||||
from this builder (if any were reported).
|
||||
raise_no_output_columns_with_cause : Any ! No_Output_Columns
|
||||
raise_no_output_columns_with_cause self =
|
||||
first_problem = self.build_problemset.get 0 if_missing=Nothing
|
||||
Error.throw (No_Output_Columns.Error first_problem)
|
||||
|
||||
## PRIVATE
|
||||
Creates a new helper object for aggregating problems to report.
|
||||
|
||||
|
@ -67,7 +67,7 @@ type Table_Column_Helper
|
||||
problem_builder = Problem_Builder.new error_on_missing_columns=error_on_missing_columns
|
||||
result = self.select_columns_helper selectors case_sensitivity reorder problem_builder
|
||||
problem_builder.attach_problems_before on_problems <|
|
||||
if error_on_empty && result.is_empty then Error.throw No_Output_Columns else result
|
||||
if error_on_empty && result.is_empty then problem_builder.raise_no_output_columns_with_cause else result
|
||||
|
||||
## PRIVATE
|
||||
A helper function encapsulating shared code for `remove_columns`
|
||||
@ -97,7 +97,7 @@ type Table_Column_Helper
|
||||
result = self.internal_columns.filter column->
|
||||
should_be_removed = selected_names.get column.name False
|
||||
should_be_removed.not
|
||||
if result.is_empty then Error.throw No_Output_Columns else
|
||||
if result.is_empty then Error.throw No_Output_Columns.Error else
|
||||
problem_builder.attach_problems_after on_problems result
|
||||
|
||||
## PRIVATE
|
||||
@ -236,7 +236,7 @@ type Table_Column_Helper
|
||||
Panic.throw (Illegal_State.Error message=msg cause=error)
|
||||
|
||||
counts = materialized_result.rows.first
|
||||
self.internal_columns.filter_with_index i-> _->
|
||||
new_columns = self.internal_columns.filter_with_index i-> _->
|
||||
include = case counts.at i of
|
||||
## No rows in input, so treating as blank by convention.
|
||||
Nothing -> True
|
||||
@ -245,6 +245,9 @@ type Table_Column_Helper
|
||||
unexpected ->
|
||||
Panic.throw (Illegal_State.Error "Unexpected result: "+unexpected.to_display_text+". Perhaps an implementation bug of `select_blank_columns_helper`.")
|
||||
if invert_selection then include.not else include
|
||||
if new_columns.is_empty.not then new_columns else
|
||||
message = if invert_selection then "All columns were blank." else "No columns were blank."
|
||||
Error.throw (No_Output_Columns.Error message)
|
||||
|
||||
## PRIVATE
|
||||
A helper function encapsulating shared code for `rename_columns`
|
||||
|
@ -1272,6 +1272,14 @@ spec setup =
|
||||
problems = [Unsupported_Database_Operation.Error "`First` aggregation requires at least one `order_by` column.", Unsupported_Database_Operation.Error "`Last` aggregation requires at least one `order_by` column."]
|
||||
Problems.test_problem_handling action problems tester
|
||||
|
||||
Test.specify "will include unsupported feature problem in No_Output_Columns" <|
|
||||
table = table_builder [["A", [1,2,Nothing,3]]]
|
||||
r1 = table.aggregate [First "A"]
|
||||
r1.should_fail_with No_Output_Columns
|
||||
r1.catch.cause . should_be_a Unsupported_Database_Operation
|
||||
r1.to_display_text . should_contain "No columns in the result"
|
||||
r1.to_display_text . should_contain "`First`"
|
||||
|
||||
Test.group prefix+"Table.aggregate+Expressions" <|
|
||||
## TODO we probably should check all kinds of aggregate columns
|
||||
to verify that all of them correctly support expressions.
|
||||
@ -1317,11 +1325,11 @@ spec setup =
|
||||
t1.should_fail_with No_Output_Columns
|
||||
|
||||
t2 = table.aggregate [Sum "MISSING"]
|
||||
t2 . should_fail_with Invalid_Aggregate_Column
|
||||
t2.should_fail_with Invalid_Aggregate_Column
|
||||
t2.catch.name.should_equal "MISSING"
|
||||
|
||||
t3 = table.aggregate [Sum 42]
|
||||
t3 . should_fail_with Missing_Input_Columns
|
||||
t3.should_fail_with Missing_Input_Columns
|
||||
t3.catch.criteria.should_equal [42]
|
||||
|
||||
Test.specify "should raise a warning when can't find a column by name, but a hard error if the missing column is in a Group_By" <|
|
||||
|
@ -93,6 +93,7 @@ spec setup =
|
||||
|
||||
t4 = t1.union [t2, t3] keep_unmatched_columns=False on_problems=Problem_Behavior.Ignore
|
||||
t4.should_fail_with No_Output_Columns
|
||||
t4.catch.to_display_text . should_equal "No columns in the result, because of another problem: Unmatched columns are set to be dropped, but no common column names were found."
|
||||
|
||||
Test.specify "should ignore column names when matching by position" <|
|
||||
t1 = table_builder [["A", [1, 2, 3]], ["Y", ["a", "b", "c"]]]
|
||||
@ -389,12 +390,13 @@ spec setup =
|
||||
t12.at "Y" . to_vector . should_equal ['aa', 'bb', 'cc', 'x', 'y', 'z']
|
||||
|
||||
Test.specify "should fail to find a common type if widening is not allowed (2)" <|
|
||||
# ToDo No_Output_Columns should have Column_Type_Mismatch as its cause for better readability (#7635)
|
||||
t1.union t2 allow_type_widening=False . should_fail_with No_Output_Columns
|
||||
r1 = t1.union t2 allow_type_widening=False
|
||||
r1.should_fail_with No_Output_Columns
|
||||
r1.catch.cause . should_be_a Column_Type_Mismatch
|
||||
r1.catch.to_display_text . should_equal "No columns in the result, because of another problem: The column [X] expects type Integer (16 bits) but one of the provided tables had type Integer (32 bits) which is not compatible with it."
|
||||
|
||||
# And this should report Column_Type_Mismatch as the more important error too.
|
||||
#t1.union t2 allow_type_widening=False on_problems=Problem_Behavior.Report_Error . should_fail_with Column_Type_Mismatch
|
||||
t1.union t2 allow_type_widening=False on_problems=Problem_Behavior.Report_Error . should_fail_with No_Output_Columns
|
||||
# And this should report Column_Type_Mismatch as the more important error too:
|
||||
t1.union t2 allow_type_widening=False on_problems=Problem_Behavior.Report_Error . should_fail_with Column_Type_Mismatch
|
||||
|
||||
Test.specify "should gracefully handle tables from different backends" <|
|
||||
t1 = table_builder [["A", [1, 2, 3]], ["B", ["a", "b", "c"]]]
|
||||
|
@ -152,7 +152,13 @@ spec setup =
|
||||
no_rows.at "X" . to_vector . should_equal []
|
||||
|
||||
no_rows.select_blank_columns . columns . map .name . should_equal ["X"]
|
||||
no_rows.remove_blank_columns . columns . map .name . should_fail_with No_Output_Columns
|
||||
r2 = no_rows.remove_blank_columns
|
||||
r2.should_fail_with No_Output_Columns
|
||||
r2.catch.to_display_text . should_equal "No columns in the result, because of another problem: All columns were blank."
|
||||
|
||||
r3 = t.select_blank_columns
|
||||
r3.should_fail_with No_Output_Columns
|
||||
r3.catch.to_display_text . should_equal "No columns in the result, because of another problem: No columns were blank."
|
||||
|
||||
Test.group prefix+"Filling Missing Values" <|
|
||||
Test.specify "should coerce long and double types to double" <|
|
||||
|
@ -131,9 +131,15 @@ spec setup =
|
||||
[Problem_Behavior.Ignore, Problem_Behavior.Report_Warning, Problem_Behavior.Report_Error].each pb->
|
||||
t = table.select_columns [] on_problems=pb
|
||||
t.should_fail_with No_Output_Columns
|
||||
# Just selecting [] means the No_Output_Columns does not have an additional cause.
|
||||
t.catch.cause . should_equal Nothing
|
||||
t.catch.to_display_text . should_equal "The result would contain no columns."
|
||||
|
||||
table.select_columns ["hmmm"] . should_fail_with Missing_Input_Columns
|
||||
table.select_columns ["hmmm"] error_on_missing_columns=False . should_fail_with No_Output_Columns
|
||||
r2 = table.select_columns ["hmmm"] error_on_missing_columns=False
|
||||
r2.should_fail_with No_Output_Columns
|
||||
r2.catch.cause . should_be_a Missing_Input_Columns
|
||||
r2.catch.to_display_text . should_equal "No columns in the result, because of another problem: The criteria 'hmmm' did not match any columns."
|
||||
|
||||
Test.group prefix+"Table.remove_columns" <|
|
||||
Test.specify "should work as shown in the doc examples" <|
|
||||
@ -215,6 +221,8 @@ spec setup =
|
||||
selector_2 = [".*".to_regex, "hmmm".to_regex]
|
||||
t1 = table.remove_columns selector_2
|
||||
t1.should_fail_with No_Output_Columns
|
||||
# No cause specified - even if some criteria were unmatched, that is not the reason for the No_Output_Columns (the reason is all other columns got deleted, by other criteria that _did_ match).
|
||||
t1.catch.cause . should_equal Nothing
|
||||
|
||||
Test.group prefix+"Table.reorder_columns" <|
|
||||
Test.specify "should work as shown in the doc examples" <|
|
||||
|
Loading…
Reference in New Issue
Block a user