-
Notifications
You must be signed in to change notification settings - Fork 263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use statement #936
Use statement #936
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# 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 typing import Iterator | ||
|
||
import pandas as pd | ||
from sqlalchemy import create_engine | ||
|
||
from evadb.catalog.catalog_utils import generate_sqlalchemy_conn_str | ||
from evadb.database import EvaDBDatabase | ||
from evadb.executor.abstract_executor import AbstractExecutor | ||
from evadb.models.storage.batch import Batch | ||
from evadb.plan_nodes.native_plan import SQLAlchemyPlan | ||
|
||
|
||
class SQLAlchemyExecutor(AbstractExecutor): | ||
""" | ||
Execute SQL through SQLAlchemy engine. | ||
""" | ||
|
||
def __init__(self, db: EvaDBDatabase, node: SQLAlchemyPlan): | ||
super().__init__(db, node) | ||
self._database_name = node.database_name | ||
self._query_string = node.query_string | ||
|
||
def exec(self, *args, **kwargs) -> Iterator[Batch]: | ||
db_catalog_entry = self.db.catalog().get_database_catalog_entry( | ||
self._database_name | ||
) | ||
|
||
conn_str = generate_sqlalchemy_conn_str( | ||
db_catalog_entry.engine, | ||
db_catalog_entry.params, | ||
) | ||
|
||
engine = create_engine(conn_str) | ||
|
||
with engine.connect() as con: | ||
if "SELECT" in self._query_string or "select" in self._query_string: | ||
yield Batch(pd.read_sql(self._query_string, engine)) | ||
else: | ||
con.execute(self._query_string) | ||
yield Batch(pd.DataFrame({"status": ["Ok"]})) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,6 +62,7 @@ class OperatorType(IntEnum): | |
LOGICAL_APPLY_AND_MERGE = auto() | ||
LOGICAL_EXTRACT_OBJECT = auto() | ||
LOGICAL_VECTOR_INDEX_SCAN = auto() | ||
LOGICAL_USE = auto() | ||
LOGICALDELIMITER = auto() | ||
|
||
|
||
|
@@ -1239,3 +1240,37 @@ def __hash__(self) -> int: | |
self.search_query_expr, | ||
) | ||
) | ||
|
||
|
||
class LogicalUse(Operator): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you also bypass optimizer for use? Just to simplify the code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will push the code to take care of this |
||
def __init__(self, database_name: str, query_string: str, children: List = None): | ||
super().__init__(OperatorType.LOGICAL_USE, children) | ||
self._database_name = database_name | ||
self._query_string = query_string | ||
|
||
@property | ||
def database_name(self): | ||
return self._database_name | ||
|
||
@property | ||
def query_string(self): | ||
return self._query_string | ||
|
||
def __eq__(self, other): | ||
is_subtree_equal = super().__eq__(other) | ||
if not isinstance(other, LogicalUse): | ||
return False | ||
return ( | ||
is_subtree_equal | ||
and self.database_name == other.database_name | ||
and self.query_string == other.query_string | ||
) | ||
|
||
def __hash__(self) -> int: | ||
return hash( | ||
( | ||
super().__hash__(), | ||
self.database_name, | ||
self.query_string, | ||
) | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
start: (sql_statement? ";")+ | ||
|
||
sql_statement: ddl_statement | dml_statement | utility_statement | ||
sql_statement: ddl_statement | dml_statement | utility_statement | context_statement | ||
|
||
ddl_statement: create_database | create_table | create_index | create_udf | ||
| drop_database | drop_table | drop_udf | drop_index | rename_table | ||
|
@@ -12,6 +12,8 @@ dml_statement: select_statement | insert_statement | update_statement | |
|
||
utility_statement: describe_statement | show_statement | help_statement | explain_statement | ||
|
||
context_statement: use_statement | ||
|
||
// Data Definition Language | ||
|
||
// Create statements | ||
|
@@ -172,11 +174,19 @@ explain_statement: EXPLAIN explainable_statement | |
|
||
explainable_statement : select_statement | insert_statement | update_statement | delete_statement | create_table | ||
|
||
// Context Statements | ||
|
||
use_statement: USE database_name "{" query_string "}" // One shortcoming that query string cannot have parenthesis | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the limitation of the lark or ? Why we can not have parenthesis? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will remove this comment. The native query can have parenthesis after I change to use curly bracket if that makes sense // now
USE postgres {
// some query
}
// before
USE postgres (
// some query
) If I use the parenthesis and the native query has arbitrary parentheses as well, I have some trouble of coming up with a grammar that works for all cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me take care of it |
||
|
||
// Common Clauses | ||
|
||
// DB Objects | ||
|
||
query_string: QUERY_STRING | ||
|
||
full_id: uid dotted_id? | ||
|
||
database_name: full_id | ||
|
||
table_name: full_id | ||
|
||
|
@@ -360,6 +370,7 @@ PARAMETERS: "PARAMETERS"i | |
PRIMARY: "PRIMARY"i | ||
REFERENCES: "REFERENCES"i | ||
RENAME: "RENAME"i | ||
USE: "USE"i | ||
SAMPLE: "SAMPLE"i | ||
IFRAMES: "IFRAMES"i | ||
AUDIORATE: "AUDIORATE"i | ||
|
@@ -556,6 +567,7 @@ ID_LITERAL: /[A-Za-z_$0-9]*?[A-Za-z_$]+?[A-Za-z_$0-9]*/ | |
DQUOTA_STRING: /"[^";]*"/ | ||
SQUOTA_STRING: /'[^';]*'/ | ||
BQUOTA_STRING: /`[^'`]*`/ | ||
QUERY_STRING: /[^{};]+/ | ||
DEC_DIGIT: /[0-9]/ | ||
|
||
// LARK | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# 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 import Tree | ||
|
||
from evadb.parser.use_statement import UseStatement | ||
|
||
|
||
class Use: | ||
def use_statement(self, tree): | ||
for child in tree.children: | ||
if isinstance(child, Tree): | ||
if child.data == "database_name": | ||
database_name = self.visit(child) | ||
if child.data == "query_string": | ||
query_string = self.visit(child) | ||
return UseStatement(database_name, query_string) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make this robust for cases where select is used for creating tables etc. Also, if query contains
Select
etcThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the output for
CREATE TABLE
from sqlalchemy? Is it possible to simply execute the query and yield whatever output sqlalchemy yields in dataframe type?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we replicate this? https://github.com/mindsdb/mindsdb/blame/b417ecff78cca2e7f09d4e7daa3461595c77ad0d/mindsdb/integrations/handlers/postgres_handler/postgres_handler.py
I'm using it in the other PR for testing the connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sqlalchemy/sqlalchemy#5433 (comment)
Based on what I see, it is still true for this API for latest SQLAlchemy. There is no rows returned besides
SELECT
.