Skip to content

Commit

Permalink
Merge pull request #1678 from mabel-dev/#1586
Browse files Browse the repository at this point in the history
  • Loading branch information
joocer authored May 26, 2024
2 parents b2fb25e + 229e7a5 commit 83605cb
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 2 deletions.
2 changes: 1 addition & 1 deletion opteryx/__version__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__build__ = 517
__build__ = 518

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
1 change: 0 additions & 1 deletion opteryx/connectors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ def connector_factory(dataset, statistics, **config):
from opteryx.connectors import file_connector

return file_connector.FileConnector(dataset=dataset, statistics=statistics)

# fall back to the default connector (local disk if not set)
connector = _storage_prefixes.get("_default", DiskConnector)

Expand Down
24 changes: 24 additions & 0 deletions opteryx/planner/logical_planner/logical_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
from opteryx.managers.expression import get_all_nodes_of_type
from opteryx.models import Node
from opteryx.planner.logical_planner import logical_planner_builders
from opteryx.planner.views import is_view
from opteryx.planner.views import view_as_plan
from opteryx.third_party.travers import Graph


Expand Down Expand Up @@ -663,6 +665,28 @@ def create_node_relation(relation):
root_node = step_id
else: # pragma: no cover
raise NotImplementedError(relation["relation"]["Derived"])
elif is_view(relation["relation"]["Table"]["name"][0]["value"]):
# We're a view, we need to add it to the plan as a subquery
view_name = relation["relation"]["Table"]["name"][0]["value"]
sub_plan = view_as_plan(view_name=view_name)
plan_head = sub_plan.get_exit_points()[0]

# Replace the exit node with a subquery node
# We call the subquery the name of the view if we don't have an alias
# and we use the colums from the exit node
subquery_node = LogicalPlanNode(node_type=LogicalPlanStepType.Subquery)
subquery_node.alias = (
view_name
if relation["relation"]["Table"]["alias"] is None
else relation["relation"]["Table"]["alias"]["name"]["value"]
)
subquery_node.columns = sub_plan[plan_head].columns
sub_plan[plan_head] = subquery_node
root_node = plan_head

# DEBUG: log (f"VIEW PLAN")
# DEBUG: log (sub_plan)

elif relation["relation"]["Table"]["args"]:
function = relation["relation"]["Table"]
function_name = function["name"][0]["value"].upper()
Expand Down
47 changes: 47 additions & 0 deletions opteryx/planner/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import orjson

from opteryx.managers.expression import NodeType
from opteryx.third_party.travers import Graph


def _load_views():
try:
with open("views.json", "rb") as defs:
return orjson.loads(defs.read())
except Exception as err:
print(f"[OPTERYX] Unable to open views definition file. {err}")
return {}


VIEWS = _load_views()


def is_view(view_name: str) -> bool:
return view_name in VIEWS


def view_as_plan(view_name: str) -> Graph:
from opteryx.planner.logical_planner import do_logical_planning_phase
from opteryx.third_party import sqloxide
from opteryx.utils.sql import clean_statement
from opteryx.utils.sql import remove_comments

operation = VIEWS.get(view_name)["statement"]

clean_sql = clean_statement(remove_comments(operation))
parsed_statements = sqloxide.parse_sql(clean_sql, dialect="mysql")
logical_plan, _, _ = next(do_logical_planning_phase(parsed_statements))

return logical_plan
15 changes: 15 additions & 0 deletions tests/sql_battery/test_shapes_and_errors_battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,7 @@
("SELECT * FROM $astronauts WHERE LIST_CONTAINS_ANY(missions, @@user_memberships)", 3, 19, None),
("SELECT $missions.* FROM $missions INNER JOIN $user ON Mission = value WHERE attribute = 'membership'", 1, 8, None),

# TEST FUNCTIONS
("EXECUTE PLANETS_BY_ID (id=1)", 1, 20, None), # simple case
("EXECUTE PLANETS_BY_ID (1)", None, None, ParameterError), # simple case)
("EXECUTE PLANETS_BY_ID (name=1)", None, None, ParameterError), # simple case)
Expand All @@ -1355,6 +1356,20 @@
("EXECUTE GET_SATELLITES_BY_PLANET_NAME(name='Jupiter')", 67, 1, SqlError), # string param
("EXECUTE multiply_two_numbers (one=1.0, two=9.9)", 1, 1, None), # multiple params

# TEST VIEWS
("SELECT * FROM mission_reports", 177, 1, None),
("SELECT * FROM mission_reports AS MR", 177, 1, None),
("SELECT MR.* FROM mission_reports AS MR", 177, 1, None),
("SELECT satellite_name FROM mission_reports", 177, 1, None),
("SELECT MR.satellite_name FROM mission_reports AS MR", 177, 1, None),
("SELECT satellite_name FROM mission_reports AS MR WHERE satellite_name ILIKE '%a%'", 90, 1, None),
("SELECT satellite_name FROM mission_reports AS MR WHERE satellite_name ILIKE '%a%'", 90, 1, None),
("SELECT * FROM mission_reports INNER JOIN $satellites ON satellite_name = name", 177, 9, None),
("SELECT * FROM my_mission_reports", 3, 19, None),
("SELECT * FROM my_mission_reports WHERE year = 1963", 2, 19, None),
("SELECT * FROM my_mission_reports ORDER BY name", 3, 19, None),
("SELECT name, status FROM my_mission_reports", 3, 2, None),

# These are queries which have been found to return the wrong result or not run correctly
# FILTERING ON FUNCTIONS
("SELECT DATE(birth_date) FROM $astronauts FOR TODAY WHERE DATE(birth_date) < '1930-01-01'", 14, 1, None),
Expand Down
8 changes: 8 additions & 0 deletions views.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mission_reports": {
"statement": "/* A test case for VIEW functionality */ SELECT s.name AS satellite_name FROM $satellites AS s INNER JOIN $planets AS p ON p.id = s.planetId"
},
"my_mission_reports": {
"statement": "/* A test case for row-permissions functionality */ SELECT * FROM $astronauts WHERE LIST_CONTAINS_ANY(missions, @@user_memberships)"
}
}

0 comments on commit 83605cb

Please sign in to comment.