From d0cb268e9dfbc2f9afc7b30d1ac1e19f66b775ef Mon Sep 17 00:00:00 2001 From: Andy Xu Date: Thu, 21 Sep 2023 21:36:40 -0700 Subject: [PATCH] Add support for function calls without input arguments: `FUNC()` (#1185) - [x] Implementation - [x] Testcases --- evadb/functions/function_bootstrap_queries.py | 10 ++++++- evadb/parser/evadb.lark | 2 +- evadb/parser/lark_visitor/_functions.py | 2 +- .../short/test_select_executor.py | 8 +++++ test/util.py | 29 ++++++++++++++++++- 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/evadb/functions/function_bootstrap_queries.py b/evadb/functions/function_bootstrap_queries.py index cd75e9a65..e51e75638 100644 --- a/evadb/functions/function_bootstrap_queries.py +++ b/evadb/functions/function_bootstrap_queries.py @@ -49,6 +49,13 @@ EvaDB_INSTALLATION_DIR ) +DummyNoInputFunction_function_query = """CREATE FUNCTION + IF NOT EXISTS DummyNoInputFunction + IMPL '{}/../test/util.py'; + """.format( + EvaDB_INSTALLATION_DIR +) + fuzzy_function_query = """CREATE FUNCTION IF NOT EXISTS FuzzDistance INPUT (Input_Array1 NDARRAY ANYTYPE, Input_Array2 NDARRAY ANYTYPE) OUTPUT (distance FLOAT(32, 7)) @@ -197,7 +204,7 @@ def init_builtin_functions(db: EvaDBDatabase, mode: str = "debug") -> None: In 'release' mode, only release functions are loaded. In addition, in 'debug' mode, the function loads a smaller model to accelerate the test suite time. - Args: + Args:G mode (str, optional): The mode for loading functions, either 'debug' or 'release'. Defaults to 'debug'. @@ -242,6 +249,7 @@ def init_builtin_functions(db: EvaDBDatabase, mode: str = "debug") -> None: DummyObjectDetector_function_query, DummyMultiObjectDetector_function_query, DummyFeatureExtractor_function_query, + DummyNoInputFunction_function_query, ] ) diff --git a/evadb/parser/evadb.lark b/evadb/parser/evadb.lark index 635dff94f..add5c026f 100644 --- a/evadb/parser/evadb.lark +++ b/evadb/parser/evadb.lark @@ -272,7 +272,7 @@ or_replace: OR REPLACE function_call: function ->function_call | aggregate_windowed_function ->aggregate_function_call -function: simple_id "(" (STAR | function_args) ")" dotted_id? +function: simple_id "(" (STAR | function_args)? ")" dotted_id? aggregate_windowed_function: aggregate_function_name "(" function_arg ")" | COUNT "(" (STAR | function_arg) ")" diff --git a/evadb/parser/lark_visitor/_functions.py b/evadb/parser/lark_visitor/_functions.py index 7baa4e66b..a3b5a868a 100644 --- a/evadb/parser/lark_visitor/_functions.py +++ b/evadb/parser/lark_visitor/_functions.py @@ -30,7 +30,7 @@ class Functions: def function(self, tree): function_name = None function_output = None - function_args = None + function_args = [] for child in tree.children: if isinstance(child, Token): diff --git a/test/integration_tests/short/test_select_executor.py b/test/integration_tests/short/test_select_executor.py index 7dc1f757c..2e0f24fd8 100644 --- a/test/integration_tests/short/test_select_executor.py +++ b/test/integration_tests/short/test_select_executor.py @@ -434,3 +434,11 @@ def test_expression_tree_signature(self): signature, f"DummyMultiObjectDetector[{function_id}](MyVideo.data[{col_id}])", ) + + def test_function_with_no_input_arguments(self): + select_query = "SELECT DummyNoInputFunction();" + actual_batch = execute_query_fetch_all(self.evadb, select_query) + expected = Batch( + pd.DataFrame([{"dummynoinputfunction.label": "DummyNoInputFunction"}]) + ) + self.assertEqual(actual_batch, expected) diff --git a/test/util.py b/test/util.py index 732aabc15..d793d1431 100644 --- a/test/util.py +++ b/test/util.py @@ -33,7 +33,10 @@ from evadb.configuration.constants import EvaDB_DATABASE_DIR, EvaDB_INSTALLATION_DIR from evadb.database import init_evadb_instance from evadb.expression.function_expression import FunctionExpression -from evadb.functions.abstract.abstract_function import AbstractClassifierFunction +from evadb.functions.abstract.abstract_function import ( + AbstractClassifierFunction, + AbstractFunction, +) from evadb.functions.decorators import decorators from evadb.functions.decorators.io_descriptors.data_types import ( NumpyArray, @@ -636,3 +639,27 @@ def classify_one(self, frames: np.ndarray): i = int(frames[0][0][0][0]) - 1 label = self.labels[i % 2 + 1] return np.array([label]) + + +class DummyNoInputFunction(AbstractFunction): + @decorators.setup(cacheable=False, function_type="test", batchable=False) + def setup(self, *args, **kwargs): + pass + + @property + def name(self) -> str: + return "DummyNoInputFunction" + + @decorators.forward( + input_signatures=[], + output_signatures=[ + PandasDataframe( + columns=["label"], + column_types=[NdArrayType.STR], + column_shapes=[(None,)], + ) + ], + ) + def forward(self, df: pd.DataFrame) -> pd.DataFrame: + ret = pd.DataFrame([{"label": "DummyNoInputFunction"}]) + return ret