Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
joocer committed Mar 24, 2023
1 parent f87b65e commit 5805f08
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 2 deletions.
4 changes: 4 additions & 0 deletions opteryx/components/binder/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

from opteryx.exceptions import ProgrammingError
from opteryx.exceptions import SqlError
from opteryx.managers.expression import ExpressionTreeNode
from opteryx.models import QueryProperties


Expand Down Expand Up @@ -58,6 +59,9 @@ def variable_binder(node, parameter_set, properties, query_type):
raise ProgrammingError("Incorrect number of bindings supplied."
" More placeholders are provided than parameters.")
placeholder_value = parameter_set.pop(0)
# prepared statements will have parsed this already
if isinstance(placeholder_value, ExpressionTreeNode):
placeholder_value = placeholder_value.value
return _build_literal_node(placeholder_value)
# fmt:on
# replace @variables
Expand Down
42 changes: 42 additions & 0 deletions opteryx/components/logical_planner/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
# 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 import operators
from opteryx.components.logical_planner import builders
from opteryx.components.logical_planner import custom_builders
from opteryx.connectors import DiskConnector
from opteryx.connectors import connector_factory
from opteryx.exceptions import DatabaseError
from opteryx.exceptions import ProgrammingError
from opteryx.exceptions import SqlError
from opteryx.managers.expression import ExpressionTreeNode
Expand Down Expand Up @@ -505,9 +508,48 @@ def analyze_query(ast, properties):
return plan


def execute_query(ast, properties):
"""execute a prepared statement"""
try:
statement_name = ast["Execute"]["name"]["value"]
parameters = [builders.build(p["Value"]) for p in ast["Execute"]["parameters"]]
prepared_statatements = orjson.loads(open("prepared_statements.json").read())
if statement_name not in prepared_statatements:
raise SqlError("Unable to EXECUTE prepared statement, '{statement_name}' not defined.")
prepared_statement = prepared_statatements[statement_name]

from opteryx.components.query_planner import QueryPlanner
from opteryx.components.sql_rewriter.sql_rewriter import clean_statement
from opteryx.components.sql_rewriter.sql_rewriter import remove_comments
from opteryx.components.sql_rewriter.temporal_extraction import extract_temporal_filters

# these would have been applied to the EXECUTE statement, we want to do them on the
# prepared statement
statement = remove_comments(prepared_statement)
statement = clean_statement(statement)
statement, properties.temporal_filters = extract_temporal_filters(statement)

# we need to plan the prepared statement
query_planner = QueryPlanner(properties=properties, statement=statement)
asts = list(query_planner.parse_and_lex())
if len(asts) != 1:
raise SqlError("Cannot execute prepared batch statements.")
query_planner.test_paramcount(asts, parameters)
prepared_ast = query_planner.bind_ast(asts[0], parameters=parameters)
plan = query_planner.create_logical_plan(prepared_ast)
# optimize is the next step, so don't need to do it here
return plan

except OSError as err:
raise DatabaseError(
"Unable to EXECUTE prepared statement, cannot find definitions."
) from err


# wrappers for the query builders
QUERY_BUILDER = {
"Analyze": analyze_query,
"Execute": execute_query,
"Explain": explain_query,
"Query": select_query,
"SetVariable": set_variable_query,
Expand Down
6 changes: 4 additions & 2 deletions opteryx/components/query_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def __init__(self, *, statement: str = "", cache=None, ast=None, properties=None
if isinstance(statement, bytes):
statement = statement.decode()
self.raw_statement = statement
self.statement = statement

if properties is None:
self.properties = QueryProperties(qid, config._config_values)
Expand Down Expand Up @@ -112,8 +113,9 @@ def parse_and_lex(self):
with open(PROFILE_LOCATION, mode="w") as f:
f.write(plans)
except Exception as err:
print("Unable to plan query {self.statement}")
print(f"{type(err).__name__} - {err}")
# print("Unable to plan query {self.statement}")
# print(f"{type(err).__name__} - {err}")
pass

yield from parsed_statements
except ValueError as exception: # pragma: no cover
Expand Down
3 changes: 3 additions & 0 deletions prepared_statements.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"get_planet_by_id": "SELECT id, name FROM $planets WHERE id = ?"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"summary": "Test EXECUTE",
"statement": "EXECUTE get_planet_by_id(3)",
"result": {"id":[3],"name":["Earth"]}
}

0 comments on commit 5805f08

Please sign in to comment.