Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
joocer committed Jul 2, 2023
1 parent 86f0851 commit b29c7b6
Show file tree
Hide file tree
Showing 50 changed files with 297 additions and 218 deletions.
76 changes: 2 additions & 74 deletions opteryx/managers/expression/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,85 +30,13 @@
from opteryx.models.node import Node
from opteryx.third_party.pyarrow_ops.ops import filter_operations

from .format_expression import format_expression

# These are bit-masks
LOGICAL_TYPE: int = int("00010000", 2)
INTERNAL_TYPE: int = int("00100000", 2)


def format_expression(root):
"""
TODO:
- change so literals are called `column_1`, `column_2`
- change so expressions are called `1_times_7`
"""

if root is None:
return "null"

if isinstance(root, list):
return [format_expression(item) for item in root]

node_type = root.node_type
_map: dict = {}

# LITERAL TYPES
if node_type == NodeType.LITERAL:
literal_type = root.type
if literal_type == OrsoTypes.VARCHAR:
return "'" + root.value.replace("'", "'") + "'"
if literal_type == OrsoTypes.TIMESTAMP:
return "'" + str(root) + "'"
if literal_type == OrsoTypes.INTERVAL:
return "<INTERVAL>"
return str(root.value)
# INTERAL IDENTIFIERS
if node_type & INTERNAL_TYPE == INTERNAL_TYPE:
if node_type in (NodeType.FUNCTION, NodeType.AGGREGATOR):
if root.value == "CASE":
con = [format_expression(a) for a in root.parameters[0].value]
vals = [format_expression(a) for a in root.parameters[1].value]
return "CASE " + "".join([f"WHEN {c} THEN {v} " for c, v in zip(con, vals)]) + "END"
distinct = "DISTINCT " if root.distinct else ""
order = ""
if root.order:
order = f" ORDER BY {', '.join(item[0].value + (' DESC' if not item[1] else '') for item in (root.order or []))}"
if root.value == "ARRAY_AGG":
limit = f" LIMIT {root.limit}" if root.limit else ""
return f"{root.value.upper()}({distinct}{format_expression(root.expression)}{order}{limit})"
return f"{root.value.upper()}({distinct}{','.join([format_expression(e) for e in root.parameters])}{order})"
if node_type == NodeType.WILDCARD:
return "*"
if node_type == NodeType.BINARY_OPERATOR:
_map = {
"StringConcat": "||",
"Plus": "+",
"Minus": "-",
"Multiply": "*",
"Divide": "/",
"MyIntegerDivide": "div",
}
return f"{format_expression(root.left)} {_map.get(root.value, root.value).upper()} {format_expression(root.right)}"
if node_type == NodeType.COMPARISON_OPERATOR:
_map = {"Eq": "=", "Lt": "<", "Gt": ">", "NotEq": "!=", "BitwiseOr": "|"}
return f"{format_expression(root.left)} {_map.get(root.value, root.value).upper()} {format_expression(root.right)}"
if node_type == NodeType.UNARY_OPERATOR:
_map = {"IsNull": "%s IS NULL", "IsNotNull": "%s IS NOT NULL"}
return _map.get(root.value, root.value + "(%s)").replace(
"%s", format_expression(root.centre)
)
if node_type == NodeType.NOT:
return f"NOT {format_expression(root.centre)}"
if node_type in (NodeType.AND, NodeType.OR, NodeType.XOR):
_map = {
NodeType.AND: "AND",
NodeType.OR: "OR",
NodeType.XOR: "XOR",
} # type:ignore
return f"{format_expression(root.left)} {_map[node_type]} {format_expression(root.right)}"

return str(root.value)


class NodeType(int, Enum):
"""
The types of Nodes we will see.
Expand Down
83 changes: 83 additions & 0 deletions opteryx/managers/expression/format_expression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from enum import Enum

import numpy
import pyarrow
from orso.types import OrsoTypes
from pyarrow import Table

from opteryx.functions import FUNCTIONS
from opteryx.functions.binary_operators import binary_operations
from opteryx.functions.unary_operations import UNARY_OPERATIONS
from opteryx.models.node import Node
from opteryx.third_party.pyarrow_ops.ops import filter_operations


def format_expression(root):
# circular imports
from . import NodeType, INTERNAL_TYPE, LOGICAL_TYPE

if root is None:
return "null"

if isinstance(root, list):
return [format_expression(item) for item in root]

node_type = root.node_type
_map: dict = {}

# LITERAL TYPES
if node_type == NodeType.LITERAL:
literal_type = root.type
if literal_type == OrsoTypes.VARCHAR:
return "'" + root.value.replace("'", "'") + "'"
if literal_type == OrsoTypes.TIMESTAMP:
return "'" + str(root) + "'"
if literal_type == OrsoTypes.INTERVAL:
return "<INTERVAL>"
return str(root.value)
# INTERAL IDENTIFIERS
if node_type & INTERNAL_TYPE == INTERNAL_TYPE:
if node_type in (NodeType.FUNCTION, NodeType.AGGREGATOR):
if root.value == "CASE":
con = [format_expression(a) for a in root.parameters[0].value]
vals = [format_expression(a) for a in root.parameters[1].value]
return "CASE " + "".join([f"WHEN {c} THEN {v} " for c, v in zip(con, vals)]) + "END"
distinct = "DISTINCT " if root.distinct else ""
order = ""
if root.order:
order = f" ORDER BY {', '.join(item[0].value + (' DESC' if not item[1] else '') for item in (root.order or []))}"
if root.value == "ARRAY_AGG":
limit = f" LIMIT {root.limit}" if root.limit else ""
return f"{root.value.upper()}({distinct}{format_expression(root.expression)}{order}{limit})"
return f"{root.value.upper()}({distinct}{','.join([format_expression(e) for e in root.parameters])}{order})"
if node_type == NodeType.WILDCARD:
return "*"
if node_type == NodeType.BINARY_OPERATOR:
_map = {
"StringConcat": "||",
"Plus": "+",
"Minus": "-",
"Multiply": "*",
"Divide": "/",
"MyIntegerDivide": "div",
}
return f"{format_expression(root.left)} {_map.get(root.value, root.value).upper()} {format_expression(root.right)}"
if node_type == NodeType.COMPARISON_OPERATOR:
_map = {"Eq": "=", "Lt": "<", "Gt": ">", "NotEq": "!=", "BitwiseOr": "|"}
return f"{format_expression(root.left)} {_map.get(root.value, root.value).upper()} {format_expression(root.right)}"
if node_type == NodeType.UNARY_OPERATOR:
_map = {"IsNull": "%s IS NULL", "IsNotNull": "%s IS NOT NULL"}
return _map.get(root.value, root.value + "(%s)").replace(
"%s", format_expression(root.centre)
)
if node_type == NodeType.NOT:
return f"NOT {format_expression(root.centre)}"
if node_type in (NodeType.AND, NodeType.OR, NodeType.XOR):
_map = {
NodeType.AND: "AND",
NodeType.OR: "OR",
NodeType.XOR: "XOR",
} # type:ignore
return f"{format_expression(root.left)} {_map[node_type]} {format_expression(root.right)}"

return str(root.value)
5 changes: 2 additions & 3 deletions tests/misc/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ def test_execute():


if __name__ == "__main__": # pragma: no cover
test_connection()
test_execute()
from tests.tools import run_tests

print("✅ okay")
run_tests()
6 changes: 2 additions & 4 deletions tests/misc/test_connection_arrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ def test_direct_as_arrow_no_limit():


if __name__ == "__main__": # pragma: no cover
test_as_arrow_no_limit()
test_as_arrow_with_limit()
test_direct_as_arrow_no_limit()
from tests.tools import run_tests

print("✅ okay")
run_tests()
6 changes: 2 additions & 4 deletions tests/misc/test_connection_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ def test_fetching():


if __name__ == "__main__": # pragma: no cover
test_connection_warnings()
test_connection_parameter_mismatch()
test_fetching()
from tests.tools import run_tests

print("✅ okay")
run_tests()
5 changes: 2 additions & 3 deletions tests/misc/test_connection_pandas.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def test_as_pandas_with_limit():


if __name__ == "__main__": # pragma: no cover
test_as_pandas_no_limit()
test_as_pandas_with_limit()
from tests.tools import run_tests

print("✅ okay")
run_tests()
5 changes: 2 additions & 3 deletions tests/misc/test_connection_polars.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ def test_as_polars_with_limit():


if __name__ == "__main__": # pragma: no cover
test_as_polars_no_limit()
test_as_polars_with_limit()
from tests.tools import run_tests

print("✅ okay")
run_tests()
4 changes: 2 additions & 2 deletions tests/misc/test_connector_prefixes.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ def test_connector_prefixes():


if __name__ == "__main__": # pragma: no cover
test_connector_prefixes()
from tests.tools import run_tests

print("✅ okay")
run_tests()
12 changes: 3 additions & 9 deletions tests/misc/test_cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,6 @@ def test_execute_error():


if __name__ == "__main__": # pragma: no cover
test_execute()
test_rowcount()
test_shape()
test_fetchone()
test_fetchmany()
test_fetchall()
test_execute_error()

print("✅ okay")
from tests.tools import run_tests

run_tests()
11 changes: 3 additions & 8 deletions tests/misc/test_date_trunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ def test_truncate_to_week():


if __name__ == "__main__": # pragma: no cover
test_truncate_to_day()
test_truncate_to_hour()
test_truncate_to_minute()
test_truncate_to_second()
test_truncate_to_week()
test_truncate_to_year()

print("✅ okay")
from tests.tools import run_tests

run_tests()
18 changes: 3 additions & 15 deletions tests/misc/test_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,6 @@ def get_user_permissions(user_roles):


if __name__ == "__main__": # pragma: no cover
test_documentation_connect_example()
test_readme_1() # execute-a-simple-query-in-python
test_readme_2() # execute-sql-on-a-pandas-dataframe
test_readme_3() # query-data-on-local-disk
test_readme_4() # query-data-on-gcs
test_readme_5() # query-data-in-sqlite
test_get_started()
test_pandas_integration_input()
test_pandas_integration_output()
test_polars_integration_input()
test_polars_integration_output()
test_permissions_example()
test_role_based_permissions()

print("✅ okay")
from tests.tools import run_tests

run_tests()
7 changes: 3 additions & 4 deletions tests/misc/test_lruk.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ def test_lru_cache_eviction():


if __name__ == "__main__": # pragma: no cover
test_lruk()
test_lru2()
test_lru_cache_eviction()
print("✅ okay")
from tests.tools import run_tests

run_tests()
5 changes: 3 additions & 2 deletions tests/misc/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ def test_node():


if __name__ == "__main__": # pragma: no cover
test_node()
print("✅ okay")
from tests.tools import run_tests

run_tests()
24 changes: 24 additions & 0 deletions tests/misc/test_node_formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os
import sys

sys.path.insert(1, os.path.join(sys.path[0], "../.."))

from orso.types import OrsoTypes

from opteryx.managers.expression import format_expression
from opteryx.managers.expression import NodeType
from opteryx.models.node import Node


def test_format_nodes():
left_node = Node(NodeType.LITERAL, type=OrsoTypes.INTEGER, value=1)
right_node = Node(NodeType.LITERAL, type=OrsoTypes.DOUBLE, value=1.1)

print(format_expression(left_node))
print(format_expression(right_node))


if __name__ == "__main__": # pragma: no cover
from tests.tools import run_tests

run_tests()
5 changes: 2 additions & 3 deletions tests/misc/test_pandas_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def test_documentation():


if __name__ == "__main__": # pragma: no cover
test_pandas()
test_documentation()
from tests.tools import run_tests

print("✅ okay")
run_tests()
4 changes: 2 additions & 2 deletions tests/misc/test_polars_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ def test_polars():


if __name__ == "__main__": # pragma: no cover
test_polars()
from tests.tools import run_tests

print("✅ okay")
run_tests()
4 changes: 2 additions & 2 deletions tests/misc/test_pyarrow_ops_join.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ def test_hash_join_consistency():


if __name__ == "__main__": # pragma: no cover
test_hash_join_consistency()
from tests.tools import run_tests

print("✅ okay")
run_tests()
5 changes: 2 additions & 3 deletions tests/misc/test_relation_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def test_dictionary_column():


if __name__ == "__main__": # pragma: no cover
test_constant_column()
test_dictionary_column()
from tests.tools import run_tests

print("✅ okay")
run_tests()
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ def test_format_sql():
), (str(formatted_sql.encode()) + "\n" + formatted_sql)


if __name__ == "__main__":
test_format_sql()
if __name__ == "__main__": # pragma: no cover
from tests.tools import run_tests

run_tests()
7 changes: 3 additions & 4 deletions tests/misc/test_string_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ def test_random_string():


if __name__ == "__main__": # pragma: no cover
test_slice_left()
test_slice_right()
test_random_string()
print("✅ okay")
from tests.tools import run_tests

run_tests()
4 changes: 2 additions & 2 deletions tests/misc/test_suggestions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ def test_hint_hints():


if __name__ == "__main__": # pragma: no cover
test_hint_hints()
from tests.tools import run_tests

print("✅ okay")
run_tests()
Loading

0 comments on commit b29c7b6

Please sign in to comment.