• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

georgia-tech-db / eva / #754

04 Sep 2023 09:54PM UTC coverage: 74.807% (-5.5%) from 80.336%
#754

push

circle-ci

jiashenC
update case

8727 of 11666 relevant lines covered (74.81%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.52
/evadb/optimizer/statement_to_opr_converter.py
1
# coding=utf-8
2
# Copyright 2018-2023 EvaDB
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
from evadb.expression.abstract_expression import AbstractExpression
1✔
16
from evadb.optimizer.operators import (
1✔
17
    LogicalCreate,
18
    LogicalCreateFunction,
19
    LogicalCreateIndex,
20
    LogicalDelete,
21
    LogicalDropObject,
22
    LogicalExplain,
23
    LogicalExtractObject,
24
    LogicalFilter,
25
    LogicalFunctionScan,
26
    LogicalGet,
27
    LogicalGroupBy,
28
    LogicalInsert,
29
    LogicalJoin,
30
    LogicalLimit,
31
    LogicalLoadData,
32
    LogicalOrderBy,
33
    LogicalProject,
34
    LogicalQueryDerivedGet,
35
    LogicalRename,
36
    LogicalSample,
37
    LogicalShow,
38
    LogicalUnion,
39
)
40
from evadb.optimizer.optimizer_utils import (
1✔
41
    column_definition_to_function_io,
42
    metadata_definition_to_function_metadata,
43
)
44
from evadb.parser.create_function_statement import CreateFunctionStatement
1✔
45
from evadb.parser.create_index_statement import CreateIndexStatement
1✔
46
from evadb.parser.create_statement import CreateTableStatement
1✔
47
from evadb.parser.delete_statement import DeleteTableStatement
1✔
48
from evadb.parser.drop_object_statement import DropObjectStatement
1✔
49
from evadb.parser.explain_statement import ExplainStatement
1✔
50
from evadb.parser.insert_statement import InsertTableStatement
1✔
51
from evadb.parser.load_statement import LoadDataStatement
1✔
52
from evadb.parser.rename_statement import RenameTableStatement
1✔
53
from evadb.parser.select_statement import SelectStatement
1✔
54
from evadb.parser.show_statement import ShowStatement
1✔
55
from evadb.parser.statement import AbstractStatement
1✔
56
from evadb.parser.table_ref import TableRef
1✔
57
from evadb.parser.types import FunctionType
1✔
58
from evadb.utils.logging_manager import logger
1✔
59

60

61
class StatementToPlanConverter:
1✔
62
    def __init__(self):
1✔
63
        self._plan = None
1✔
64

65
    def visit_table_ref(self, table_ref: TableRef):
1✔
66
        """Bind table ref object and convert to LogicalGet, LogicalJoin,
67
            LogicalFunctionScan, or LogicalQueryDerivedGet
68

69
        Arguments:
70
            table {TableRef} - - [Input table ref object created by the parser]
71
        """
72
        if table_ref.is_table_atom():
1✔
73
            # Table
74
            catalog_entry = table_ref.table.table_obj
1✔
75
            self._plan = LogicalGet(table_ref, catalog_entry, table_ref.alias)
1✔
76

77
        elif table_ref.is_table_valued_expr():
×
78
            tve = table_ref.table_valued_expr
×
79
            if tve.func_expr.name.lower() == str(FunctionType.EXTRACT_OBJECT).lower():
×
80
                self._plan = LogicalExtractObject(
×
81
                    detector=tve.func_expr.children[1],
82
                    tracker=tve.func_expr.children[2],
83
                    alias=table_ref.alias,
84
                    do_unnest=tve.do_unnest,
85
                )
86
            else:
87
                self._plan = LogicalFunctionScan(
×
88
                    func_expr=tve.func_expr,
89
                    alias=table_ref.alias,
90
                    do_unnest=tve.do_unnest,
91
                )
92

93
        elif table_ref.is_select():
×
94
            # NestedQuery
95
            self.visit_select(table_ref.select_statement)
×
96
            child_plan = self._plan
×
97
            self._plan = LogicalQueryDerivedGet(table_ref.alias)
×
98
            self._plan.append_child(child_plan)
×
99

100
        elif table_ref.is_join():
×
101
            join_node = table_ref.join_node
×
102
            join_plan = LogicalJoin(
×
103
                join_type=join_node.join_type,
104
                join_predicate=join_node.predicate,
105
            )
106
            self.visit_table_ref(join_node.left)
×
107
            join_plan.append_child(self._plan)
×
108
            self.visit_table_ref(join_node.right)
×
109
            join_plan.append_child(self._plan)
×
110
            self._plan = join_plan
×
111

112
        if table_ref.sample_freq:
1✔
113
            self._visit_sample(table_ref.sample_freq, table_ref.sample_type)
×
114

115
    def visit_select(self, statement: SelectStatement):
1✔
116
        """converter for select statement
117

118
        Arguments:
119
            statement {SelectStatement} - - [input select statement]
120
        """
121

122
        table_ref = statement.from_table
1✔
123
        if table_ref is None:
1✔
124
            logger.error("From entry missing in select statement")
1✔
125
            return None
1✔
126

127
        self.visit_table_ref(table_ref)
1✔
128

129
        # Filter Operator
130
        predicate = statement.where_clause
1✔
131
        if predicate is not None:
1✔
132
            self._visit_select_predicate(predicate)
1✔
133

134
        # union
135
        if statement.union_link is not None:
1✔
136
            self._visit_union(statement.union_link, statement.union_all)
1✔
137

138
        # TODO ACTION: Group By
139
        if statement.groupby_clause is not None:
1✔
140
            self._visit_groupby(statement.groupby_clause)
1✔
141

142
        if statement.orderby_list is not None:
1✔
143
            self._visit_orderby(statement.orderby_list)
1✔
144

145
        if statement.limit_count is not None:
1✔
146
            self._visit_limit(statement.limit_count)
1✔
147

148
        # Projection operator
149
        select_columns = statement.target_list
1✔
150

151
        if select_columns is not None:
1✔
152
            self._visit_projection(select_columns)
1✔
153

154
    def _visit_sample(self, sample_freq, sample_type):
1✔
155
        sample_opr = LogicalSample(sample_freq, sample_type)
×
156
        sample_opr.append_child(self._plan)
×
157
        self._plan = sample_opr
×
158

159
    def _visit_groupby(self, groupby_clause):
1✔
160
        groupby_opr = LogicalGroupBy(groupby_clause)
1✔
161
        groupby_opr.append_child(self._plan)
1✔
162
        self._plan = groupby_opr
1✔
163

164
    def _visit_orderby(self, orderby_list):
1✔
165
        # orderby_list structure: List[(TupleValueExpression, EnumInt), ...]
166
        orderby_opr = LogicalOrderBy(orderby_list)
1✔
167
        orderby_opr.append_child(self._plan)
1✔
168
        self._plan = orderby_opr
1✔
169

170
    def _visit_limit(self, limit_count):
1✔
171
        limit_opr = LogicalLimit(limit_count)
1✔
172
        limit_opr.append_child(self._plan)
1✔
173
        self._plan = limit_opr
1✔
174

175
    def _visit_union(self, target, all):
1✔
176
        left_child_plan = self._plan
×
177
        self.visit_select(target)
×
178
        right_child_plan = self._plan
×
179
        self._plan = LogicalUnion(all=all)
×
180
        self._plan.append_child(left_child_plan)
×
181
        self._plan.append_child(right_child_plan)
×
182

183
    def _visit_projection(self, select_columns):
1✔
184
        projection_opr = LogicalProject(select_columns)
1✔
185
        projection_opr.append_child(self._plan)
1✔
186
        self._plan = projection_opr
1✔
187

188
    def _visit_select_predicate(self, predicate: AbstractExpression):
1✔
189
        filter_opr = LogicalFilter(predicate)
1✔
190
        filter_opr.append_child(self._plan)
1✔
191
        self._plan = filter_opr
1✔
192

193
    def visit_insert(self, statement: AbstractStatement):
1✔
194
        """Converter for parsed insert statement
195

196
        Arguments:
197
            statement {AbstractStatement} - - [input insert statement]
198
        """
199
        # not removing previous commented code
200
        insert_data_opr = LogicalInsert(
×
201
            statement.table_ref,
202
            statement.column_list,
203
            statement.value_list,
204
        )
205
        self._plan = insert_data_opr
×
206

207
        """
×
208
        table_ref = statement.table
209
        table_metainfo = bind_dataset(table_ref.table)
210
        if table_metainfo is None:
211
            # Create a new metadata object
212
            table_metainfo = create_video_metadata(table_ref.table.table_name)
213

214
        # populate self._column_map
215
        self._populate_column_map(table_metainfo)
216

217
        # Bind column_list
218
        bind_columns_expr(statement.column_list, self._column_map)
219

220
        # Nothing to be done for values as we add support for other variants of
221
        # insert we will handle them
222
        value_list = statement.value_list
223

224
        # Ready to create Logical node
225
        insert_opr = LogicalInsert(
226
            table_ref, table_metainfo, statement.column_list, value_list)
227
        self._plan = insert_opr
228
        """
229

230
    def visit_create(self, statement: AbstractStatement):
1✔
231
        """Converter for parsed insert Statement
232

233
        Arguments:
234
            statement {AbstractStatement} - - [Create statement]
235
        """
236
        table_info = statement.table_info
1✔
237
        if table_info is None:
1✔
238
            logger.error("Missing Table Name In Create Statement")
×
239

240
        create_opr = LogicalCreate(
1✔
241
            table_info, statement.column_list, statement.if_not_exists
242
        )
243

244
        if statement.query is not None:
1✔
245
            self.visit_select(statement.query)
×
246
            create_opr.append_child(self._plan)
×
247
        self._plan = create_opr
1✔
248

249
    def visit_rename(self, statement: RenameTableStatement):
1✔
250
        """Converter for parsed rename statement
251
        Arguments:
252
            statement(RenameTableStatement): [Rename statement]
253
        """
254
        rename_opr = LogicalRename(statement.old_table_ref, statement.new_table_name)
×
255
        self._plan = rename_opr
×
256

257
    def visit_create_function(self, statement: CreateFunctionStatement):
1✔
258
        """Converter for parsed create function statement
259

260
        Arguments:
261
            statement {CreateFunctionStatement} - - CreateFunctionStatement
262
        """
263
        annotated_inputs = column_definition_to_function_io(statement.inputs, True)
1✔
264
        annotated_outputs = column_definition_to_function_io(statement.outputs, False)
1✔
265
        annotated_metadata = metadata_definition_to_function_metadata(
1✔
266
            statement.metadata
267
        )
268

269
        create_function_opr = LogicalCreateFunction(
1✔
270
            statement.name,
271
            statement.if_not_exists,
272
            annotated_inputs,
273
            annotated_outputs,
274
            statement.impl_path,
275
            statement.function_type,
276
            annotated_metadata,
277
        )
278

279
        if statement.query is not None:
1✔
280
            self.visit_select(statement.query)
×
281
            create_function_opr.append_child(self._plan)
×
282

283
        self._plan = create_function_opr
1✔
284

285
    def visit_drop_object(self, statement: DropObjectStatement):
1✔
286
        self._plan = LogicalDropObject(
1✔
287
            statement.object_type, statement.name, statement.if_exists
288
        )
289

290
    def visit_load_data(self, statement: LoadDataStatement):
1✔
291
        """Converter for parsed load data statement
292
        Arguments:
293
            statement(LoadDataStatement): [Load data statement]
294
        """
295
        load_data_opr = LogicalLoadData(
1✔
296
            statement.table_info,
297
            statement.path,
298
            statement.column_list,
299
            statement.file_options,
300
        )
301
        self._plan = load_data_opr
1✔
302

303
    def visit_show(self, statement: ShowStatement):
1✔
304
        show_opr = LogicalShow(statement.show_type)
1✔
305
        self._plan = show_opr
1✔
306

307
    def visit_explain(self, statement: ExplainStatement):
1✔
308
        explain_opr = LogicalExplain([self.visit(statement.explainable_stmt)])
×
309
        self._plan = explain_opr
×
310

311
    def visit_create_index(self, statement: CreateIndexStatement):
1✔
312
        create_index_opr = LogicalCreateIndex(
×
313
            statement.name,
314
            statement.table_ref,
315
            statement.col_list,
316
            statement.vector_store_type,
317
            statement.function,
318
        )
319
        self._plan = create_index_opr
×
320

321
    def visit_delete(self, statement: DeleteTableStatement):
1✔
322
        delete_opr = LogicalDelete(
×
323
            statement.table_ref,
324
            statement.where_clause,
325
        )
326
        self._plan = delete_opr
×
327

328
    def visit(self, statement: AbstractStatement):
1✔
329
        """Based on the instance of the statement the corresponding
330
           visit is called.
331
           The logic is hidden from client.
332

333
        Arguments:
334
            statement {AbstractStatement} - - [Input statement]
335
        """
336
        if isinstance(statement, SelectStatement):
1✔
337
            self.visit_select(statement)
1✔
338
        elif isinstance(statement, InsertTableStatement):
1✔
339
            self.visit_insert(statement)
1✔
340
        elif isinstance(statement, CreateTableStatement):
1✔
341
            self.visit_create(statement)
1✔
342
        elif isinstance(statement, RenameTableStatement):
1✔
343
            self.visit_rename(statement)
1✔
344
        elif isinstance(statement, CreateFunctionStatement):
1✔
345
            self.visit_create_function(statement)
1✔
346
        elif isinstance(statement, DropObjectStatement):
1✔
347
            self.visit_drop_object(statement)
1✔
348
        elif isinstance(statement, LoadDataStatement):
1✔
349
            self.visit_load_data(statement)
1✔
350
        elif isinstance(statement, ShowStatement):
1✔
351
            self.visit_show(statement)
1✔
352
        elif isinstance(statement, ExplainStatement):
1✔
353
            self.visit_explain(statement)
1✔
354
        elif isinstance(statement, CreateIndexStatement):
1✔
355
            self.visit_create_index(statement)
1✔
356
        elif isinstance(statement, DeleteTableStatement):
×
357
            self.visit_delete(statement)
×
358
        return self._plan
1✔
359

360
    @property
1✔
361
    def plan(self):
1✔
362
        return self._plan
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc