Repository: mabel-dev/opteryx

Issue:
🪲 Blobs and Strings should be able to be compared
Bug from running ClickBench tests:

~~~
Error: Incompatible types for column 'hits.SearchPhrase' (BLOB) and literal '' (VARCHAR). Using `CAST(column AS type)` may help resolve.
~~~


Discussion:

Resolving patch:
diff --git a/opteryx/__version__.py b/opteryx/__version__.py
--- a/opteryx/__version__.py
+++ b/opteryx/__version__.py
@@ -1,4 +1,4 @@
-__build__ = 496
+__build__ = 498
 
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/opteryx/planner/binder/binder_visitor.py b/opteryx/planner/binder/binder_visitor.py
--- a/opteryx/planner/binder/binder_visitor.py
+++ b/opteryx/planner/binder/binder_visitor.py
@@ -68,6 +68,7 @@ def get_mismatched_condition_column_types(node: Node, relaxed: bool = False) ->
                 relaxed
                 and (left_type.is_numeric() and right_type.is_numeric())
                 or (left_type.is_temporal() and right_type.is_temporal())
+                or (left_type.is_large_object() and right_type.is_large_object())
             ):
                 return None
             if left_type == OrsoTypes.NULL or right_type == OrsoTypes.NULL:
diff --git a/opteryx/planner/binder/operator_map.py b/opteryx/planner/binder/operator_map.py
--- a/opteryx/planner/binder/operator_map.py
+++ b/opteryx/planner/binder/operator_map.py
@@ -17,6 +17,22 @@ class OperatorMapType(NamedTuple):
 
 # fmt: off
 OPERATOR_MAP: Dict[Tuple[OrsoTypes, OrsoTypes, str], OperatorMapType] = {
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "Eq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "NotEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "Gt"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "GtEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "Lt"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "LtEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "Like"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "NotLike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "ILike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "NotILike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "RLike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "PGRegexIMatch"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "PGRegexNotIMatch"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "NotRLike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "BitwiseOr"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.BLOB, OrsoTypes.VARCHAR, "StringConcat"): OperatorMapType(OrsoTypes.BLOB, None, 100.0),
     (OrsoTypes.BOOLEAN, OrsoTypes.ARRAY, "InList"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
     (OrsoTypes.BOOLEAN, OrsoTypes.ARRAY, "NotInList"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
     (OrsoTypes.BOOLEAN, OrsoTypes.BOOLEAN, "Or"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
@@ -139,6 +155,22 @@ class OperatorMapType(NamedTuple):
     (OrsoTypes.TIMESTAMP, OrsoTypes.TIMESTAMP, "LtEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
     (OrsoTypes.VARCHAR, OrsoTypes.ARRAY, "InList"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
     (OrsoTypes.VARCHAR, OrsoTypes.ARRAY, "NotInList"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "Eq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "NotEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "Gt"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "GtEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "Lt"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "LtEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "Like"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "NotLike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "ILike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "NotILike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "RLike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "PGRegexIMatch"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "PGRegexNotIMatch"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "NotRLike"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "BitwiseOr"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
+    (OrsoTypes.VARCHAR, OrsoTypes.BLOB, "StringConcat"): OperatorMapType(OrsoTypes.BLOB, None, 100.0),
     (OrsoTypes.VARCHAR, OrsoTypes.VARCHAR, "Eq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
     (OrsoTypes.VARCHAR, OrsoTypes.VARCHAR, "NotEq"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),
     (OrsoTypes.VARCHAR, OrsoTypes.VARCHAR, "Gt"): OperatorMapType(OrsoTypes.BOOLEAN, None, 100.0),


Test patch:
diff --git a/tests/sql_battery/test_run_only_battery.py b/tests/sql_battery/test_run_only_battery.py
--- a/tests/sql_battery/test_run_only_battery.py
+++ b/tests/sql_battery/test_run_only_battery.py
@@ -57,6 +57,8 @@ def test_run_only_tests(statement):
 if __name__ == "__main__":  # pragma: no cover
     import shutil
     import time
+    from opteryx.utils.formatter import format_sql
+    from tests.tools import trunc_printable
 
     width = shutil.get_terminal_size((80, 20))[0] - 15
 
@@ -66,8 +68,10 @@ def test_run_only_tests(statement):
     for index, statement in enumerate(RUN_ONLY_TESTS):
         start = time.monotonic_ns()
         print(
-            f"\033[0;36m{(index + 1):04}\033[0m {statement[0:width - 1].ljust(width)}",
+            f"\033[38;2;255;184;108m{(index + 1):04}\033[0m"
+            f" {trunc_printable(format_sql(statement), width - 1)}",
             end="",
+            flush=True,
         )
 
         test_run_only_tests(statement)
diff --git a/tests/sql_battery/tests/planner.run_tests b/tests/sql_battery/tests/planner.run_tests
new file mode 100644
--- /dev/null
+++ b/tests/sql_battery/tests/planner.run_tests
@@ -0,0 +1,3 @@
+SELECT name, id FROM $planets AS A UNION SELECT name, id FROM $planets AS B;
+SELECT * FROM $planets LEFT ANTI JOIN $satellites USING (id);
+EXPLAIN ANALYZE FORMAT JSON  SELECT * FROM $planets AS a INNER JOIN (SELECT id FROM $planets) AS b USING (id);
\ No newline at end of file
diff --git a/tests/sql_battery/tests/planner.run_tests_disabled b/tests/sql_battery/tests/planner.run_tests_disabled
deleted file mode 100644
--- a/tests/sql_battery/tests/planner.run_tests_disabled
+++ /dev/null
@@ -1,3 +0,0 @@
-SELECT * FROM $planets UNION SELECT * FROM $planets;
-SELECT * FROM $planets LEFT ANTI JOIN $satellites ON id = id;
-EXPLAIN ANALYZE FORMAT JSON  SELECT * FROM $planets AS a INNER JOIN (SELECT id FROM $planets) AS b USING (id);
\ No newline at end of file
diff --git a/tests/sql_battery/tests/types.run_tests b/tests/sql_battery/tests/types.run_tests
new file mode 100644
--- /dev/null
+++ b/tests/sql_battery/tests/types.run_tests
@@ -0,0 +1,5 @@
+SELECT BLOB('apple') = 'apple';
+SELECT 'apple' = BLOB('apple');
+SELECT BLOB('apple') > 'banana';
+SELECT 'banana' > BLOB('apple');
+SELECT BLOB('apple') LIKE 'a%ple';
\ No newline at end of file