Skip to content

Commit

Permalink
Adding SET statement for configuration management (#1180)
Browse files Browse the repository at this point in the history
Added

- Parser for SET statement
- Class for SET statement
- StatementType.SET to parser.types
  • Loading branch information
hershd23 committed Sep 23, 2023
1 parent e09cba9 commit 27e2635
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 2 deletions.
4 changes: 4 additions & 0 deletions evadb/executor/plan_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@
from evadb.executor.rename_executor import RenameExecutor
from evadb.executor.sample_executor import SampleExecutor
from evadb.executor.seq_scan_executor import SequentialScanExecutor
from evadb.executor.set_executor import SetExecutor
from evadb.executor.show_info_executor import ShowInfoExecutor
from evadb.executor.storage_executor import StorageExecutor
from evadb.executor.union_executor import UnionExecutor
from evadb.executor.use_executor import UseExecutor
from evadb.executor.vector_index_scan_executor import VectorIndexScanExecutor
from evadb.models.storage.batch import Batch
from evadb.parser.create_statement import CreateDatabaseStatement
from evadb.parser.set_statement import SetStatement
from evadb.parser.statement import AbstractStatement
from evadb.parser.use_statement import UseStatement
from evadb.plan_nodes.abstract_plan import AbstractPlan
Expand Down Expand Up @@ -90,6 +92,8 @@ def _build_execution_tree(
return CreateDatabaseExecutor(db=self._db, node=plan)
elif isinstance(plan, UseStatement):
return UseExecutor(db=self._db, node=plan)
elif isinstance(plan, SetStatement):
return SetExecutor(db=self._db, node=plan)

# Get plan node type
plan_opr_type = plan.opr_type
Expand Down
43 changes: 43 additions & 0 deletions evadb/executor/set_executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# coding=utf-8
# Copyright 2018-2023 EvaDB
#
# 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.
from evadb.database import EvaDBDatabase
from evadb.executor.abstract_executor import AbstractExecutor
from evadb.parser.set_statement import SetStatement


class SetExecutor(AbstractExecutor):
def __init__(self, db: EvaDBDatabase, node: SetStatement):
super().__init__(db, node)

def exec(self, *args, **kwargs):
# Get method implementation from the config.update_value
"""
NOTE :- Currently adding adding all configs in 'default' category.
The idea is to deprecate category to maintain the same format for
the query as DuckDB and Postgres
Ref :-
https://www.postgresql.org/docs/7.0/sql-set.htm
https://duckdb.org/docs/sql/configuration.html
This design change for configuation manager will be taken care of
as a separate PR for the issue #1140, where all instances of config use
will be replaced
"""
self._config.update_value(
category="default",
key=self.node.config_name,
value=self.node.config_value.value,
)
8 changes: 7 additions & 1 deletion evadb/parser/evadb.lark
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ddl_statement: create_database | create_table | create_index | create_function
| drop_database | drop_table | drop_function | drop_index | rename_table

dml_statement: select_statement | insert_statement | update_statement
| delete_statement | load_statement
| delete_statement | load_statement | set_statement

utility_statement: describe_statement | show_statement | help_statement | explain_statement

Expand Down Expand Up @@ -79,6 +79,12 @@ drop_table: DROP TABLE if_exists? uid

drop_function: DROP FUNCTION if_exists? uid

// SET statements (configuration)
set_statement: SET config_name (EQUAL_SYMBOL | TO) config_value

config_name: uid

config_value: (string_literal | decimal_literal | boolean_literal | real_literal)

// Data Manipulation Language

Expand Down
2 changes: 2 additions & 0 deletions evadb/parser/lark_visitor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from evadb.parser.lark_visitor._load_statement import Load
from evadb.parser.lark_visitor._rename_statement import RenameTable
from evadb.parser.lark_visitor._select_statement import Select
from evadb.parser.lark_visitor._set_statement import Set
from evadb.parser.lark_visitor._show_statements import Show
from evadb.parser.lark_visitor._table_sources import TableSources
from evadb.parser.lark_visitor._use_statement import Use
Expand Down Expand Up @@ -77,6 +78,7 @@ class LarkInterpreter(
Explain,
Delete,
Use,
Set,
):
def __init__(self, query):
super().__init__()
Expand Down
35 changes: 35 additions & 0 deletions evadb/parser/lark_visitor/_set_statement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# coding=utf-8
# Copyright 2018-2023 EvaDB
#
# 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.
from lark.tree import Tree

from evadb.parser.set_statement import SetStatement


##################################################################
# DELETE STATEMENTS
##################################################################
class Set:
def set_statement(self, tree):
config_name = None
config_value = None
for child in tree.children:
if isinstance(child, Tree):
if child.data == "config_name":
config_name = self.visit(child)
elif child.data == "config_value":
config_value = self.visit(child)

set_stmt = SetStatement(config_name, config_value)
return set_stmt
49 changes: 49 additions & 0 deletions evadb/parser/set_statement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# coding=utf-8
# Copyright 2018-2023 EvaDB
#
# 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.
from __future__ import annotations

from typing import Any

from evadb.parser.statement import AbstractStatement
from evadb.parser.types import StatementType


class SetStatement(AbstractStatement):
def __init__(self, config_name: str, config_value: Any):
super().__init__(StatementType.SET)
self._config_name = config_name
self._config_value = config_value

@property
def config_name(self):
return self._config_name

@property
def config_value(self):
return self._config_value

def __str__(self):
return f"SET {str(self.config_name)} = {str(self.config_value)}"

def __eq__(self, other: object) -> bool:
if not isinstance(other, SetStatement):
return False
return (
self.config_name == other.config_name
and self.config_value == other.config_value
)

def __hash__(self) -> int:
return hash((super().__hash__(), self.config_name, self.config_value))
1 change: 1 addition & 0 deletions evadb/parser/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class StatementType(EvaDBEnum):
CREATE_INDEX # noqa: F821
CREATE_DATABASE # noqa: F821
USE # noqa: F821
SET # noqa: F821
# add other types


Expand Down
7 changes: 6 additions & 1 deletion evadb/parser/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@
from evadb.parser.parser import Parser
from evadb.parser.rename_statement import RenameTableStatement
from evadb.parser.select_statement import SelectStatement
from evadb.parser.set_statement import SetStatement
from evadb.parser.show_statement import ShowStatement
from evadb.parser.types import ObjectType
from evadb.parser.use_statement import UseStatement

# List of statements for which we omit binder and optimizer and pass the statement
# directly to the executor.
SKIP_BINDER_AND_OPTIMIZER_STATEMENTS = (CreateDatabaseStatement, UseStatement)
SKIP_BINDER_AND_OPTIMIZER_STATEMENTS = (
CreateDatabaseStatement,
UseStatement,
SetStatement,
)


def parse_expression(expr: str):
Expand Down
41 changes: 41 additions & 0 deletions test/integration_tests/short/test_set_executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# coding=utf-8
# Copyright 2018-2023 EvaDB
#
# 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 unittest
from test.util import get_evadb_for_testing

import pytest

from evadb.server.command_handler import execute_query_fetch_all

NUM_FRAMES = 10


@pytest.mark.notparallel
class SetExecutorTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.evadb = get_evadb_for_testing()
cls.evadb.catalog().reset()

@classmethod
def tearDownClass(cls):
pass

# integration test
def test_set_execution(self):
execute_query_fetch_all(self.evadb, "SET OPENAIKEY = 'ABCD';")
current_config_value = self.evadb.config.get_value("default", "OPENAIKEY")

self.assertEqual("ABCD", current_config_value)
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ def test_check_plan_equality(self):
extract_object_plan = LogicalExtractObject(
MagicMock(), MagicMock(), MagicMock(), MagicMock()
)

create_plan.append_child(create_function_plan)

plans.append(dummy_plan)
Expand Down
34 changes: 34 additions & 0 deletions test/unit_tests/parser/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from evadb.parser.parser import Parser
from evadb.parser.rename_statement import RenameTableStatement
from evadb.parser.select_statement import SelectStatement
from evadb.parser.set_statement import SetStatement
from evadb.parser.statement import AbstractStatement, StatementType
from evadb.parser.table_ref import JoinNode, TableInfo, TableRef, TableValuedExpression
from evadb.parser.types import (
Expand Down Expand Up @@ -693,6 +694,39 @@ def test_delete_statement(self):

self.assertEqual(delete_stmt, expected_stmt)

def test_set_statement(self):
parser = Parser()
set_statement = """SET OPENAIKEY = 'ABCD'"""
evadb_statement_list = parser.parse(set_statement)

self.assertIsInstance(evadb_statement_list, list)
self.assertEqual(len(evadb_statement_list), 1)
self.assertEqual(evadb_statement_list[0].stmt_type, StatementType.SET)

set_stmt = evadb_statement_list[0]

expected_stmt = SetStatement(
"OPENAIKEY", ConstantValueExpression("ABCD", ColumnType.TEXT)
)

self.assertEqual(set_stmt, expected_stmt)

# TESTING 'TO' IN PLACE OF '='
set_statement = """SET OPENAIKEY TO 'ABCD'"""
evadb_statement_list = parser.parse(set_statement)

self.assertIsInstance(evadb_statement_list, list)
self.assertEqual(len(evadb_statement_list), 1)
self.assertEqual(evadb_statement_list[0].stmt_type, StatementType.SET)

set_stmt = evadb_statement_list[0]

expected_stmt = SetStatement(
"OPENAIKEY", ConstantValueExpression("ABCD", ColumnType.TEXT)
)

self.assertEqual(set_stmt, expected_stmt)

def test_create_predict_function_statement(self):
parser = Parser()
create_func_query = """
Expand Down

0 comments on commit 27e2635

Please sign in to comment.