sql-error-categorizer 0.2.3__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/.gitignore +1 -1
  2. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/Makefile +3 -4
  3. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/PKG-INFO +3 -3
  4. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/pyproject.toml +3 -3
  5. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/requirements.txt +2 -2
  6. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/src/sql_error_categorizer/detectors/complications.py +68 -63
  7. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/src/sql_error_categorizer/detectors/logical.py +291 -199
  8. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/src/sql_error_categorizer/detectors/semantic.py +25 -197
  9. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/src/sql_error_categorizer/detectors/syntax.py +116 -251
  10. sql_error_categorizer-0.2.3/tests/1_syn/test_002_ambiguous_column.py → sql_error_categorizer-0.3.0/tests/1_syn/test_001_ambiguous_column.py +2 -1
  11. sql_error_categorizer-0.2.3/tests/1_syn/test_004_undefined_column.py → sql_error_categorizer-0.3.0/tests/1_syn/test_003_undefined_column.py +1 -1
  12. sql_error_categorizer-0.2.3/tests/1_syn/test_005_undefined_function.py → sql_error_categorizer-0.3.0/tests/1_syn/test_004_undefined_function.py +1 -1
  13. sql_error_categorizer-0.2.3/tests/1_syn/test_006_undefined_parameter.py → sql_error_categorizer-0.3.0/tests/1_syn/test_005_undefined_parameter.py +1 -1
  14. sql_error_categorizer-0.2.3/tests/1_syn/test_007_undefined_tables.py → sql_error_categorizer-0.3.0/tests/1_syn/test_006_undefined_object.py +1 -1
  15. sql_error_categorizer-0.2.3/tests/1_syn/test_008_invalid_schema_names.py → sql_error_categorizer-0.3.0/tests/1_syn/test_007_invalid_schema_names.py +1 -1
  16. sql_error_categorizer-0.2.3/tests/1_syn/test_009_misspellings.py → sql_error_categorizer-0.3.0/tests/1_syn/test_008_misspellings.py +1 -1
  17. sql_error_categorizer-0.2.3/tests/1_syn/test_035_is_where_not_applicable.py → sql_error_categorizer-0.3.0/tests/1_syn/test_012_is_where_not_applicable.py +1 -1
  18. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/1_syn/test_013_data_type_mismatch.py +1 -1
  19. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/1_syn/test_014_aggregate_function_outside_select_or_having.py +1 -1
  20. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/1_syn/test_015_nested_aggregate_functions.py +1 -1
  21. sql_error_categorizer-0.3.0/tests/1_syn/test_016_extraneous_omitted_grouping_column.py +47 -0
  22. sql_error_categorizer-0.3.0/tests/1_syn/test_017_having_without_group_by.py +59 -0
  23. sql_error_categorizer-0.3.0/tests/1_syn/test_018_too_many_columns_in_subquery.py +42 -0
  24. sql_error_categorizer-0.3.0/tests/1_syn/test_021_using_where_twice.py +53 -0
  25. sql_error_categorizer-0.3.0/tests/1_syn/test_022_omitted_from.py +51 -0
  26. sql_error_categorizer-0.3.0/tests/1_syn/test_024_036_additional_omitted_semicolons.py +60 -0
  27. sql_error_categorizer-0.3.0/tests/1_syn/test_024_comparison_with_null.py +40 -0
  28. sql_error_categorizer-0.3.0/tests/1_syn/test_026_duplicate_clause.py +43 -0
  29. sql_error_categorizer-0.3.0/tests/1_syn/test_029_keywords_order.py +36 -0
  30. sql_error_categorizer-0.3.0/tests/1_syn/test_032_033_curly_square_or_unmatched_brackets.py +68 -0
  31. sql_error_categorizer-0.3.0/tests/1_syn/test_035_nonstandard_operators.py +31 -0
  32. sql_error_categorizer-0.2.3/tests/2_sem/test_040_tautological_inconsistent_expressions.py → sql_error_categorizer-0.3.0/tests/2_sem/test_038_tautological_inconsistent_expressions.py +1 -1
  33. sql_error_categorizer-0.3.0/tests/2_sem/test_039_distinct_sum_avg.py +51 -0
  34. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/3_log/test_058_join_on_incorrect_table.py +3 -3
  35. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/3_log/test_059_join_when_join_needs_to_be_omitted.py +3 -3
  36. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/3_log/test_062_missing_join.py +3 -3
  37. sql_error_categorizer-0.3.0/tests/3_log/test_067_wildcards_without_like.py +60 -0
  38. sql_error_categorizer-0.3.0/tests/3_log/test_068_069_wrong_invalid_wildcard.py +85 -0
  39. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/3_log/test_070_extraneous_column_in_select.py +14 -2
  40. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/3_log/test_071_missing_column_from_select.py +14 -2
  41. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/3_log/test_072_missing_distinct_from_select.py +2 -2
  42. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/3_log/test_073_missing_as_from_select.py +2 -2
  43. sql_error_categorizer-0.2.3/tests/4_com/test_083_unnecessary_distinct_in_select.py → sql_error_categorizer-0.3.0/tests/4_com/test_096_unnecessary_distinct_in_select.py +15 -6
  44. sql_error_categorizer-0.3.0/tests/4_com/test_102_like_without_wildcards.py +34 -0
  45. sql_error_categorizer-0.2.3/tests/4_com/test_092_unnecessary_distinct_in_aggregate_function.py → sql_error_categorizer-0.3.0/tests/4_com/test_106_unnecessary_distinct_in_aggregate_function.py +4 -4
  46. sql_error_categorizer-0.2.3/tests/4_com/test_095_group_by_with_singleton_groups.py → sql_error_categorizer-0.3.0/tests/4_com/test_109_group_by_with_singleton_groups.py +3 -3
  47. sql_error_categorizer-0.2.3/tests/4_com/test_097_group_by_can_be_replaced_by_distinct.py → sql_error_categorizer-0.3.0/tests/4_com/test_111_group_by_can_be_replaced_by_distinct.py +3 -3
  48. sql_error_categorizer-0.3.0/tests/4_com/test_114_order_by_in_subquery.py +42 -0
  49. sql_error_categorizer-0.2.3/tests/1_syn/test_016_extraneous_omitted_grouping_column.py +0 -81
  50. sql_error_categorizer-0.2.3/tests/1_syn/test_017_having_without_group_by.py +0 -80
  51. sql_error_categorizer-0.2.3/tests/1_syn/test_019_using_where_twice.py +0 -60
  52. sql_error_categorizer-0.2.3/tests/1_syn/test_020_missing_from.py +0 -76
  53. sql_error_categorizer-0.2.3/tests/1_syn/test_021_comparison_with_null.py +0 -43
  54. sql_error_categorizer-0.2.3/tests/1_syn/test_022_038_additional_omitted_semicolons.py +0 -56
  55. sql_error_categorizer-0.2.3/tests/1_syn/test_024_duplicate_clause.py +0 -71
  56. sql_error_categorizer-0.2.3/tests/1_syn/test_026_too_many_columns_in_subquery.py +0 -70
  57. sql_error_categorizer-0.2.3/tests/1_syn/test_030_keywords_order.py +0 -37
  58. sql_error_categorizer-0.2.3/tests/1_syn/test_034_curly_square_or_unmatched_brackets.py +0 -59
  59. sql_error_categorizer-0.2.3/tests/1_syn/test_037_nonstandard_operators.py +0 -29
  60. sql_error_categorizer-0.2.3/tests/2_sem/test_041_distinct_sum_avg.py +0 -77
  61. sql_error_categorizer-0.2.3/tests/2_sem/test_043_wildcards_without_like.py +0 -104
  62. sql_error_categorizer-0.2.3/tests/2_sem/test_044_incorrect_wildcards.py +0 -143
  63. sql_error_categorizer-0.2.3/tests/4_com/test_088_like_no_wildcards.py +0 -32
  64. sql_error_categorizer-0.2.3/tests/4_com/test_100_order_by_in_subquery.py +0 -55
  65. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/.readthedocs.yaml +0 -0
  66. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/LICENSE +0 -0
  67. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/README.md +0 -0
  68. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/datasets/catalogs/constraints.json +0 -0
  69. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/datasets/catalogs/miedema.json +0 -0
  70. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/datasets/sql/constraints.sql +0 -0
  71. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/datasets/sql/miedema.sql +0 -0
  72. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/docs/Makefile +0 -0
  73. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/docs/conf.py +0 -0
  74. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/docs/index.rst +0 -0
  75. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/docs/make.bat +0 -0
  76. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/docs/requirements.txt +0 -0
  77. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/src/sql_error_categorizer/__init__.py +0 -0
  78. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/src/sql_error_categorizer/detectors/__init__.py +0 -0
  79. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/src/sql_error_categorizer/detectors/base.py +0 -0
  80. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/test_detector.py +0 -0
  81. {sql_error_categorizer-0.2.3 → sql_error_categorizer-0.3.0}/tests/__init__.py +0 -0
@@ -1,7 +1,7 @@
1
1
  # CUSTOM
2
2
  test_q.sql
3
3
  test_s?.sql
4
-
4
+ .codex
5
5
 
6
6
  # Byte-compiled / optimized / DLL files
7
7
  __pycache__/
@@ -14,6 +14,9 @@ endif
14
14
 
15
15
  .PHONY: install build uninstall documentation test upload download clean coverage
16
16
 
17
+ install: uninstall build
18
+ $(VENV_BIN)/python -m pip install ./dist/*.whl
19
+
17
20
  $(VENV):
18
21
  python -m venv --clear $(VENV)
19
22
  touch -a $(REQUIREMENTS)
@@ -22,10 +25,6 @@ $(VENV):
22
25
  $(VENV)_upgrade: $(VENV)
23
26
  $(VENV_BIN)/python -m pip install --upgrade -r $(REQUIREMENTS)
24
27
 
25
-
26
- install: uninstall build
27
- $(VENV_BIN)/python -m pip install ./dist/*.whl
28
-
29
28
  build: $(VENV)
30
29
  rm -rf dist/
31
30
  $(VENV_BIN)/python -m build
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql_error_categorizer
3
- Version: 0.2.3
3
+ Version: 0.3.0
4
4
  Summary: This project analyses SQL statements and labels possible errors or complications.
5
5
  Project-URL: Repository, https://github.com/DavidePonzini/sql_error_categorizer
6
6
  Project-URL: Documentation, https://sql-error-categorizer.readthedocs.io/en/latest/index.html
@@ -14,10 +14,10 @@ Requires-Python: >=3.11
14
14
  Requires-Dist: psycopg2
15
15
  Requires-Dist: python-dateutil
16
16
  Requires-Dist: pyyaml
17
- Requires-Dist: sql-error-taxonomy
17
+ Requires-Dist: sql-error-taxonomy>=1.1.2
18
18
  Requires-Dist: sqlglot
19
19
  Requires-Dist: sqlparse
20
- Requires-Dist: sqlscope>=1.0.14
20
+ Requires-Dist: sqlscope>=1.0.15
21
21
  Requires-Dist: z3-solver
22
22
  Description-Content-Type: text/markdown
23
23
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sql_error_categorizer"
7
- version = "0.2.3"
7
+ version = "0.3.0"
8
8
  authors = [
9
9
  { name="Davide Ponzini", email="davide.ponzini95@gmail.com" },
10
10
  ]
@@ -21,8 +21,8 @@ dependencies = [
21
21
  "pyyaml",
22
22
  "sqlparse",
23
23
  "sqlglot",
24
- "sqlscope>=1.0.14",
25
- "sql_error_taxonomy",
24
+ "sqlscope>=1.0.15",
25
+ "sql_error_taxonomy>=1.1.2",
26
26
  "z3-solver",
27
27
  "python-dateutil",
28
28
  ]
@@ -4,8 +4,8 @@ sqlparse
4
4
  sqlglot==28.0.0
5
5
  z3-solver
6
6
  python-dateutil
7
- sql_error_taxonomy>=1.0.1
8
- sqlscope>=1.0.14
7
+ sql_error_taxonomy>=1.1.2
8
+ sqlscope>=1.0.15
9
9
 
10
10
  # Dependencies for development
11
11
  ipython
@@ -1,14 +1,10 @@
1
1
  '''Detector for complications in SQL queries.'''
2
2
 
3
- import difflib
4
- import re
5
- import sqlparse
6
- import sqlparse.keywords
7
3
  from typing import Callable
8
4
  from sqlglot import exp
9
5
  from sql_error_taxonomy import SqlErrors
10
- from sqlscope.catalog import ConstraintType, ConstraintColumn, Constraint
11
- from sqlscope import Query, Catalog
6
+ from sqlscope.catalog import ConstraintType, ConstraintColumn
7
+ from sqlscope import Query
12
8
  from sqlscope import util
13
9
 
14
10
  from .base import BaseDetector, DetectedError
@@ -36,29 +32,31 @@ class ComplicationDetector(BaseDetector):
36
32
  results: list[DetectedError] = super().run()
37
33
 
38
34
  checks = [
39
- self.com_83_unnecessary_distinct_in_select_clause,
40
- self.com_84_unnecessary_join,
41
- self.com_85_unused_correlation_name,
42
- self.com_86_correlation_names_are_always_identical,
43
- self.com_87_unnecessary_general_comparison_operator,
44
- self.com_88_like_without_wildcards,
45
- self.com_89_unnecessarily_complicated_select_in_exists_subquery,
46
- self.com_90_in_exists_can_be_replaced_by_comparison,
47
- self.com_91_unnecessary_aggregate_function,
48
- self.com_92_unnecessary_distinct_in_aggregate_function,
49
- self.com_93_unnecessary_argument_of_count,
50
- self.com_94_unnecessary_group_by_in_exists_subquery,
51
- self.com_95_group_by_with_singleton_groups,
52
- self.com_96_group_by_with_only_a_single_group,
53
- self.com_97_group_by_can_be_replaced_by_distinct,
54
- self.com_98_union_can_be_replaced_by_or,
55
- self.com_99_complication_unnecessary_column_in_order_by_clause,
56
- self.com_100_order_by_in_subquery,
57
- self.com_101_inefficient_having,
58
- self.com_102_inefficient_union,
59
- self.com_103_condition_in_the_subquery_can_be_moved_up,
60
- self.com_104_condition_on_left_table_in_left_outer_join,
61
- self.com_105_outer_join_can_be_replaced_by_inner_join,
35
+ self.detect_95_unnecessary_complication,
36
+ self.detect_96_unnecessary_distinct_in_select_clause,
37
+ self.detect_97_unnecessary_table_reference,
38
+ self.detect_98_unused_correlation_name,
39
+ self.detect_99_tables_have_same_data,
40
+ self.detect_100_correlation_name_identical_to_table_name,
41
+ self.detect_101_unnecessary_general_comparison_operator,
42
+ self.detect_102_like_without_wildcards,
43
+ self.detect_103_unnecessarily_complicated_select_in_exists_subquery,
44
+ self.detect_104_in_exists_can_be_replaced_by_comparison,
45
+ self.detect_105_unnecessary_aggregate_function,
46
+ self.detect_106_unnecessary_distinct_in_aggregate_function,
47
+ self.detect_107_unnecessary_argument_of_count,
48
+ self.detect_108_unnecessary_group_by_in_exists_subquery,
49
+ self.detect_109_group_by_with_singleton_groups,
50
+ self.detect_110_group_by_with_only_a_single_group,
51
+ self.detect_111_group_by_can_be_replaced_by_distinct,
52
+ self.detect_112_union_can_be_replaced_by_or,
53
+ self.detect_113_unnecessary_column_in_order_by_clause,
54
+ self.detect_114_order_by_in_subquery,
55
+ self.detect_115_inefficient_having,
56
+ self.detect_116_inefficient_union,
57
+ self.detect_117_condition_in_the_subquery_can_be_moved_up,
58
+ self.detect_118_outer_join_can_be_replaced_by_inner_join,
59
+ self.detect_119_unused_cte,
62
60
  ]
63
61
 
64
62
  for chk in checks:
@@ -66,7 +64,11 @@ class ComplicationDetector(BaseDetector):
66
64
 
67
65
  return results
68
66
 
69
- def com_83_unnecessary_distinct_in_select_clause(self) -> list[DetectedError]:
67
+ def detect_95_unnecessary_complication(self) -> list[DetectedError]:
68
+ '''NOTE: this is an umbrella term, so it can't be directly detected.'''
69
+ return []
70
+
71
+ def detect_96_unnecessary_distinct_in_select_clause(self) -> list[DetectedError]:
70
72
  '''
71
73
  Flags a SELECT DISTINCT clause that is unnecessary because the selected
72
74
  columns are already unique due to existing constraints.
@@ -81,12 +83,12 @@ class ComplicationDetector(BaseDetector):
81
83
  constraints = [c for c in select.output.unique_constraints if c.constraint_type != ConstraintType.DISTINCT]
82
84
 
83
85
  if len(constraints) > 0:
84
- result.append(DetectedError(SqlErrors.COM_83_UNNECESSARY_DISTINCT_IN_SELECT_CLAUSE, (select.sql,)))
86
+ result.append(DetectedError(SqlErrors.UNNECESSARY_DISTINCT_IN_SELECT_CLAUSE, (select.sql,)))
85
87
 
86
88
  return result
87
89
 
88
90
  # TODO: refactor
89
- def com_84_unnecessary_join(self) -> list[DetectedError]:
91
+ def detect_97_unnecessary_table_reference(self) -> list[DetectedError]:
90
92
  '''
91
93
  Flags a query that joins to a table not present in the correct solution.
92
94
  '''
@@ -110,25 +112,29 @@ class ComplicationDetector(BaseDetector):
110
112
  # Find the original table name (with alias if it was used) to report back
111
113
  original_table_name = next((t for t in original_q_tables if t.lower().startswith(table_name_lower)), table_name_lower)
112
114
  results.append((
113
- SqlErrors.COM_84_UNNECESSARY_JOIN,
115
+ SqlErrors.UNNECESSARY_TABLE_REFERENCE,
114
116
  f"Unnecessary JOIN: The table '{original_table_name}' is not needed to answer the query."
115
117
  ))
116
118
 
117
119
  return results
118
120
 
119
121
  # TODO: implement
120
- def com_85_unused_correlation_name(self) -> list[DetectedError]:
122
+ def detect_98_unused_correlation_name(self) -> list[DetectedError]:
121
123
  return []
122
124
 
123
125
  # TODO: implement
124
- def com_86_correlation_names_are_always_identical(self) -> list[DetectedError]:
126
+ def detect_99_tables_have_same_data(self) -> list[DetectedError]:
127
+ return []
128
+
129
+ # TODO: implement
130
+ def detect_100_correlation_name_identical_to_table_name(self) -> list[DetectedError]:
125
131
  return []
126
132
 
127
133
  # TODO: implement
128
- def com_87_unnecessary_general_comparison_operator(self) -> list[DetectedError]:
134
+ def detect_101_unnecessary_general_comparison_operator(self) -> list[DetectedError]:
129
135
  return []
130
136
 
131
- def com_88_like_without_wildcards(self) -> list[DetectedError]:
137
+ def detect_102_like_without_wildcards(self) -> list[DetectedError]:
132
138
  '''
133
139
  Flags queries where the LIKE operator is used without wildcards ('%' or '_').
134
140
  This indicates a potential misunderstanding, where the '=' operator should
@@ -157,23 +163,23 @@ class ComplicationDetector(BaseDetector):
157
163
  if '%' not in pattern_value and '_' not in pattern_value:
158
164
  full_expression = str(like)
159
165
 
160
- results.append(DetectedError(SqlErrors.COM_88_LIKE_WITHOUT_WILDCARDS, (full_expression,)))
166
+ results.append(DetectedError(SqlErrors.LIKE_WITHOUT_WILDCARDS, (full_expression,)))
161
167
 
162
168
  return results
163
169
 
164
170
  # TODO: implement
165
- def com_89_unnecessarily_complicated_select_in_exists_subquery(self) -> list[DetectedError]:
171
+ def detect_103_unnecessarily_complicated_select_in_exists_subquery(self) -> list[DetectedError]:
166
172
  return []
167
173
 
168
174
  # TODO: implement
169
- def com_90_in_exists_can_be_replaced_by_comparison(self) -> list[DetectedError]:
175
+ def detect_104_in_exists_can_be_replaced_by_comparison(self) -> list[DetectedError]:
170
176
  return []
171
177
 
172
178
  # TODO: implement
173
- def com_91_unnecessary_aggregate_function(self) -> list[DetectedError]:
179
+ def detect_105_unnecessary_aggregate_function(self) -> list[DetectedError]:
174
180
  return []
175
181
 
176
- def com_92_unnecessary_distinct_in_aggregate_function(self) -> list[DetectedError]:
182
+ def detect_106_unnecessary_distinct_in_aggregate_function(self) -> list[DetectedError]:
177
183
  '''MIN and MAX never require DISTINCT. For other aggregate functions, DISTINCT is unnecessary if the argument is unique.'''
178
184
 
179
185
  results: list[DetectedError] = []
@@ -189,7 +195,7 @@ class ComplicationDetector(BaseDetector):
189
195
  continue
190
196
 
191
197
  if isinstance(agg_func, (exp.Min, exp.Max)):
192
- results.append(DetectedError(SqlErrors.COM_92_UNNECESSARY_DISTINCT_IN_AGGREGATE_FUNCTION, (str(agg_func),)))
198
+ results.append(DetectedError(SqlErrors.UNNECESSARY_DISTINCT_IN_AGGREGATE_FUNCTION, (str(agg_func),)))
193
199
  continue
194
200
 
195
201
  arg_expr = agg_func.this.expressions # `.this` is the DISTINCT, `.expressions` are the arguments
@@ -199,7 +205,7 @@ class ComplicationDetector(BaseDetector):
199
205
  for expr in arg_expr:
200
206
  # Check if the argument is a constant literal
201
207
  if isinstance(expr, exp.Literal):
202
- results.append(DetectedError(SqlErrors.COM_92_UNNECESSARY_DISTINCT_IN_AGGREGATE_FUNCTION, (str(agg_func),)))
208
+ results.append(DetectedError(SqlErrors.UNNECESSARY_DISTINCT_IN_AGGREGATE_FUNCTION, (str(agg_func),)))
203
209
  continue
204
210
 
205
211
  # Check if the argument is a column
@@ -210,18 +216,18 @@ class ComplicationDetector(BaseDetector):
210
216
  unique_constraints = [c for c in select.all_constraints if c.constraint_type == ConstraintType.UNIQUE]
211
217
  for constraint in unique_constraints:
212
218
  if { ConstraintColumn(column_name, table_idx=select._get_table_idx_for_column(expr)) } == constraint.columns:
213
- results.append(DetectedError(SqlErrors.COM_92_UNNECESSARY_DISTINCT_IN_AGGREGATE_FUNCTION, (str(agg_func),)))
219
+ results.append(DetectedError(SqlErrors.UNNECESSARY_DISTINCT_IN_AGGREGATE_FUNCTION, (str(agg_func),)))
214
220
  break
215
221
  return results
216
222
 
217
- def com_93_unnecessary_argument_of_count(self) -> list[DetectedError]:
223
+ def detect_107_unnecessary_argument_of_count(self) -> list[DetectedError]:
218
224
  return []
219
225
 
220
226
  # TODO: implement
221
- def com_94_unnecessary_group_by_in_exists_subquery(self) -> list[DetectedError]:
227
+ def detect_108_unnecessary_group_by_in_exists_subquery(self) -> list[DetectedError]:
222
228
  return []
223
229
 
224
- def com_95_group_by_with_singleton_groups(self) -> list[DetectedError]:
230
+ def detect_109_group_by_with_singleton_groups(self) -> list[DetectedError]:
225
231
  '''
226
232
  Flags GROUP BY clauses on singleton groups due to the presence
227
233
  of UNIQUE constraints on the grouped columns.
@@ -241,16 +247,16 @@ class ComplicationDetector(BaseDetector):
241
247
 
242
248
  for constraint in constraints:
243
249
  if constraint.columns.issubset(group_by_constraint.columns):
244
- results.append(DetectedError(SqlErrors.COM_95_GROUP_BY_WITH_SINGLETON_GROUPS, (group_by_constraint, constraint)))
250
+ results.append(DetectedError(SqlErrors.GROUP_BY_WITH_SINGLETON_GROUPS, (group_by_constraint, constraint)))
245
251
  break
246
252
 
247
253
  return results
248
254
 
249
255
  # TODO: implement
250
- def com_96_group_by_with_only_a_single_group(self) -> list[DetectedError]:
256
+ def detect_110_group_by_with_only_a_single_group(self) -> list[DetectedError]:
251
257
  return []
252
258
 
253
- def com_97_group_by_can_be_replaced_by_distinct(self) -> list[DetectedError]:
259
+ def detect_111_group_by_can_be_replaced_by_distinct(self) -> list[DetectedError]:
254
260
  '''
255
261
  Flags GROUP BY clauses that can be replaced by SELECT DISTINCT.
256
262
  This occurs when all selected columns are included in the GROUP BY clause
@@ -290,18 +296,18 @@ class ComplicationDetector(BaseDetector):
290
296
  group_by_col_names = {(util.ast.column.get_real_name(col), select._get_table_idx_for_column(col)) for col in group_by_columns}
291
297
 
292
298
  if select_col_names == group_by_col_names:
293
- results.append(DetectedError(SqlErrors.COM_97_GROUP_BY_CAN_BE_REPLACED_WITH_DISTINCT, (select_col_names,)))
299
+ results.append(DetectedError(SqlErrors.GROUP_BY_CAN_BE_REPLACED_WITH_DISTINCT, (select_col_names,)))
294
300
 
295
301
  return results
296
302
 
297
303
 
298
304
 
299
305
  # TODO: implement
300
- def com_98_union_can_be_replaced_by_or(self) -> list[DetectedError]:
306
+ def detect_112_union_can_be_replaced_by_or(self) -> list[DetectedError]:
301
307
  return []
302
308
 
303
309
  # TODO: refactor
304
- def com_99_complication_unnecessary_column_in_order_by_clause(self) -> list[DetectedError]:
310
+ def detect_113_unnecessary_column_in_order_by_clause(self) -> list[DetectedError]:
305
311
  '''
306
312
  Flags when the ORDER BY clause contains unnecessary columns in addition
307
313
  to the required ones.
@@ -330,7 +336,7 @@ class ComplicationDetector(BaseDetector):
330
336
  return results
331
337
 
332
338
  # TODO: implement
333
- def com_100_order_by_in_subquery(self) -> list[DetectedError]:
339
+ def detect_114_order_by_in_subquery(self) -> list[DetectedError]:
334
340
  '''
335
341
  Flags when a subquery contains an ORDER BY clause.
336
342
  Subqueries both ORDER BY and LIMIT are considered valid.
@@ -348,31 +354,30 @@ class ComplicationDetector(BaseDetector):
348
354
 
349
355
  checked_subqueries.add(subquery.sql)
350
356
  if subquery.order_by and not subquery.limit:
351
- results.append(DetectedError(SqlErrors.COM_100_ORDER_BY_IN_SUBQUERY, (subquery.sql,)))
357
+ results.append(DetectedError(SqlErrors.ORDER_BY_IN_SUBQUERY, (subquery.sql,)))
352
358
 
353
359
  return results
354
360
 
355
361
  # TODO: implement
356
- def com_101_inefficient_having(self) -> list[DetectedError]:
362
+ def detect_115_inefficient_having(self) -> list[DetectedError]:
357
363
  return []
358
364
 
359
365
  # TODO: implement
360
- def com_102_inefficient_union(self) -> list[DetectedError]:
366
+ def detect_116_inefficient_union(self) -> list[DetectedError]:
361
367
  return []
362
368
 
363
369
  # TODO: implement
364
- def com_103_condition_in_the_subquery_can_be_moved_up(self) -> list[DetectedError]:
370
+ def detect_117_condition_in_the_subquery_can_be_moved_up(self) -> list[DetectedError]:
365
371
  return []
366
372
 
367
373
  # TODO: implement
368
- def com_104_condition_on_left_table_in_left_outer_join(self) -> list[DetectedError]:
374
+ def detect_118_outer_join_can_be_replaced_by_inner_join(self) -> list[DetectedError]:
369
375
  return []
370
-
376
+
371
377
  # TODO: implement
372
- def com_105_outer_join_can_be_replaced_by_inner_join(self) -> list[DetectedError]:
378
+ def detect_119_unused_cte(self) -> list[DetectedError]:
373
379
  return []
374
380
 
375
-
376
381
  #region Utility methods
377
382
  def _get_select_columns(self, ast: dict) -> list:
378
383
  '''