code: def _handle_drop_function(self, function_name: str, if_exists: bool):
      # check catalog if it already has this function entry
      if not self.catalog().get_function_catalog_entry_by_name(
          function_name
      ):
          err_msg = f"Function {function_name} does not exist, therefore cannot be dropped."
          if if_exists:
              logger.warning(err_msg)
              return Batch(pd.DataFrame([err_msg]))
          else:
              raise RuntimeError(err_msg)
      else:
          function_entry = (
              self.catalog().get_function_catalog_entry_by_name(
                  function_name
              )
          )
          for cache in function_entry.dep_caches:
              self.catalog().drop_function_cache_catalog_entry(cache)
  
          # todo also delete the indexes associated with the table
  
          self.catalog().delete_function_catalog_entry_by_name(
              function_name
          )
  
          return Batch(
              pd.DataFrame(
                  {f"Function {function_name} successfully dropped"},
                  index=[0],
              )
          )
  
file_path: evadb/evadb/executor/drop_object_executor.py
imports: import pandas
  from evadb.database import EvaDBDatabase
  from evadb.executor.abstract_executor import AbstractExecutor
  from evadb.executor.executor_utils import ExecutorError, handle_vector_store_params
  from evadb.models.storage.batch import Batch
  from evadb.parser.types import ObjectType
  from evadb.plan_nodes.drop_object_plan import DropObjectPlan
  from evadb.storage.storage_engine import StorageEngine
  from evadb.third_party.vector_stores.utils import VectorStoreFactory
  from evadb.utils.logging_manager import logger
label_desc: Use image node_id 4 for calling the DropObjectExecutor obj's underlying member method code with example usage: obj._handle_drop_function(function_name, if_exists) and returns: Batch, Batch