We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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._


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `opteryx/operators/join_node.py`
Content:
```
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
```

Path: `opteryx/__version__.py`
Content:
```
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 FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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

{"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"}], "after_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.exceptions import UnsupportedSyntaxError\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 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", "path": "opteryx/operators/join_node.py"}, {"content": "__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.\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"}]}