Repository: mabel-dev/opteryx

Issue:
🪲 binder unit test not refactored
### Thank you for taking the time to report a problem with Opteryx.
_To help us to respond to your request we ask that you try to provide the below detail about the bug._

**Describe the bug** _A clear and specific description of what the bug is. What the error, incorrect or unexpected behaviour was._


**Expected behaviour** _A clear and concise description of what you expected to happen._


**Sample Code/Statement** _If you can, please submit the SQL statement or Python code snippet, or a representative example using the sample datasets._

~~~sql

~~~

**Additional context** _Add any other context about the problem here, for example what you have done to try to diagnose or workaround the problem._



Discussion:

Resolving patch:
diff --git a/opteryx/components/binder/binder_visitor.py b/opteryx/components/binder/binder_visitor.py
--- a/opteryx/components/binder/binder_visitor.py
+++ b/opteryx/components/binder/binder_visitor.py
@@ -26,6 +26,7 @@
 from opteryx.components.binder.binding_context import BindingContext
 from opteryx.exceptions import AmbiguousDatasetError
 from opteryx.exceptions import ColumnNotFoundError
+from opteryx.exceptions import InvalidFunctionParameterError
 from opteryx.exceptions import InvalidInternalStateError
 from opteryx.exceptions import UnsupportedSyntaxError
 from opteryx.managers.expression import NodeType
@@ -464,7 +465,12 @@ def visit_function_dataset(
                     )
                 node.columns = columns
             else:
-                column_definition = int(column_definition)
+                try:
+                    column_definition = int(column_definition)
+                except TypeError:
+                    raise InvalidFunctionParameterError(
+                        f"Expected number of rows for 'FAKE' function or list of column types. Are you missing parenthesis?"
+                    )
                 names = node.columns + tuple(
                     f"column_{i}" for i in range(len(node.columns), column_definition)
                 )
diff --git a/opteryx/operators/scanner_node.py b/opteryx/operators/scanner_node.py
--- a/opteryx/operators/scanner_node.py
+++ b/opteryx/operators/scanner_node.py
@@ -88,3 +88,5 @@ def execute(self) -> Iterable:
             self.execution_time += time.monotonic_ns() - start_clock
             yield normalize_morsel(schema, morsel)
             start_clock = time.monotonic_ns()
+        if morsel:
+            self.statistics.columns_read += morsel.num_columns


Test patch:
diff --git a/tests/query_execution/test_count_star.py b/tests/query_execution/test_count_star.py
--- a/tests/query_execution/test_count_star.py
+++ b/tests/query_execution/test_count_star.py
@@ -19,8 +19,8 @@ def test_count_star():
     cur.execute("SELECT count(*) FROM testdata.flat.formats.parquet WITH(NO_PARTITION);")
     cur.arrow()
     stats = cur.stats
-    assert stats["columns_read"] == 1, stats["columns_read"]
-    assert stats["rows_read"] == 100000
+    assert stats["columns_read"] == 13, stats["columns_read"]
+    assert stats["rows_read"] == 100000, stats["rows_read"]
     conn.close()
 
     conn = opteryx.connect()
@@ -28,12 +28,13 @@ def test_count_star():
     cur.execute("SELECT COUNT(*) FROM testdata.flat.formats.parquet WITH(NO_PARTITION);")
     cur.arrow()
     stats = cur.stats
-    assert stats["columns_read"] == 1
-    assert stats["rows_read"] == 100000
+    assert stats["columns_read"] == 13, stats["columns_read"]
+    assert stats["rows_read"] == 100000, stats["rows_read"]
     conn.close()
 
 
 if __name__ == "__main__":  # pragma: no cover
     from tests.tools import run_tests
 
+    test_count_star()
     run_tests()
diff --git a/tests/query_execution/test_show_functions.py b/tests/query_execution/test_show_functions.py
--- a/tests/query_execution/test_show_functions.py
+++ b/tests/query_execution/test_show_functions.py
@@ -4,22 +4,25 @@
 """
 import os
 import sys
+import pytest
 
 sys.path.insert(1, os.path.join(sys.path[0], "../.."))
 
 
 def test_show_functions():
     import opteryx
-
-    conn = opteryx.connect()
-    cur = conn.cursor()
-    cur.execute("SHOW FUNCTIONS")
-    rows = cur.fetchall()
-
-    # below here is not in the documentation
-    rows = list(rows)
-    assert len(rows) > 85, len(rows)
-    conn.close()
+    from opteryx.exceptions import UnsupportedSyntaxError
+
+    with pytest.raises(UnsupportedSyntaxError):
+        conn = opteryx.connect()
+        cur = conn.cursor()
+        cur.execute("SHOW FUNCTIONS")
+        rows = cur.fetchall()
+
+        # below here is not in the documentation
+        rows = list(rows)
+        assert len(rows) > 85, len(rows)
+        conn.close()
 
 
 if __name__ == "__main__":  # pragma: no cover
diff --git a/tests/query_planner/test_binder_visitor.py b/tests/query_planner/test_binder_visitor.py
--- a/tests/query_planner/test_binder_visitor.py
+++ b/tests/query_planner/test_binder_visitor.py
@@ -7,7 +7,7 @@
 
 sys.path.insert(1, os.path.join(sys.path[0], "../.."))
 
-from opteryx.components.binder.binder_visitor import BinderVisitor
+from opteryx.components.binder.binder_visitor import BinderVisitor, BindingContext
 from opteryx.components.logical_planner import LogicalPlan, LogicalPlanNode, LogicalPlanStepType
 from orso.schema import RelationSchema
 
@@ -44,38 +44,38 @@ def test_logical_plan_visitor():
 
     class TestBinderVisitor(BinderVisitor):
         def visit_scan(self, node, context):
-            context.setdefault("schemas", [])[node.relation] = RelationSchema(name="test")
+            context.schemas[node.relation] = RelationSchema(name="test")
             node.source = node.relation
             return node, context
 
         def visit_filter(self, node, context):
             # the filter has the left scan before it
-            node.sources = context.get("schemas")
+            node.sources = set(context.schemas.keys())
             return node, context
 
         def visit_union(self, node, context):
-            node.sources = context.get("schemas")
+            node.sources = set(context.schemas.keys())
             return node, context
 
         def visit_project(self, node, context):
             # the project has the left and right scans before it
-            node.sources = context.get("schemas")
+            node.sources = set(context.schemas.keys())
             return node, context
 
-    context = {
-        "schemas": {},
-        "cache": None,
-        "connection": None,
-        "relations": set(),
-    }
+    context = BindingContext(
+        schemas={}, qid="12345", connection=None, relations=set(), statistics=None
+    )
 
     visitor = TestBinderVisitor()
-    visitor.traverse(plan, 4, context)
+    bound_plan, bound_context = visitor.traverse(plan, 4, context)
 
-    # this is just on the left branch
-    assert set(filter_node.sources) == {"left"}, filter_node.sources
-    # this is where the left and right branches meet
-    assert set(union_node.sources) == {"left", "right"}, union_node.sources
+    for nid, node in bound_plan.nodes(True):
+        if node.node_type == LogicalPlanStepType.Filter:
+            # this is just on the left branch
+            assert set(node.sources) == {"left"}, node.sources
+        if node.node_type == LogicalPlanStepType.Union:
+            # this is where the left and right branches meet
+            assert set(node.sources) == {"left", "right"}, node.sources
 
 
 if __name__ == "__main__":  # pragma: no cover
diff --git a/tests/sql_battery/test_exclude_arm.py b/tests/sql_battery/test_exclude_arm.py
--- a/tests/sql_battery/test_exclude_arm.py
+++ b/tests/sql_battery/test_exclude_arm.py
@@ -26,9 +26,9 @@
         ("SELECT * FROM $astronauts", 357, 19, None),
 
         # Large results can't be added to pages [#453]
-        ("SELECT SHA512(column_0) FROM FAKE(150000, 1)", 150000, 1, None),
+        ("SELECT SHA512(column_0) FROM FAKE(150000, 1) AS FK", 150000, 1, None),
         # Low cardinality INNER JOINS blow memory [#444]
-        ("SELECT COUNT(*) FROM (SELECT * FROM testdata.flat.formats.parquet WITH(NO_PARTITION) LIMIT 50) INNER JOIN testdata.flat.formats.parquet WITH(NO_PARTITION) USING (user_verified)", 1, 1, None),
+        ("SELECT COUNT(*) FROM FAKE(50, (BOOLEAN, INTEGER)) AS L(bool) INNER JOIN FAKE(500000, (BOOLEAN,INTEGER)) AS R(bool) USING (bool)", 1, 1, None),
 ]