Repository: mabel-dev/opteryx

Issue:
🪲 IP Address handing fails edge cases
### 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
SELECT null | "192.168.0.0/24"
~~~

**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/functions/binary_operators.py b/opteryx/functions/binary_operators.py
--- a/opteryx/functions/binary_operators.py
+++ b/opteryx/functions/binary_operators.py
@@ -25,6 +25,7 @@
 
 from opteryx.utils import dates
 
+# fmt:off
 OPERATOR_FUNCTION_MAP: Dict[str, Any] = {
     "Divide": numpy.divide,
     "Minus": numpy.subtract,
@@ -32,9 +33,7 @@
     "Multiply": numpy.multiply,
     "Plus": numpy.add,
     "StringConcat": compute.binary_join_element_wise,
-    "MyIntegerDivide": lambda left, right: numpy.trunc(numpy.divide(left, right)).astype(
-        numpy.int64
-    ),
+    "MyIntegerDivide": lambda left, right: numpy.trunc(numpy.divide(left, right)).astype(numpy.int64),
     "BitwiseOr": numpy.bitwise_or,
     "BitwiseAnd": numpy.bitwise_and,
     "BitwiseXor": numpy.bitwise_xor,
@@ -45,12 +44,10 @@
 BINARY_OPERATORS = set(OPERATOR_FUNCTION_MAP.keys())
 
 INTERVALS = (pyarrow.lib.MonthDayNano, pyarrow.lib.MonthDayNanoIntervalArray)
-DATES = (
-    numpy.datetime64,
-    pyarrow.lib.Date32Array,
-)
+DATES = (numpy.datetime64, pyarrow.lib.Date32Array)
 LISTS = (pyarrow.Array, numpy.ndarray, list, array.ArrayType)
 STRINGS = (str, numpy.str_)
+# fmt:on
 
 # Also supported by the AST but not implemented
 
@@ -123,11 +120,19 @@ def _ip_containment(left: List[Optional[str]], right: List[str]) -> List[Optiona
         List[Optional[bool]]:
             A list of boolean values indicating if each corresponding IP in 'left' is in 'right'.
     """
+    from ipaddress import AddressValueError
     from ipaddress import IPv4Address
     from ipaddress import IPv4Network
 
-    network = IPv4Network(right[0], strict=False)
-    return [(IPv4Address(ip) in network) if ip else None for ip in left]
+    try:
+        network = IPv4Network(right[0], strict=False)
+        return [(IPv4Address(ip) in network) if ip is not None else None for ip in left]
+    except AddressValueError as err:
+        from opteryx.exceptions import IncorrectTypeError
+
+        raise IncorrectTypeError(
+            "The `|` operator can be used as bitwise OR or IP address containment only."
+        ) from err
 
 
 def _either_side_is_type(left, right, types):
diff --git a/opteryx/third_party/pyarrow_ops/ops.py b/opteryx/third_party/pyarrow_ops/ops.py
--- a/opteryx/third_party/pyarrow_ops/ops.py
+++ b/opteryx/third_party/pyarrow_ops/ops.py
@@ -1,9 +1,6 @@
 """
 Original code modified for Opteryx.
 """
-from ipaddress import IPv4Address
-from ipaddress import IPv4Network
-
 import numpy
 import pyarrow
 from pyarrow import compute
@@ -160,20 +157,7 @@ def _inner_filter_operations(arr, operator, value):
         # MODIFIED FOR OPTERYX - see comment above
         matches = compute.match_substring_regex(arr, value[0], ignore_case=True)  # [#325]
         return numpy.invert(matches)
-    if operator == "BitwiseOr":
-        try:
-            # parse right to be a list of IPs
-            value = IPv4Network(value[0], strict=False)
-            # is left in right
-            result = []
-            for address in arr:
-                if address:
-                    result.append(IPv4Address(address) in value)
-                else:
-                    result.append(None)
-            return result
-        except Exception as e:
-            raise NotImplementedError("`|` can only be used to test IP address containment.")
+
     raise NotImplementedError(f"Operator {operator} is not implemented!")  # pragma: no cover
 
 


Test patch:
diff --git a/tests/sql_battery/test_shapes_and_errors_battery.py b/tests/sql_battery/test_shapes_and_errors_battery.py
--- a/tests/sql_battery/test_shapes_and_errors_battery.py
+++ b/tests/sql_battery/test_shapes_and_errors_battery.py
@@ -1014,6 +1014,28 @@
         ("SELECT name FROM $satellites WHERE '192.168.0.1' | '192.168.0.0/24'", 177, 1, None),
         ("SELECT name FROM $satellites WHERE '192.168.0.1' | '192.167.0.0/24'", 0, 1, None),
         ("SELECT name FROM $satellites WHERE 12 | 22", 177, 1, None),
+        ("SELECT '1' | '1'", None, None, IncorrectTypeError),
+        ("SELECT 'abc' | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT 123 | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT '192.168.1.1' | 'abc'", None, None, IncorrectTypeError),
+        ("SELECT '192.168.1.1' | 123", None, None, IncorrectTypeError),
+        ("SELECT '192.168.1.1' | 0", None, None, IncorrectTypeError),
+        ("SELECT 0 | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT '0' | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT '192.168.1.1' | '0'", None, None, IncorrectTypeError),
+        ("SELECT name FROM $satellites WHERE '1' | '1'", None, None, IncorrectTypeError),
+        ("SELECT name FROM $satellites WHERE 'abc' | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT name FROM $satellites WHERE 123 | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT name FROM $satellites WHERE '10.10.10.10' | '192.168.1.1'", 0, 1, None),
+        ("SELECT name FROM $satellites WHERE 0 | 0", 0, 1, None),
+        ("SELECT name FROM $satellites WHERE 0 | 123456", 177, 1, None),
+        ("SELECT name FROM $satellites WHERE 123456 | 0", 177, 1, None),
+        ("SELECT name FROM $satellites WHERE 987654321 | 123456789", 177, 1, None),
+        ("SELECT '192.168.1.1' | '255.255..255'", None, None, IncorrectTypeError),
+        ("SELECT '192.168.1.1/32' | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT '192.168.1.*' | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT '!!' | '192.168.1.1'", None, None, IncorrectTypeError),
+        ("SELECT null | '192.168.1.1'", 1, 1, None),
 
         ("SELECT COUNT(*), place FROM (SELECT CASE id WHEN 3 THEN 'Earth' WHEN 1 THEN 'Mercury' ELSE 'Elsewhere' END as place FROM $planets) AS SQ GROUP BY place;", 3, 2, None),
         ("SELECT COUNT(*), place FROM (SELECT CASE id WHEN 3 THEN 'Earth' WHEN 1 THEN 'Mercury' END as place FROM $planets) AS SQ GROUP BY place HAVING place IS NULL;", 1, 2, None),