prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  🪲 Incorrect error raised on type errors on JOINs
  ### 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._
  
  
  </issue>
  <code>
  [start of opteryx/operators/join_node.py]
  1 # Licensed under the Apache License, Version 2.0 (the "License");
  2 # you may not use this file except in compliance with the License.
  3 # You may obtain a copy of the License at
  4 #
  5 #     http://www.apache.org/licenses/LICENSE-2.0
  6 #
  7 # Unless required by applicable law or agreed to in writing, software
  8 # distributed under the License is distributed on an "AS IS" BASIS,
  9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10 # See the License for the specific language governing permissions and
  11 # limitations under the License.
  12 
  13 """
  14 Join Node
  15 
  16 This is a SQL Query Execution Plan Node.
  17 
  18 This handles most of the join types as a wrapper for pyarrow's JOIN functions, 
  19 only CROSS JOINs are not handled here.
  20 
  21 This is a faster implementation that the pyarrow_ops implementation, but hashes
  22 the right table every call so carries a penalty.
  23 """
  24 from typing import Generator
  25 
  26 import pyarrow
  27 
  28 from opteryx.models import QueryProperties
  29 from opteryx.operators import BasePlanNode
  30 
  31 
  32 class JoinNode(BasePlanNode):
  33     def __init__(self, properties: QueryProperties, **config):
  34         super().__init__(properties=properties)
  35         self._join_type = config["type"]
  36         self._on = config.get("on")
  37         self._using = config.get("using")
  38 
  39         self._left_columns = config.get("left_columns")
  40         self._left_relation = config.get("left_relation_names")
  41 
  42         self._right_columns = config.get("right_columns")
  43         self._right_relation = config.get("right_relation_names")
  44 
  45     @property
  46     def name(self):  # pragma: no cover
  47         return f"{self._join_type} Join"
  48 
  49     @property
  50     def config(self):  # pragma: no cover
  51         return ""
  52 
  53     def execute(self) -> Generator:
  54         left_node = self._producers[0]  # type:ignore
  55         right_node = self._producers[1]  # type:ignore
  56 
  57         right_table = pyarrow.concat_tables(right_node.execute(), mode="default")
  58 
  59         for morsel in left_node.execute():
  60             # in place until #1295 resolved
  61             if not self._right_columns[0] in morsel.column_names:
  62                 self._right_columns, self._left_columns = self._left_columns, self._right_columns
  63 
  64             try:
  65                 # do the join
  66                 new_morsel = morsel.join(
  67                     right_table,
  68                     keys=self._right_columns,
  69                     right_keys=self._left_columns,
  70                     join_type=self._join_type,
  71                     coalesce_keys=self._using is not None,
  72                 )
  73             except pyarrow.ArrowInvalid as err:  # pragma: no cover
  74                 last_token = str(err).split(" ")[-1]
  75                 column = None
  76                 for col in left_node.columns:
  77                     if last_token == col.identity:
  78                         column = col.name
  79                         break
  80                 for col in right_node.columns:
  81                     if last_token == col.identity:
  82                         column = col.name
  83                         break
  84                 if column:
  85                     raise pyarrow.ArrowInvalid(str(err).replace(last_token, f"'{column}'"))
  86                 raise err
  87 
  88             yield new_morsel
  89
  [end of opteryx/operators/join_node.py]
  [start of opteryx/__version__.py]
  1 __build__ = 226
  2 
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 #
  7 #     http://www.apache.org/licenses/LICENSE-2.0
  8 #
  9 # Unless required by applicable law or agreed to in writing, software
  10 # distributed under the License is distributed on an "AS IS" BASIS,
  11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 # See the License for the specific language governing permissions and
  13 # limitations under the License.
  14 
  15 """
  16 Store the version here so:
  17 1) we don't load dependencies by storing it in __init__.py
  18 2) we can import it in setup.py for the same reason
  19 """
  20 from enum import Enum  # isort: skip
  21 
  22 
  23 class VersionStatus(Enum):
  24     ALPHA = "alpha"
  25     BETA = "beta"
  26     RELEASE = "release"
  27 
  28 
  29 _major = 0
  30 _minor = 12
  31 _revision = 4
  32 _status = VersionStatus.BETA
  33 
  34 __version__ = f"{_major}.{_minor}.{_revision}" + (
  35     f"-{_status.value}.{__build__}" if _status != VersionStatus.RELEASE else ""
  36 )
  37
  [end of opteryx/__version__.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/opteryx/__version__.py b/opteryx/__version__.py
  --- a/opteryx/__version__.py
  +++ b/opteryx/__version__.py
  @@ -1,4 +1,4 @@
  -__build__ = 226
  +__build__ = 228
   
   # 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/operators/join_node.py b/opteryx/operators/join_node.py
  --- a/opteryx/operators/join_node.py
  +++ b/opteryx/operators/join_node.py
  @@ -25,6 +25,7 @@
   
   import pyarrow
   
  +from opteryx.exceptions import UnsupportedSyntaxError
   from opteryx.models import QueryProperties
   from opteryx.operators import BasePlanNode
   
  @@ -82,7 +83,11 @@
                           column = col.name
                           break
                   if column:
  -                    raise pyarrow.ArrowInvalid(str(err).replace(last_token, f"'{column}'"))
  -                raise err
  +                    raise UnsupportedSyntaxError(
  +                        f"Unable to JOIN with unsupported column types in table, '{column}'."
  +                    ) from err
  +                raise UnsupportedSyntaxError(
  +                    "Unable to JOIN with unsupported column types in table."
  +                ) from err
   
               yield new_morsel
  
verification_info: {"golden_diff": "diff --git a/opteryx/__version__.py b/opteryx/__version__.py\n--- a/opteryx/__version__.py\n+++ b/opteryx/__version__.py\n@@ -1,4 +1,4 @@\n-__build__ = 226\n+__build__ = 228\n \n # Licensed under the Apache License, Version 2.0 (the \"License\");\n # you may not use this file except in compliance with the License.\ndiff --git a/opteryx/operators/join_node.py b/opteryx/operators/join_node.py\n--- a/opteryx/operators/join_node.py\n+++ b/opteryx/operators/join_node.py\n@@ -25,6 +25,7 @@\n \n import pyarrow\n \n+from opteryx.exceptions import UnsupportedSyntaxError\n from opteryx.models import QueryProperties\n from opteryx.operators import BasePlanNode\n \n@@ -82,7 +83,11 @@\n                         column = col.name\n                         break\n                 if column:\n-                    raise pyarrow.ArrowInvalid(str(err).replace(last_token, f\"'{column}'\"))\n-                raise err\n+                    raise UnsupportedSyntaxError(\n+                        f\"Unable to JOIN with unsupported column types in table, '{column}'.\"\n+                    ) from err\n+                raise UnsupportedSyntaxError(\n+                    \"Unable to JOIN with unsupported column types in table.\"\n+                ) from err\n \n             yield new_morsel\n", "issue": "\ud83e\udeb2 Incorrect error raised on type errors on JOINs\n### Thank you for taking the time to report a problem with Opteryx.\r\n_To help us to respond to your request we ask that you try to provide the below detail about the bug._\r\n\r\n**Describe the bug** _A clear and specific description of what the bug is. What the error, incorrect or unexpected behaviour was._\r\n\r\n\r\n**Expected behaviour** _A clear and concise description of what you expected to happen._\r\n\r\n\r\n**Sample Code/Statement** _If you can, please submit the SQL statement or Python code snippet, or a representative example using the sample datasets._\r\n\r\n~~~sql\r\n\r\n~~~\r\n\r\n**Additional context** _Add any other context about the problem here, for example what you have done to try to diagnose or workaround the problem._\r\n\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nJoin Node\n\nThis is a SQL Query Execution Plan Node.\n\nThis handles most of the join types as a wrapper for pyarrow's JOIN functions, \nonly CROSS JOINs are not handled here.\n\nThis is a faster implementation that the pyarrow_ops implementation, but hashes\nthe right table every call so carries a penalty.\n\"\"\"\nfrom typing import Generator\n\nimport pyarrow\n\nfrom opteryx.models import QueryProperties\nfrom opteryx.operators import BasePlanNode\n\n\nclass JoinNode(BasePlanNode):\n    def __init__(self, properties: QueryProperties, **config):\n        super().__init__(properties=properties)\n        self._join_type = config[\"type\"]\n        self._on = config.get(\"on\")\n        self._using = config.get(\"using\")\n\n        self._left_columns = config.get(\"left_columns\")\n        self._left_relation = config.get(\"left_relation_names\")\n\n        self._right_columns = config.get(\"right_columns\")\n        self._right_relation = config.get(\"right_relation_names\")\n\n    @property\n    def name(self):  # pragma: no cover\n        return f\"{self._join_type} Join\"\n\n    @property\n    def config(self):  # pragma: no cover\n        return \"\"\n\n    def execute(self) -> Generator:\n        left_node = self._producers[0]  # type:ignore\n        right_node = self._producers[1]  # type:ignore\n\n        right_table = pyarrow.concat_tables(right_node.execute(), mode=\"default\")\n\n        for morsel in left_node.execute():\n            # in place until #1295 resolved\n            if not self._right_columns[0] in morsel.column_names:\n                self._right_columns, self._left_columns = self._left_columns, self._right_columns\n\n            try:\n                # do the join\n                new_morsel = morsel.join(\n                    right_table,\n                    keys=self._right_columns,\n                    right_keys=self._left_columns,\n                    join_type=self._join_type,\n                    coalesce_keys=self._using is not None,\n                )\n            except pyarrow.ArrowInvalid as err:  # pragma: no cover\n                last_token = str(err).split(\" \")[-1]\n                column = None\n                for col in left_node.columns:\n                    if last_token == col.identity:\n                        column = col.name\n                        break\n                for col in right_node.columns:\n                    if last_token == col.identity:\n                        column = col.name\n                        break\n                if column:\n                    raise pyarrow.ArrowInvalid(str(err).replace(last_token, f\"'{column}'\"))\n                raise err\n\n            yield new_morsel\n", "path": "opteryx/operators/join_node.py"}, {"content": "__build__ = 226\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nStore the version here so:\n1) we don't load dependencies by storing it in __init__.py\n2) we can import it in setup.py for the same reason\n\"\"\"\nfrom enum import Enum  # isort: skip\n\n\nclass VersionStatus(Enum):\n    ALPHA = \"alpha\"\n    BETA = \"beta\"\n    RELEASE = \"release\"\n\n\n_major = 0\n_minor = 12\n_revision = 4\n_status = VersionStatus.BETA\n\n__version__ = f\"{_major}.{_minor}.{_revision}\" + (\n    f\"-{_status.value}.{__build__}\" if _status != VersionStatus.RELEASE else \"\"\n)\n", "path": "opteryx/__version__.py"}]}