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

georgia-tech-db / eva / #758

04 Sep 2023 08:37PM UTC coverage: 0.0% (-78.3%) from 78.333%
#758

push

circle-ci

hershd23
Increased underline length in at line 75 in text_summarization.rst
	modified:   docs/source/benchmarks/text_summarization.rst

0 of 11303 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/evadb/optimizer/operators.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 collections import deque
×
16
from enum import IntEnum, auto
×
17
from pathlib import Path
×
18
from typing import Any, List
×
19

20
from evadb.catalog.catalog_type import VectorStoreType
×
21
from evadb.catalog.models.column_catalog import ColumnCatalogEntry
×
22
from evadb.catalog.models.table_catalog import TableCatalogEntry
×
23
from evadb.catalog.models.udf_io_catalog import UdfIOCatalogEntry
×
24
from evadb.catalog.models.udf_metadata_catalog import UdfMetadataCatalogEntry
×
25
from evadb.expression.abstract_expression import AbstractExpression
×
26
from evadb.expression.constant_value_expression import ConstantValueExpression
×
27
from evadb.expression.function_expression import FunctionExpression
×
28
from evadb.parser.alias import Alias
×
29
from evadb.parser.create_statement import ColumnDefinition
×
30
from evadb.parser.table_ref import TableInfo, TableRef
×
31
from evadb.parser.types import JoinType, ObjectType, ShowType
×
32

33

34
class OperatorType(IntEnum):
×
35
    """
36
    Manages enums for all the operators supported
37
    """
38

39
    DUMMY = auto()
×
40
    LOGICALEXCHANGE = auto()
×
41
    LOGICALGET = auto()
×
42
    LOGICALFILTER = auto()
×
43
    LOGICALPROJECT = auto()
×
44
    LOGICALINSERT = auto()
×
45
    LOGICALDELETE = auto()
×
46
    LOGICALCREATE = auto()
×
47
    LOGICALRENAME = auto()
×
48
    LOGICAL_DROP_OBJECT = auto()
×
49
    LOGICALCREATEUDF = auto()
×
50
    LOGICALLOADDATA = auto()
×
51
    LOGICALQUERYDERIVEDGET = auto()
×
52
    LOGICALUNION = auto()
×
53
    LOGICALGROUPBY = auto()
×
54
    LOGICALORDERBY = auto()
×
55
    LOGICALLIMIT = auto()
×
56
    LOGICALSAMPLE = auto()
×
57
    LOGICALJOIN = auto()
×
58
    LOGICALFUNCTIONSCAN = auto()
×
59
    LOGICAL_SHOW = auto()
×
60
    LOGICALEXPLAIN = auto()
×
61
    LOGICALCREATEINDEX = auto()
×
62
    LOGICAL_APPLY_AND_MERGE = auto()
×
63
    LOGICAL_EXTRACT_OBJECT = auto()
×
64
    LOGICAL_VECTOR_INDEX_SCAN = auto()
×
65
    LOGICAL_USE = auto()
×
66
    LOGICALDELIMITER = auto()
×
67

68

69
class Operator:
×
70
    """Base class for logical plan of operators
71
    Arguments:
72
        op_type: {OperatorType} -- {the opr type held by this node}
73
        children: {List} -- {the list of operator children for this node}
74
    """
75

76
    def __init__(self, op_type: OperatorType, children=None):
×
77
        self._opr_type = op_type
×
78
        self._children = children or []
×
79

80
    @property
×
81
    def children(self):
×
82
        return self._children
×
83

84
    @children.setter
×
85
    def children(self, children):
×
86
        self._children = children
×
87

88
    @property
×
89
    def opr_type(self):
×
90
        return self._opr_type
×
91

92
    def append_child(self, child: "Operator"):
×
93
        self.children.append(child)
×
94

95
    def clear_children(self):
×
96
        self.children = []
×
97

98
    def __str__(self) -> str:
×
99
        return "%s[%s](%s)" % (
×
100
            type(self).__name__,
101
            hex(id(self)),
102
            ", ".join("%s=%s" % item for item in vars(self).items()),
103
        )
104

105
    def __eq__(self, other):
×
106
        is_subtree_equal = True
×
107
        if not isinstance(other, Operator):
×
108
            return False
×
109
        if len(self.children) != len(other.children):
×
110
            return False
×
111
        for child1, child2 in zip(self.children, other.children):
×
112
            is_subtree_equal = is_subtree_equal and (child1 == child2)
×
113
        return is_subtree_equal
×
114

115
    def is_logical(self):
×
116
        return self._opr_type < OperatorType.LOGICALDELIMITER
×
117

118
    def __hash__(self) -> int:
×
119
        return hash((self.opr_type, tuple(self.children)))
×
120

121
    def __copy__(self):
×
122
        # deepcopy the children
123
        cls = self.__class__
×
124
        result = cls.__new__(cls)
×
125
        for k, v in self.__dict__.items():
×
126
            if k == "_children":
×
127
                setattr(result, k, [])
×
128
            else:
129
                setattr(result, k, v)
×
130
        return result
×
131

132
    def bfs(self):
×
133
        """Returns a generator which visits all nodes in operator tree in
134
        breadth-first search (BFS) traversal order.
135

136
        Returns:
137
            the generator object.
138
        """
139
        queue = deque([self])
×
140
        while queue:
×
141
            node = queue.popleft()
×
142
            yield node
×
143
            for child in node.children:
×
144
                queue.append(child)
×
145

146
    def find_all(self, operator_type: Any):
×
147
        """Returns a generator which visits all the nodes in operator tree and yields one that matches the passed `operator_type`.
148

149
        Args:
150
            operator_type (Any): operator type to match with
151

152
        Returns:
153
            the generator object.
154
        """
155

156
        for node in self.bfs():
×
157
            if isinstance(node, operator_type):
×
158
                yield node
×
159

160

161
class Dummy(Operator):
×
162
    """
163
    Acts as a placeholder for matching any operator in optimizer.
164
    It tracks the group_id of the matching operator.
165
    """
166

167
    def __init__(self, group_id: int, opr: Operator):
×
168
        super().__init__(OperatorType.DUMMY, None)
×
169
        self.group_id = group_id
×
170
        self.opr = opr
×
171

172

173
class LogicalGet(Operator):
×
174
    def __init__(
×
175
        self,
176
        video: TableRef,
177
        table_obj: TableCatalogEntry,
178
        alias: str,
179
        predicate: AbstractExpression = None,
180
        target_list: List[AbstractExpression] = None,
181
        sampling_rate: int = None,
182
        sampling_type: str = None,
183
        chunk_params: dict = {},
184
        children=None,
185
    ):
186
        self._video = video
×
187
        self._table_obj = table_obj
×
188
        self._alias = alias
×
189
        self._predicate = predicate
×
190
        self._target_list = target_list
×
191
        self._sampling_rate = sampling_rate
×
192
        self._sampling_type = sampling_type
×
193
        self.chunk_params = chunk_params
×
194
        super().__init__(OperatorType.LOGICALGET, children)
×
195

196
    @property
×
197
    def video(self):
×
198
        return self._video
×
199

200
    @property
×
201
    def table_obj(self):
×
202
        return self._table_obj
×
203

204
    @property
×
205
    def alias(self):
×
206
        return self._alias
×
207

208
    @property
×
209
    def predicate(self):
×
210
        return self._predicate
×
211

212
    @property
×
213
    def target_list(self):
×
214
        return self._target_list
×
215

216
    @property
×
217
    def sampling_rate(self):
×
218
        return self._sampling_rate
×
219

220
    @property
×
221
    def sampling_type(self):
×
222
        return self._sampling_type
×
223

224
    def __eq__(self, other):
×
225
        is_subtree_equal = super().__eq__(other)
×
226
        if not isinstance(other, LogicalGet):
×
227
            return False
×
228
        return (
×
229
            is_subtree_equal
230
            and self.video == other.video
231
            and self.table_obj == other.table_obj
232
            and self.alias == other.alias
233
            and self.predicate == other.predicate
234
            and self.target_list == other.target_list
235
            and self.sampling_rate == other.sampling_rate
236
            and self.sampling_type == other.sampling_type
237
            and self.chunk_params == other.chunk_params
238
        )
239

240
    def __hash__(self) -> int:
×
241
        return hash(
×
242
            (
243
                super().__hash__(),
244
                self.alias,
245
                self.video,
246
                self.table_obj,
247
                self.predicate,
248
                tuple(self.target_list or []),
249
                self.sampling_rate,
250
                self.sampling_type,
251
                frozenset(self.chunk_params.items()),
252
            )
253
        )
254

255

256
class LogicalQueryDerivedGet(Operator):
×
257
    def __init__(
×
258
        self,
259
        alias: str,
260
        predicate: AbstractExpression = None,
261
        target_list: List[AbstractExpression] = None,
262
        children: List = None,
263
    ):
264
        super().__init__(OperatorType.LOGICALQUERYDERIVEDGET, children=children)
×
265
        self._alias = alias
×
266
        self.predicate = predicate
×
267
        self.target_list = target_list or []
×
268

269
    @property
×
270
    def alias(self):
×
271
        return self._alias
×
272

273
    def __eq__(self, other):
×
274
        is_subtree_equal = super().__eq__(other)
×
275
        if not isinstance(other, LogicalQueryDerivedGet):
×
276
            return False
×
277
        return (
×
278
            is_subtree_equal
279
            and self.predicate == other.predicate
280
            and self.target_list == other.target_list
281
            and self.alias == other.alias
282
        )
283

284
    def __hash__(self) -> int:
×
285
        return hash(
×
286
            (
287
                super().__hash__(),
288
                self.alias,
289
                self.predicate,
290
                tuple(self.target_list),
291
            )
292
        )
293

294

295
class LogicalFilter(Operator):
×
296
    def __init__(self, predicate: AbstractExpression, children=None):
×
297
        self._predicate = predicate
×
298
        super().__init__(OperatorType.LOGICALFILTER, children)
×
299

300
    @property
×
301
    def predicate(self):
×
302
        return self._predicate
×
303

304
    def __eq__(self, other):
×
305
        is_subtree_equal = super().__eq__(other)
×
306
        if not isinstance(other, LogicalFilter):
×
307
            return False
×
308
        return is_subtree_equal and self.predicate == other.predicate
×
309

310
    def __hash__(self) -> int:
×
311
        return hash((super().__hash__(), self.predicate))
×
312

313

314
class LogicalProject(Operator):
×
315
    def __init__(self, target_list: List[AbstractExpression], children=None):
×
316
        super().__init__(OperatorType.LOGICALPROJECT, children)
×
317
        self._target_list = target_list
×
318

319
    @property
×
320
    def target_list(self):
×
321
        return self._target_list
×
322

323
    def __eq__(self, other):
×
324
        is_subtree_equal = super().__eq__(other)
×
325
        if not isinstance(other, LogicalProject):
×
326
            return False
×
327
        return is_subtree_equal and self.target_list == other.target_list
×
328

329
    def __hash__(self) -> int:
×
330
        return hash((super().__hash__(), tuple(self.target_list)))
×
331

332

333
class LogicalGroupBy(Operator):
×
334
    def __init__(self, groupby_clause: ConstantValueExpression, children: List = None):
×
335
        super().__init__(OperatorType.LOGICALGROUPBY, children)
×
336
        self._groupby_clause = groupby_clause
×
337

338
    @property
×
339
    def groupby_clause(self):
×
340
        return self._groupby_clause
×
341

342
    def __eq__(self, other):
×
343
        is_subtree_equal = super().__eq__(other)
×
344
        if not isinstance(other, LogicalGroupBy):
×
345
            return False
×
346
        return is_subtree_equal and self.groupby_clause == other.groupby_clause
×
347

348
    def __hash__(self) -> int:
×
349
        return hash((super().__hash__(), self.groupby_clause))
×
350

351

352
class LogicalOrderBy(Operator):
×
353
    def __init__(self, orderby_list: List, children: List = None):
×
354
        super().__init__(OperatorType.LOGICALORDERBY, children)
×
355
        self._orderby_list = orderby_list
×
356

357
    @property
×
358
    def orderby_list(self):
×
359
        return self._orderby_list
×
360

361
    def __eq__(self, other):
×
362
        is_subtree_equal = super().__eq__(other)
×
363
        if not isinstance(other, LogicalOrderBy):
×
364
            return False
×
365
        return is_subtree_equal and self.orderby_list == other.orderby_list
×
366

367
    def __hash__(self) -> int:
×
368
        return hash((super().__hash__(), tuple(self.orderby_list)))
×
369

370

371
class LogicalLimit(Operator):
×
372
    def __init__(self, limit_count: ConstantValueExpression, children: List = None):
×
373
        super().__init__(OperatorType.LOGICALLIMIT, children)
×
374
        self._limit_count = limit_count
×
375

376
    @property
×
377
    def limit_count(self):
×
378
        return self._limit_count
×
379

380
    def __eq__(self, other):
×
381
        is_subtree_equal = super().__eq__(other)
×
382
        if not isinstance(other, LogicalLimit):
×
383
            return False
×
384
        return is_subtree_equal and self.limit_count == other.limit_count
×
385

386
    def __hash__(self) -> int:
×
387
        return hash((super().__hash__(), self.limit_count))
×
388

389

390
class LogicalSample(Operator):
×
391
    def __init__(
×
392
        self,
393
        sample_freq: ConstantValueExpression,
394
        sample_type: ConstantValueExpression,
395
        children: List = None,
396
    ):
397
        super().__init__(OperatorType.LOGICALSAMPLE, children)
×
398
        self._sample_freq = sample_freq
×
399
        self._sample_type = sample_type
×
400

401
    @property
×
402
    def sample_freq(self):
×
403
        return self._sample_freq
×
404

405
    @property
×
406
    def sample_type(self):
×
407
        return self._sample_type
×
408

409
    def __eq__(self, other):
×
410
        is_subtree_equal = super().__eq__(other)
×
411
        if not isinstance(other, LogicalSample):
×
412
            return False
×
413
        return (
×
414
            is_subtree_equal
415
            and self.sample_freq == other.sample_freq
416
            and self.sample_type == other.sample_type
417
        )
418

419
    def __hash__(self) -> int:
×
420
        return hash((super().__hash__(), self.sample_freq, self.sample_type))
×
421

422

423
class LogicalUnion(Operator):
×
424
    def __init__(self, all: bool, children: List = None):
×
425
        super().__init__(OperatorType.LOGICALUNION, children)
×
426
        self._all = all
×
427

428
    @property
×
429
    def all(self):
×
430
        return self._all
×
431

432
    def __eq__(self, other):
×
433
        is_subtree_equal = super().__eq__(other)
×
434
        if not isinstance(other, LogicalUnion):
×
435
            return False
×
436
        return is_subtree_equal and self.all == other.all
×
437

438
    def __hash__(self) -> int:
×
439
        return hash((super().__hash__(), self.all))
×
440

441

442
class LogicalInsert(Operator):
×
443
    """[Logical Node for Insert operation]
444

445
    Arguments:
446
        table(TableCatalogEntry): table to insert data into
447
        column_list{List[AbstractExpression]}:
448
            [After binding annotated column_list]
449
        value_list{List[AbstractExpression]}:
450
            [value list to insert]
451
    """
452

453
    def __init__(
×
454
        self,
455
        table: TableCatalogEntry,
456
        column_list: List[AbstractExpression],
457
        value_list: List[AbstractExpression],
458
        children: List = None,
459
    ):
460
        super().__init__(OperatorType.LOGICALINSERT, children)
×
461
        self._table = table
×
462
        self._column_list = column_list
×
463
        self._value_list = value_list
×
464

465
    @property
×
466
    def table(self):
×
467
        return self._table
×
468

469
    @property
×
470
    def value_list(self):
×
471
        return self._value_list
×
472

473
    @property
×
474
    def column_list(self):
×
475
        return self._column_list
×
476

477
    def __eq__(self, other):
×
478
        is_subtree_equal = super().__eq__(other)
×
479
        if not isinstance(other, LogicalInsert):
×
480
            return False
×
481
        return (
×
482
            is_subtree_equal
483
            and self.table == other.table
484
            and self.value_list == other.value_list
485
            and self.column_list == other.column_list
486
        )
487

488
    def __hash__(self) -> int:
×
489
        return hash(
×
490
            (
491
                super().__hash__(),
492
                self.table,
493
                tuple(self.value_list),
494
                tuple(self.column_list),
495
            )
496
        )
497

498

499
class LogicalDelete(Operator):
×
500
    """[Logical Node for Delete Operation]
501

502
    Arguments:
503
        table_ref(TableCatalogEntry): table to delete tuples from,
504
        where_clause(AbstractExpression): the predicate used to select which rows to delete,
505

506
    """
507

508
    def __init__(
×
509
        self,
510
        table_ref: TableRef,
511
        where_clause: AbstractExpression = None,
512
        children=None,
513
    ):
514
        super().__init__(OperatorType.LOGICALDELETE, children)
×
515
        self._table_ref = table_ref
×
516
        self._where_clause = where_clause
×
517

518
    @property
×
519
    def table_ref(self):
×
520
        return self._table_ref
×
521

522
    @property
×
523
    def where_clause(self):
×
524
        return self._where_clause
×
525

526
    def __eq__(self, other):
×
527
        is_subtree_equal = super().__eq__(other)
×
528
        if not isinstance(other, LogicalDelete):
×
529
            return False
×
530
        return (
×
531
            is_subtree_equal
532
            and self.table_ref == other.table_ref
533
            and self.where_clause == other.where_clause
534
        )
535

536
    def __hash__(self) -> int:
×
537
        return hash(
×
538
            (
539
                super().__hash__(),
540
                self.table_ref,
541
                self.where_clause,
542
            )
543
        )
544

545

546
class LogicalCreate(Operator):
×
547
    """Logical node for create table operations
548

549
    Arguments:
550
        video {TableRef}: [video table that is to be created]
551
        column_list {List[ColumnDefinition]}:
552
        if_not_exists {bool}: [create table if exists]
553

554
    """
555

556
    def __init__(
×
557
        self,
558
        video: TableInfo,
559
        column_list: List[ColumnDefinition],
560
        if_not_exists: bool = False,
561
        children: List = None,
562
    ):
563
        super().__init__(OperatorType.LOGICALCREATE, children)
×
564
        self._video = video
×
565
        self._column_list = column_list
×
566
        self._if_not_exists = if_not_exists
×
567

568
    @property
×
569
    def video(self):
×
570
        return self._video
×
571

572
    @property
×
573
    def column_list(self):
×
574
        return self._column_list
×
575

576
    @property
×
577
    def if_not_exists(self):
×
578
        return self._if_not_exists
×
579

580
    def __eq__(self, other):
×
581
        is_subtree_equal = super().__eq__(other)
×
582
        if not isinstance(other, LogicalCreate):
×
583
            return False
×
584
        return (
×
585
            is_subtree_equal
586
            and self.video == other.video
587
            and self.column_list == other.column_list
588
            and self.if_not_exists == other.if_not_exists
589
        )
590

591
    def __hash__(self) -> int:
×
592
        return hash(
×
593
            (
594
                super().__hash__(),
595
                self.video,
596
                tuple(self.column_list),
597
                self.if_not_exists,
598
            )
599
        )
600

601

602
class LogicalRename(Operator):
×
603
    """Logical node for rename table operations
604

605
    Arguments:
606
        old_table {TableRef}: [old table that is to be renamed]
607
        new_name {TableInfo}: [new name for the old table]
608
    """
609

610
    def __init__(self, old_table_ref: TableRef, new_name: TableInfo, children=None):
×
611
        super().__init__(OperatorType.LOGICALRENAME, children)
×
612
        self._new_name = new_name
×
613
        self._old_table_ref = old_table_ref
×
614

615
    @property
×
616
    def new_name(self):
×
617
        return self._new_name
×
618

619
    @property
×
620
    def old_table_ref(self):
×
621
        return self._old_table_ref
×
622

623
    def __eq__(self, other):
×
624
        is_subtree_equal = super().__eq__(other)
×
625
        if not isinstance(other, LogicalRename):
×
626
            return False
×
627
        return (
×
628
            is_subtree_equal
629
            and self._new_name == other._new_name
630
            and self._old_table_ref == other._old_table_ref
631
        )
632

633
    def __hash__(self) -> int:
×
634
        return hash((super().__hash__(), self._new_name, self._old_table_ref))
×
635

636

637
class LogicalCreateUDF(Operator):
×
638
    """
639
    Logical node for create udf operations
640

641
    Attributes:
642
        name: str
643
            udf_name provided by the user required
644
        if_not_exists: bool
645
            if true should throw an error if udf with same name exists
646
            else will replace the existing
647
        inputs: List[UdfIOCatalogEntry]
648
            udf inputs, annotated list similar to table columns
649
        outputs: List[UdfIOCatalogEntry]
650
            udf outputs, annotated list similar to table columns
651
        impl_path: Path
652
            file path which holds the implementation of the udf.
653
            This file should be placed in the UDF directory and
654
            the path provided should be relative to the UDF dir.
655
        udf_type: str
656
            udf type. it ca be object detection, classification etc.
657
    """
658

659
    def __init__(
×
660
        self,
661
        name: str,
662
        if_not_exists: bool,
663
        inputs: List[UdfIOCatalogEntry],
664
        outputs: List[UdfIOCatalogEntry],
665
        impl_path: Path,
666
        udf_type: str = None,
667
        metadata: List[UdfMetadataCatalogEntry] = None,
668
        children: List = None,
669
    ):
670
        super().__init__(OperatorType.LOGICALCREATEUDF, children)
×
671
        self._name = name
×
672
        self._if_not_exists = if_not_exists
×
673
        self._inputs = inputs
×
674
        self._outputs = outputs
×
675
        self._impl_path = impl_path
×
676
        self._udf_type = udf_type
×
677
        self._metadata = metadata
×
678

679
    @property
×
680
    def name(self):
×
681
        return self._name
×
682

683
    @property
×
684
    def if_not_exists(self):
×
685
        return self._if_not_exists
×
686

687
    @property
×
688
    def inputs(self):
×
689
        return self._inputs
×
690

691
    @property
×
692
    def outputs(self):
×
693
        return self._outputs
×
694

695
    @property
×
696
    def impl_path(self):
×
697
        return self._impl_path
×
698

699
    @property
×
700
    def udf_type(self):
×
701
        return self._udf_type
×
702

703
    @property
×
704
    def metadata(self):
×
705
        return self._metadata
×
706

707
    def __eq__(self, other):
×
708
        is_subtree_equal = super().__eq__(other)
×
709
        if not isinstance(other, LogicalCreateUDF):
×
710
            return False
×
711
        return (
×
712
            is_subtree_equal
713
            and self.name == other.name
714
            and self.if_not_exists == other.if_not_exists
715
            and self.inputs == other.inputs
716
            and self.outputs == other.outputs
717
            and self.udf_type == other.udf_type
718
            and self.impl_path == other.impl_path
719
            and self.metadata == other.metadata
720
        )
721

722
    def __hash__(self) -> int:
×
723
        return hash(
×
724
            (
725
                super().__hash__(),
726
                self.name,
727
                self.if_not_exists,
728
                tuple(self.inputs),
729
                tuple(self.outputs),
730
                self.udf_type,
731
                self.impl_path,
732
                tuple(self.metadata),
733
            )
734
        )
735

736

737
class LogicalDropObject(Operator):
×
738
    """
739
    Logical node for DROP Object operations
740

741
    Attributes:
742
        object_type: ObjectType
743
        name: str
744
            UDF name provided by the user
745
        if_exists: bool
746
            if false, throws an error when no UDF with name exists
747
            else logs a warning
748
    """
749

750
    def __init__(
×
751
        self, object_type: ObjectType, name: str, if_exists: bool, children: List = None
752
    ):
753
        super().__init__(OperatorType.LOGICAL_DROP_OBJECT, children)
×
754
        self._object_type = object_type
×
755
        self._name = name
×
756
        self._if_exists = if_exists
×
757

758
    @property
×
759
    def object_type(self):
×
760
        return self._object_type
×
761

762
    @property
×
763
    def name(self):
×
764
        return self._name
×
765

766
    @property
×
767
    def if_exists(self):
×
768
        return self._if_exists
×
769

770
    def __eq__(self, other):
×
771
        is_subtree_equal = super().__eq__(other)
×
772
        if not isinstance(other, LogicalDropObject):
×
773
            return False
×
774
        return (
×
775
            is_subtree_equal
776
            and self.object_type == other.object_type
777
            and self.name == other.name
778
            and self.if_exists == other.if_exists
779
        )
780

781
    def __hash__(self) -> int:
×
782
        return hash((super().__hash__(), self.object_type, self.name, self.if_exists))
×
783

784

785
class LogicalLoadData(Operator):
×
786
    """Logical node for load data operation
787

788
    Arguments:
789
        table(TableCatalogEntry): table to load data into
790
        path(Path): file path from where we are loading data
791
    """
792

793
    def __init__(
×
794
        self,
795
        table_info: TableInfo,
796
        path: Path,
797
        column_list: List[AbstractExpression] = None,
798
        file_options: dict = dict(),
799
        children: List = None,
800
    ):
801
        super().__init__(OperatorType.LOGICALLOADDATA, children=children)
×
802
        self._table_info = table_info
×
803
        self._path = path
×
804
        self._column_list = column_list or []
×
805
        self._file_options = file_options
×
806

807
    @property
×
808
    def table_info(self):
×
809
        return self._table_info
×
810

811
    @property
×
812
    def path(self):
×
813
        return self._path
×
814

815
    @property
×
816
    def column_list(self):
×
817
        return self._column_list
×
818

819
    @property
×
820
    def file_options(self):
×
821
        return self._file_options
×
822

823
    def __str__(self):
×
824
        return "LogicalLoadData(table: {}, path: {}, \
×
825
                column_list: {}, \
826
                file_options: {})".format(
827
            self.table_info, self.path, self.column_list, self.file_options
828
        )
829

830
    def __eq__(self, other):
×
831
        is_subtree_equal = super().__eq__(other)
×
832
        if not isinstance(other, LogicalLoadData):
×
833
            return False
×
834
        return (
×
835
            is_subtree_equal
836
            and self.table_info == other.table_info
837
            and self.path == other.path
838
            and self.column_list == other.column_list
839
            and self.file_options == other.file_options
840
        )
841

842
    def __hash__(self) -> int:
×
843
        return hash(
×
844
            (
845
                super().__hash__(),
846
                self.table_info,
847
                self.path,
848
                tuple(self.column_list),
849
                frozenset(self.file_options.items()),
850
            )
851
        )
852

853

854
class LogicalFunctionScan(Operator):
×
855
    """
856
    Logical node for function table scans
857

858
    Attributes:
859
        func_expr: AbstractExpression
860
            function_expression that yield a table like output
861
    """
862

863
    def __init__(
×
864
        self,
865
        func_expr: AbstractExpression,
866
        alias: Alias,
867
        do_unnest: bool = False,
868
        children: List = None,
869
    ):
870
        super().__init__(OperatorType.LOGICALFUNCTIONSCAN, children)
×
871
        self._func_expr = func_expr
×
872
        self._do_unnest = do_unnest
×
873
        self._alias = alias
×
874

875
    @property
×
876
    def alias(self):
×
877
        return self._alias
×
878

879
    @property
×
880
    def func_expr(self):
×
881
        return self._func_expr
×
882

883
    @property
×
884
    def do_unnest(self):
×
885
        return self._do_unnest
×
886

887
    def __eq__(self, other):
×
888
        is_subtree_equal = super().__eq__(other)
×
889
        if not isinstance(other, LogicalFunctionScan):
×
890
            return False
×
891
        return (
×
892
            is_subtree_equal
893
            and self.func_expr == other.func_expr
894
            and self.do_unnest == other.do_unnest
895
            and self.alias == other.alias
896
        )
897

898
    def __hash__(self) -> int:
×
899
        return hash((super().__hash__(), self.func_expr, self.do_unnest, self.alias))
×
900

901

902
class LogicalExtractObject(Operator):
×
903
    def __init__(
×
904
        self,
905
        detector: FunctionExpression,
906
        tracker: FunctionExpression,
907
        alias: Alias,
908
        do_unnest: bool = False,
909
        children: List = None,
910
    ):
911
        super().__init__(OperatorType.LOGICAL_EXTRACT_OBJECT, children)
×
912
        self.detector = detector
×
913
        self.tracker = tracker
×
914
        self.do_unnest = do_unnest
×
915
        self.alias = alias
×
916

917
    def __eq__(self, other):
×
918
        is_subtree_equal = super().__eq__(other)
×
919
        if not isinstance(other, LogicalExtractObject):
×
920
            return False
×
921
        return (
×
922
            is_subtree_equal
923
            and self.detector == other.detector
924
            and self.tracker == other.tracker
925
            and self.do_unnest == other.do_unnest
926
            and self.alias == other.alias
927
        )
928

929
    def __hash__(self) -> int:
×
930
        return hash(
×
931
            (
932
                super().__hash__(),
933
                self.detector,
934
                self.tracker,
935
                self.do_unnest,
936
                self.alias,
937
            )
938
        )
939

940

941
class LogicalJoin(Operator):
×
942
    """
943
    Logical node for join operators
944

945
    Attributes:
946
        join_type: JoinType
947
            Join type provided by the user - Lateral, Inner, Outer
948
        join_predicate: AbstractExpression
949
            condition/predicate expression used to join the tables
950
    """
951

952
    def __init__(
×
953
        self,
954
        join_type: JoinType,
955
        join_predicate: AbstractExpression = None,
956
        left_keys: List[ColumnCatalogEntry] = None,
957
        right_keys: List[ColumnCatalogEntry] = None,
958
        children: List = None,
959
    ):
960
        super().__init__(OperatorType.LOGICALJOIN, children)
×
961
        self._join_type = join_type
×
962
        self._join_predicate = join_predicate
×
963
        self._left_keys = left_keys
×
964
        self._right_keys = right_keys
×
965
        self._join_project = None
×
966

967
    @property
×
968
    def join_type(self):
×
969
        return self._join_type
×
970

971
    @property
×
972
    def join_predicate(self):
×
973
        return self._join_predicate
×
974

975
    @property
×
976
    def left_keys(self):
×
977
        return self._left_keys
×
978

979
    @property
×
980
    def right_keys(self):
×
981
        return self._right_keys
×
982

983
    @property
×
984
    def join_project(self):
×
985
        return self._join_project
×
986

987
    def lhs(self):
×
988
        return self.children[0]
×
989

990
    def rhs(self):
×
991
        return self.children[1]
×
992

993
    def __eq__(self, other):
×
994
        is_subtree_equal = super().__eq__(other)
×
995
        if not isinstance(other, LogicalJoin):
×
996
            return False
×
997
        return (
×
998
            is_subtree_equal
999
            and self.join_type == other.join_type
1000
            and self.join_predicate == other.join_predicate
1001
            and self.left_keys == other.left_keys
1002
            and self.right_keys == other.right_keys
1003
            and self.join_project == other.join_project
1004
        )
1005

1006
    def __hash__(self) -> int:
×
1007
        return hash(
×
1008
            (
1009
                super().__hash__(),
1010
                self.join_type,
1011
                self.join_predicate,
1012
                self.left_keys,
1013
                self.right_keys,
1014
                tuple(self.join_project or []),
1015
            )
1016
        )
1017

1018

1019
class LogicalShow(Operator):
×
1020
    def __init__(self, show_type: ShowType, children: List = None):
×
1021
        super().__init__(OperatorType.LOGICAL_SHOW, children)
×
1022
        self._show_type = show_type
×
1023

1024
    @property
×
1025
    def show_type(self):
×
1026
        return self._show_type
×
1027

1028
    def __eq__(self, other):
×
1029
        is_subtree_equal = super().__eq__(other)
×
1030
        if not isinstance(other, LogicalShow):
×
1031
            return False
×
1032
        return is_subtree_equal and self.show_type == other.show_type
×
1033

1034
    def __hash__(self) -> int:
×
1035
        return hash((super().__hash__(), self.show_type))
×
1036

1037

1038
class LogicalExchange(Operator):
×
1039
    def __init__(self, children=None):
×
1040
        super().__init__(OperatorType.LOGICALEXCHANGE, children)
×
1041

1042
    def __eq__(self, other):
×
1043
        is_subtree_equal = super().__eq__(other)
×
1044
        if not isinstance(other, LogicalExchange):
×
1045
            return False
×
1046
        return is_subtree_equal
×
1047

1048

1049
class LogicalExplain(Operator):
×
1050
    def __init__(self, children: List = None):
×
1051
        super().__init__(OperatorType.LOGICALEXPLAIN, children)
×
1052
        assert len(children) == 1, "EXPLAIN command only takes one child"
×
1053
        self._explainable_opr = children[0]
×
1054

1055
    @property
×
1056
    def explainable_opr(self):
×
1057
        return self._explainable_opr
×
1058

1059
    def __eq__(self, other):
×
1060
        is_subtree_equal = super().__eq__(other)
×
1061
        if not isinstance(other, LogicalExplain):
×
1062
            return False
×
1063
        return is_subtree_equal and self._explainable_opr == other.explainable_opr
×
1064

1065
    def __hash__(self) -> int:
×
1066
        return hash((super().__hash__(), self._explainable_opr))
×
1067

1068

1069
class LogicalCreateIndex(Operator):
×
1070
    def __init__(
×
1071
        self,
1072
        name: str,
1073
        table_ref: TableRef,
1074
        col_list: List[ColumnDefinition],
1075
        vector_store_type: VectorStoreType,
1076
        udf_func: FunctionExpression = None,
1077
        children: List = None,
1078
    ):
1079
        super().__init__(OperatorType.LOGICALCREATEINDEX, children)
×
1080
        self._name = name
×
1081
        self._table_ref = table_ref
×
1082
        self._col_list = col_list
×
1083
        self._vector_store_type = vector_store_type
×
1084
        self._udf_func = udf_func
×
1085

1086
    @property
×
1087
    def name(self):
×
1088
        return self._name
×
1089

1090
    @property
×
1091
    def table_ref(self):
×
1092
        return self._table_ref
×
1093

1094
    @property
×
1095
    def col_list(self):
×
1096
        return self._col_list
×
1097

1098
    @property
×
1099
    def vector_store_type(self):
×
1100
        return self._vector_store_type
×
1101

1102
    @property
×
1103
    def udf_func(self):
×
1104
        return self._udf_func
×
1105

1106
    def __eq__(self, other):
×
1107
        is_subtree_equal = super().__eq__(other)
×
1108
        if not isinstance(other, LogicalCreateIndex):
×
1109
            return False
×
1110
        return (
×
1111
            is_subtree_equal
1112
            and self.name == other.name
1113
            and self.table_ref == other.table_ref
1114
            and self.col_list == other.col_list
1115
            and self.vector_store_type == other.vector_store_type
1116
            and self.udf_func == other.udf_func
1117
        )
1118

1119
    def __hash__(self) -> int:
×
1120
        return hash(
×
1121
            (
1122
                super().__hash__(),
1123
                self.name,
1124
                self.table_ref,
1125
                tuple(self.col_list),
1126
                self.vector_store_type,
1127
                self.udf_func,
1128
            )
1129
        )
1130

1131

1132
class LogicalApplyAndMerge(Operator):
×
1133
    """Evaluate the function expression on the input data and return the merged output.
1134
    This operator simplifies the process of evaluating functions on a table source.
1135
    Currently, it performs an inner join while merging the function output with the
1136
    input data. This means that if the function does not return any output for a given
1137
    input row, that row will be dropped from the output. We can consider expanding this
1138
    to support left joins and other types of joins in the future.
1139
    """
1140

1141
    def __init__(
×
1142
        self,
1143
        func_expr: FunctionExpression,
1144
        alias: Alias,
1145
        do_unnest: bool = False,
1146
        children: List = None,
1147
    ):
1148
        super().__init__(OperatorType.LOGICAL_APPLY_AND_MERGE, children)
×
1149
        self._func_expr = func_expr
×
1150
        self._do_unnest = do_unnest
×
1151
        self._alias = alias
×
1152
        self._merge_type = JoinType.INNER_JOIN
×
1153

1154
    @property
×
1155
    def alias(self):
×
1156
        return self._alias
×
1157

1158
    @property
×
1159
    def func_expr(self):
×
1160
        return self._func_expr
×
1161

1162
    @property
×
1163
    def do_unnest(self):
×
1164
        return self._do_unnest
×
1165

1166
    def __eq__(self, other):
×
1167
        is_subtree_equal = super().__eq__(other)
×
1168
        if not isinstance(other, LogicalApplyAndMerge):
×
1169
            return False
×
1170
        return (
×
1171
            is_subtree_equal
1172
            and self.func_expr == other.func_expr
1173
            and self.do_unnest == other.do_unnest
1174
            and self.alias == other.alias
1175
            and self._merge_type == other._merge_type
1176
        )
1177

1178
    def __hash__(self) -> int:
×
1179
        return hash(
×
1180
            (
1181
                super().__hash__(),
1182
                self.func_expr,
1183
                self.do_unnest,
1184
                self.alias,
1185
                self._merge_type,
1186
            )
1187
        )
1188

1189

1190
class LogicalVectorIndexScan(Operator):
×
1191
    def __init__(
×
1192
        self,
1193
        index_name: str,
1194
        vector_store_type: VectorStoreType,
1195
        limit_count: ConstantValueExpression,
1196
        search_query_expr: FunctionExpression,
1197
        children: List = None,
1198
    ):
1199
        super().__init__(OperatorType.LOGICAL_VECTOR_INDEX_SCAN, children)
×
1200
        self._index_name = index_name
×
1201
        self._vector_store_type = vector_store_type
×
1202
        self._limit_count = limit_count
×
1203
        self._search_query_expr = search_query_expr
×
1204

1205
    @property
×
1206
    def index_name(self):
×
1207
        return self._index_name
×
1208

1209
    @property
×
1210
    def vector_store_type(self):
×
1211
        return self._vector_store_type
×
1212

1213
    @property
×
1214
    def limit_count(self):
×
1215
        return self._limit_count
×
1216

1217
    @property
×
1218
    def search_query_expr(self):
×
1219
        return self._search_query_expr
×
1220

1221
    def __eq__(self, other):
×
1222
        is_subtree_equal = super().__eq__(other)
×
1223
        if not isinstance(other, LogicalVectorIndexScan):
×
1224
            return False
×
1225
        return (
×
1226
            is_subtree_equal
1227
            and self.index_name == other.index_name
1228
            and self.vector_store_type == other.vector_store_type
1229
            and self.limit_count == other.limit_count
1230
            and self.search_query_expr == other.search_query_expr
1231
        )
1232

1233
    def __hash__(self) -> int:
×
1234
        return hash(
×
1235
            (
1236
                super().__hash__(),
1237
                self.index_name,
1238
                self.vector_store_type,
1239
                self.limit_count,
1240
                self.search_query_expr,
1241
            )
1242
        )
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