Skip to content

Commit

Permalink
✨ support SHOW CREATE VIEW #2000
Browse files Browse the repository at this point in the history
  • Loading branch information
joocer committed Sep 13, 2024
1 parent 47dcdeb commit 7591765
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 7 deletions.
2 changes: 1 addition & 1 deletion opteryx/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
from .read_node import ReaderNode
from .set_variable_node import SetVariableNode
from .show_columns_node import ShowColumnsNode # column details
from .show_create_node import ShowCreateNode # SHOW CREATE VIEW

# from .show_create_node import ShowCreateNode # SHOW CREATE TABLE
# from .show_databases_node import ShowDatabasesNode # SHOW DATABASES
# from .show_functions_node import ShowFunctionsNode # supported functions
from .show_value_node import ShowValueNode # display node for SHOW
Expand Down
65 changes: 65 additions & 0 deletions opteryx/operators/show_create_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 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.

"""
Show Create Node
This is a SQL Query Execution Plan Node.
"""

from typing import Generator

import pyarrow

from opteryx.exceptions import DatasetNotFoundError
from opteryx.exceptions import UnsupportedSyntaxError
from opteryx.models import QueryProperties
from opteryx.operators import BasePlanNode
from opteryx.operators import OperatorType


class ShowCreateNode(BasePlanNode):
operator_type = OperatorType.PRODUCER

def __init__(self, properties: QueryProperties, **config):
super().__init__(properties=properties)

self.object_type = config.get("object_type")
self.object_name = config.get("object_name")

@classmethod
def from_json(cls, json_obj: str) -> "BasePlanNode": # pragma: no cover
raise NotImplementedError()

@property
def name(self): # pragma: no cover
return "Show"

@property
def config(self): # pragma: no cover
return ""

def execute(self) -> Generator:
if self.object_type == "VIEW":
from opteryx.planner.views import is_view
from opteryx.planner.views import view_as_sql

if is_view(self.object_name):
view_sql = view_as_sql(self.object_name)
buffer = [{self.object_name: view_sql}]
table = pyarrow.Table.from_pylist(buffer)
yield table
return

raise DatasetNotFoundError(self.object_name)

raise UnsupportedSyntaxError("Invalid SHOW statement")
2 changes: 1 addition & 1 deletion opteryx/planner/ast_rewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def temporal_range_binder(ast, filters):
ast["table_name"][0]["start_date"] = temporal_range[1]
ast["table_name"][0]["end_date"] = temporal_range[2]
return ast
if "ShowCreate" in ast:
if "ShowCreate" in ast and filters:
temporal_range = filters.pop(0)
ast["ShowCreate"]["start_date"] = temporal_range[1]
ast["ShowCreate"]["end_date"] = temporal_range[2]
Expand Down
24 changes: 22 additions & 2 deletions opteryx/planner/logical_planner/logical_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ def __str__(self):
if node_type == LogicalPlanStepType.Set:
return f"SET ({self.variable} TO {self.value.value})"
if node_type == LogicalPlanStepType.Show:
return f"SHOW ({', '.join(self.items)})"
if self.object_type == "VARIABLE":
return f"SHOW ({' '.join(self.items)})"
if self.object_type == "VIEW":
return f"SHOW (CREATE VIEW {self.object_name})"
if node_type == LogicalPlanStepType.ShowColumns:
return f"SHOW{' FULL' if self.full else ''}{' EXTENDED' if self.extended else ''} COLUMNS ({self.relation})"
if node_type == LogicalPlanStepType.Subquery:
Expand Down Expand Up @@ -996,7 +999,24 @@ def plan_show_variable(statement):
root_node = "ShowVariable"
plan = LogicalPlan()
show_step = LogicalPlanNode(node_type=LogicalPlanStepType.Show)
show_step.object_type = "VARIABLE"
show_step.items = extract_variable(statement[root_node]["variable"])

if show_step.items[0] not in ("PARAMETER",):
raise UnsupportedSyntaxError(f"SHOW {show_step.items[0]} is not supported.")

plan.add_node(random_string(), show_step)
return plan


def plan_show_create_query(statement):
root_node = "ShowCreate"
plan = LogicalPlan()
show_step = LogicalPlanNode(node_type=LogicalPlanStepType.Show)
show_step.object_type = statement[root_node]["obj_type"].upper()
show_step.object_name = extract_variable(statement[root_node]["obj_name"])
if isinstance(show_step.object_name, list):
show_step.object_name = ".".join(show_step.object_name)
plan.add_node(random_string(), show_step)
return plan

Expand Down Expand Up @@ -1047,7 +1067,7 @@ def plan_show_variables(statement):
"Query": plan_query,
"SetVariable": plan_set_variable,
"ShowColumns": plan_show_columns,
# "ShowCreate": show_create_query,
"ShowCreate": plan_show_create_query,
# "ShowFunctions": show_functions_query,
"ShowVariable": plan_show_variable, # generic SHOW handler
"ShowVariables": plan_show_variables,
Expand Down
8 changes: 5 additions & 3 deletions opteryx/planner/temporary_physical_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@ def create_physical_plan(logical_plan, query_properties) -> ExecutionTree:
elif node_type == LogicalPlanStepType.Set:
node = operators.SetVariableNode(query_properties, **node_config)
elif node_type == LogicalPlanStepType.Show:
if node_config["items"][0] == "PARAMETER":
node = operators.ShowValueNode(query_properties, kind="PARAMETER", value=node_config["items"][1])
if node_config["object_type"] == "VARIABLE":
node = operators.ShowValueNode(query_properties, kind=node_config["items"][1], value=node_config["items"][1])
elif node_config["object_type"] == "VIEW":
node = operators.ShowCreateNode(query_properties, **node_config)
else:
raise UnsupportedSyntaxError("Can only SHOW variables")
raise UnsupportedSyntaxError(f"Unsupported SHOW type '{node_config['object_type']}'")
elif node_type == LogicalPlanStepType.ShowColumns:
node = operators.ShowColumnsNode(query_properties, **node_config)
elif node_type == LogicalPlanStepType.Subquery:
Expand Down
4 changes: 4 additions & 0 deletions opteryx/planner/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ def view_as_plan(view_name: str):
logical_plan, _, _ = next(do_logical_planning_phase(parsed_statements))

return logical_plan


def view_as_sql(view_name: str):
return VIEWS.get(view_name)["statement"]
4 changes: 4 additions & 0 deletions tests/sql_battery/test_shapes_and_errors_battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -1714,6 +1714,10 @@
("SELECT pids FROM (SELECT * FROM $planets LEFT JOIN (SELECT ARRAY_AGG(id) AS pids, planetId FROM $satellites GROUP BY planetId) AS sats ON sats.planetId = $planets.id) as satellites", 9, 1, None),
("SELECT pid FROM (SELECT * FROM $planets LEFT JOIN (SELECT ARRAY_AGG(id) AS pids, planetId FROM $satellites GROUP BY planetId) AS sats ON sats.planetId = $planets.id) as satellites CROSS JOIN UNNEST(pids) AS pid", 177, 1, None),

("SHOW CREATE VIEW mission_reports", 1, 1, None),
("SHOW CREATE VIEW mission.reports", 1, 1, DatasetNotFoundError),
("SHOW CREATE TABLE mission_reports", 1, 1, UnsupportedSyntaxError),

# ****************************************************************************************

# These are queries which have been found to return the wrong result or not run correctly
Expand Down

0 comments on commit 7591765

Please sign in to comment.