jupyter-duckdb 1.4.100__py3-none-any.whl → 1.4.106__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- duckdb_kernel/kernel.py +9 -5
- duckdb_kernel/parser/RAParser.py +5 -0
- duckdb_kernel/parser/elements/RAElement.py +4 -1
- duckdb_kernel/parser/elements/binary/And.py +1 -1
- duckdb_kernel/parser/elements/binary/ConditionalSet.py +7 -1
- duckdb_kernel/parser/elements/binary/Cross.py +1 -1
- duckdb_kernel/parser/elements/binary/LeftSemiJoin.py +24 -0
- duckdb_kernel/parser/elements/binary/RightSemiJoin.py +24 -0
- duckdb_kernel/parser/elements/binary/__init__.py +3 -1
- duckdb_kernel/parser/util/QuerySplitter.py +87 -0
- duckdb_kernel/tests/__init__.py +2 -2
- duckdb_kernel/tests/test_ra.py +385 -10
- {jupyter_duckdb-1.4.100.dist-info → jupyter_duckdb-1.4.106.dist-info}/METADATA +4 -2
- {jupyter_duckdb-1.4.100.dist-info → jupyter_duckdb-1.4.106.dist-info}/RECORD +16 -13
- {jupyter_duckdb-1.4.100.dist-info → jupyter_duckdb-1.4.106.dist-info}/WHEEL +0 -0
- {jupyter_duckdb-1.4.100.dist-info → jupyter_duckdb-1.4.106.dist-info}/top_level.txt +0 -0
duckdb_kernel/kernel.py
CHANGED
|
@@ -13,6 +13,7 @@ from .db import Connection, DatabaseError, Table
|
|
|
13
13
|
from .db.error import *
|
|
14
14
|
from .magics import *
|
|
15
15
|
from .parser import RAParser, DCParser, ParserError
|
|
16
|
+
from .parser.util.QuerySplitter import split_queries, get_last_query
|
|
16
17
|
from .util.ResultSetComparator import ResultSetComparator
|
|
17
18
|
from .util.SQL import SQL_KEYWORDS
|
|
18
19
|
from .util.TestError import TestError
|
|
@@ -27,9 +28,11 @@ class DuckDBKernel(Kernel):
|
|
|
27
28
|
implementation_version = '1.0'
|
|
28
29
|
banner = 'DuckDB Kernel'
|
|
29
30
|
language_info = {
|
|
30
|
-
'name': '
|
|
31
|
-
'mimetype': 'application/sql',
|
|
31
|
+
'name': 'sql',
|
|
32
32
|
'file_extension': '.sql',
|
|
33
|
+
'mimetype': 'text/x-sql',
|
|
34
|
+
'codemirror_mode': 'sql',
|
|
35
|
+
'pygments_lexer': 'sql',
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
def __init__(self, **kwargs):
|
|
@@ -166,7 +169,9 @@ class DuckDBKernel(Kernel):
|
|
|
166
169
|
# print result if not silent
|
|
167
170
|
if not silent:
|
|
168
171
|
# print EXPLAIN queries as raw text if using DuckDB
|
|
169
|
-
|
|
172
|
+
last_query = get_last_query(query, remove_comments=True).strip()
|
|
173
|
+
|
|
174
|
+
if last_query.startswith('EXPLAIN') and state.db.plain_explain():
|
|
170
175
|
for ekey, evalue in rows:
|
|
171
176
|
html = f'<b>{ekey}</b><br><pre>{evalue}</pre>'
|
|
172
177
|
break
|
|
@@ -273,8 +278,7 @@ class DuckDBKernel(Kernel):
|
|
|
273
278
|
|
|
274
279
|
# You can only execute one statement at a time using SQLite.
|
|
275
280
|
if not state.db.multiple_statements_per_query():
|
|
276
|
-
|
|
277
|
-
for statement in statements:
|
|
281
|
+
for statement in split_queries(content):
|
|
278
282
|
try:
|
|
279
283
|
state.db.execute(statement)
|
|
280
284
|
except EmptyResultError:
|
duckdb_kernel/parser/RAParser.py
CHANGED
|
@@ -2,6 +2,7 @@ from .LogicParser import LogicParser
|
|
|
2
2
|
from .ParserError import RAParserError
|
|
3
3
|
from .elements import *
|
|
4
4
|
from .tokenizer import *
|
|
5
|
+
from .util.QuerySplitter import get_last_query
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
# Instead of multiple nested loops, a tree with rotation can
|
|
@@ -10,6 +11,10 @@ from .tokenizer import *
|
|
|
10
11
|
class RAParser:
|
|
11
12
|
@staticmethod
|
|
12
13
|
def parse_query(query: str) -> RAElement:
|
|
14
|
+
# remove comments from query
|
|
15
|
+
query = get_last_query(query, split_at=None, remove_comments=True)
|
|
16
|
+
|
|
17
|
+
# parse query
|
|
13
18
|
initial_token = Token(query)
|
|
14
19
|
return RAParser.parse_tokens(initial_token, depth=0)
|
|
15
20
|
|
|
@@ -36,10 +36,13 @@ class RAElement:
|
|
|
36
36
|
# if all columns are from the same relation we can skip the relation name
|
|
37
37
|
if len(set(c.table for c in columns)) == 1:
|
|
38
38
|
column_names = ', '.join(f'{c.current_name} AS "{c.name}"' for c in columns)
|
|
39
|
+
order_names = ', '.join(f'"{c.name}" ASC' for c in columns)
|
|
39
40
|
else:
|
|
40
41
|
column_names = ', '.join(f'{c.current_name} AS "{c.full_name}"' for c in columns)
|
|
42
|
+
order_names = ', '.join(f'"{c.full_name}" ASC' for c in columns)
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
# create sql
|
|
45
|
+
return f'SELECT {column_names} FROM ({sql}) {self._name()} ORDER BY {order_names}'
|
|
43
46
|
|
|
44
47
|
def to_sql_with_count(self, tables: Dict[str, Table]) -> str:
|
|
45
48
|
sql, _ = self.to_sql(tables)
|
|
@@ -347,7 +347,13 @@ class ConditionalSet:
|
|
|
347
347
|
sql_join_filters += f' AND {join_filter}'
|
|
348
348
|
|
|
349
349
|
sql_condition = condition.to_sql(joined_columns) if condition is not None else '1=1'
|
|
350
|
-
|
|
350
|
+
|
|
351
|
+
if self.projection == ('*',):
|
|
352
|
+
sql_order = ', '.join(f'{rc.name} ASC' for rcl in rcls for rc in rcl)
|
|
353
|
+
else:
|
|
354
|
+
sql_order = ', '.join(f'{col} ASC' for col in self.projection)
|
|
355
|
+
|
|
356
|
+
sql_query = f'SELECT DISTINCT {sql_select} FROM {sql_tables} WHERE ({sql_join_filters}) AND ({sql_condition}) ORDER BY {sql_order}'
|
|
351
357
|
|
|
352
358
|
# Create a mapping from intermediate column names to constant values.
|
|
353
359
|
column_name_mapping = {
|
|
@@ -8,7 +8,7 @@ from ...util.RenamableColumnList import RenamableColumnList
|
|
|
8
8
|
class Cross(RABinaryOperator):
|
|
9
9
|
@staticmethod
|
|
10
10
|
def symbols() -> Tuple[str, ...]:
|
|
11
|
-
return chr(215), 'x'
|
|
11
|
+
return chr(215), 'x', 'times'
|
|
12
12
|
|
|
13
13
|
def to_sql(self, tables: Dict[str, Table]) -> Tuple[str, RenamableColumnList]:
|
|
14
14
|
# execute subqueries
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Tuple, Dict
|
|
2
|
+
|
|
3
|
+
from duckdb_kernel.db import Table
|
|
4
|
+
from ..RABinaryOperator import RABinaryOperator
|
|
5
|
+
from ...util.RenamableColumnList import RenamableColumnList
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LeftSemiJoin(RABinaryOperator):
|
|
9
|
+
@staticmethod
|
|
10
|
+
def symbols() -> Tuple[str, ...]:
|
|
11
|
+
return chr(8905), 'lsjoin'
|
|
12
|
+
|
|
13
|
+
def to_sql(self, tables: Dict[str, Table]) -> Tuple[str, RenamableColumnList]:
|
|
14
|
+
# execute subqueries
|
|
15
|
+
lq, lcols = self.left.to_sql(tables)
|
|
16
|
+
rq, rcols = self.right.to_sql(tables)
|
|
17
|
+
|
|
18
|
+
# find matching columns
|
|
19
|
+
join_cols, all_cols = lcols.intersect(rcols)
|
|
20
|
+
|
|
21
|
+
on_clause = ' AND '.join(f'{l.current_name} = {r.current_name}' for l, r in join_cols)
|
|
22
|
+
|
|
23
|
+
# create sql
|
|
24
|
+
return f'SELECT {lcols.list} FROM ({lq}) {self._name()} JOIN ({rq}) {self._name()} ON {on_clause}', lcols
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Tuple, Dict
|
|
2
|
+
|
|
3
|
+
from duckdb_kernel.db import Table
|
|
4
|
+
from ..RABinaryOperator import RABinaryOperator
|
|
5
|
+
from ...util.RenamableColumnList import RenamableColumnList
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RightSemiJoin(RABinaryOperator):
|
|
9
|
+
@staticmethod
|
|
10
|
+
def symbols() -> Tuple[str, ...]:
|
|
11
|
+
return chr(8906), 'rsjoin'
|
|
12
|
+
|
|
13
|
+
def to_sql(self, tables: Dict[str, Table]) -> Tuple[str, RenamableColumnList]:
|
|
14
|
+
# execute subqueries
|
|
15
|
+
lq, lcols = self.left.to_sql(tables)
|
|
16
|
+
rq, rcols = self.right.to_sql(tables)
|
|
17
|
+
|
|
18
|
+
# find matching columns
|
|
19
|
+
join_cols, all_cols = lcols.intersect(rcols, prefer_right=True)
|
|
20
|
+
|
|
21
|
+
on_clause = ' AND '.join(f'{l.current_name} = {r.current_name}' for l, r in join_cols)
|
|
22
|
+
|
|
23
|
+
# create sql
|
|
24
|
+
return f'SELECT {rcols.list} FROM ({lq}) {self._name()} JOIN ({rq}) {self._name()} ON {on_clause}', rcols
|
|
@@ -3,7 +3,9 @@ from .Difference import Difference
|
|
|
3
3
|
from .Intersection import Intersection
|
|
4
4
|
from .Join import Join
|
|
5
5
|
from .LeftOuterJoin import LeftOuterJoin
|
|
6
|
+
from .LeftSemiJoin import LeftSemiJoin
|
|
6
7
|
from .RightOuterJoin import RightOuterJoin
|
|
8
|
+
from .RightSemiJoin import RightSemiJoin
|
|
7
9
|
from .FullOuterJoin import FullOuterJoin
|
|
8
10
|
from .Union import Union
|
|
9
11
|
|
|
@@ -37,7 +39,7 @@ RA_BINARY_OPERATORS = [
|
|
|
37
39
|
[Union],
|
|
38
40
|
[Intersection],
|
|
39
41
|
[Join],
|
|
40
|
-
[LeftOuterJoin, RightOuterJoin, FullOuterJoin],
|
|
42
|
+
[LeftOuterJoin, RightOuterJoin, FullOuterJoin, LeftSemiJoin, RightSemiJoin],
|
|
41
43
|
[Cross],
|
|
42
44
|
[Division]
|
|
43
45
|
]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import Iterator
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def split_queries(query: str, split_at: str | None = ';', remove_comments: bool = False) -> Iterator[str]:
|
|
5
|
+
quotes = '\'"`'
|
|
6
|
+
|
|
7
|
+
escaped = False
|
|
8
|
+
in_quotes = None
|
|
9
|
+
in_singleline_comment = False
|
|
10
|
+
in_multiline_comment = False
|
|
11
|
+
|
|
12
|
+
previous = None
|
|
13
|
+
current_query = []
|
|
14
|
+
|
|
15
|
+
for symbol in query:
|
|
16
|
+
keep_symbol = True
|
|
17
|
+
|
|
18
|
+
# escaped symbol
|
|
19
|
+
if escaped:
|
|
20
|
+
escaped = False
|
|
21
|
+
|
|
22
|
+
# backslash (escape)
|
|
23
|
+
elif symbol == '\\':
|
|
24
|
+
escaped = True
|
|
25
|
+
|
|
26
|
+
# if in quotes
|
|
27
|
+
elif in_quotes is not None:
|
|
28
|
+
if symbol == in_quotes:
|
|
29
|
+
in_quotes = False
|
|
30
|
+
|
|
31
|
+
# if in single line comment
|
|
32
|
+
elif in_singleline_comment:
|
|
33
|
+
if symbol == '\n':
|
|
34
|
+
in_singleline_comment = False
|
|
35
|
+
elif remove_comments:
|
|
36
|
+
keep_symbol = False
|
|
37
|
+
|
|
38
|
+
# if in multiline comment
|
|
39
|
+
elif in_multiline_comment:
|
|
40
|
+
if previous == '*' and symbol == '/':
|
|
41
|
+
in_multiline_comment = False
|
|
42
|
+
|
|
43
|
+
if remove_comments:
|
|
44
|
+
keep_symbol = False
|
|
45
|
+
|
|
46
|
+
# start of quotes
|
|
47
|
+
elif symbol in quotes:
|
|
48
|
+
in_quotes = symbol
|
|
49
|
+
|
|
50
|
+
# start of single line comment
|
|
51
|
+
elif previous == '-' and symbol == '-':
|
|
52
|
+
in_singleline_comment = True
|
|
53
|
+
|
|
54
|
+
if remove_comments:
|
|
55
|
+
keep_symbol = False
|
|
56
|
+
current_query.pop()
|
|
57
|
+
|
|
58
|
+
# start of multiline comment
|
|
59
|
+
elif previous == '/' and symbol == '*':
|
|
60
|
+
in_multiline_comment = True
|
|
61
|
+
|
|
62
|
+
if remove_comments:
|
|
63
|
+
keep_symbol = False
|
|
64
|
+
current_query.pop()
|
|
65
|
+
|
|
66
|
+
# semicolon
|
|
67
|
+
elif split_at is not None and symbol == split_at:
|
|
68
|
+
yield ''.join(current_query)
|
|
69
|
+
|
|
70
|
+
current_query = []
|
|
71
|
+
keep_symbol = False
|
|
72
|
+
|
|
73
|
+
# store symbol
|
|
74
|
+
if keep_symbol:
|
|
75
|
+
current_query.append(symbol)
|
|
76
|
+
|
|
77
|
+
previous = symbol
|
|
78
|
+
|
|
79
|
+
# yield remaining symbols
|
|
80
|
+
yield ''.join(current_query)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_last_query(query: str, split_at: str | None = ';', remove_comments: bool = False) -> str:
|
|
84
|
+
for query in split_queries(query, split_at, remove_comments):
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
return query
|
duckdb_kernel/tests/__init__.py
CHANGED
|
@@ -56,7 +56,7 @@ class Connection:
|
|
|
56
56
|
sql = root.to_sql_with_renamed_columns(self.tables)
|
|
57
57
|
cols, rows = self.execute_sql_return_cols(sql)
|
|
58
58
|
|
|
59
|
-
return cols, sorted(rows, key=lambda t: tuple(-1 if x is None else x for x in t))
|
|
59
|
+
return cols, rows # sorted(rows, key=lambda t: tuple(-1 if x is None else x for x in t))
|
|
60
60
|
|
|
61
61
|
def execute_ra(self, root: RAElement) -> List:
|
|
62
62
|
_, rows = self.execute_ra_return_cols(root)
|
|
@@ -66,7 +66,7 @@ class Connection:
|
|
|
66
66
|
sql, cnm = root.to_sql_with_renamed_columns(self.tables)
|
|
67
67
|
cols, rows = self.execute_sql_return_cols(sql)
|
|
68
68
|
|
|
69
|
-
return [cnm.get(c, c) for c in cols], sorted(rows)
|
|
69
|
+
return [cnm.get(c, c) for c in cols], rows # sorted(rows)
|
|
70
70
|
|
|
71
71
|
def execute_dc(self, root: ConditionalSet) -> List:
|
|
72
72
|
_, rows = self.execute_dc_return_cols(root)
|
duckdb_kernel/tests/test_ra.py
CHANGED
|
@@ -79,9 +79,33 @@ def test_case_insensitivity():
|
|
|
79
79
|
]
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def test_comments():
|
|
83
|
+
for query in (
|
|
84
|
+
# single line
|
|
85
|
+
'Shows -- x Users\n x Seasons',
|
|
86
|
+
'Shows x Seasons -- x Users',
|
|
87
|
+
'Shows x Seasons--',
|
|
88
|
+
'Shows x Seasons--\n',
|
|
89
|
+
# multi line
|
|
90
|
+
'Shows /* x Users */ x Seasons',
|
|
91
|
+
'Shows /* x Users */\n x Seasons',
|
|
92
|
+
'Shows /* x Users\n */ x Seasons',
|
|
93
|
+
'Shows x Seasons/**/',
|
|
94
|
+
'Shows x Seasons/*\n*/',
|
|
95
|
+
'Shows x Seasons\n/**/',
|
|
96
|
+
'Shows x Seasons/* x Users'
|
|
97
|
+
):
|
|
98
|
+
root = RAParser.parse_query(query)
|
|
99
|
+
|
|
100
|
+
assert isinstance(root, BinaryOperators.Cross)
|
|
101
|
+
assert isinstance(root.left, RAOperand) and root.left.name == 'Shows'
|
|
102
|
+
assert isinstance(root.right, RAOperand) and root.right.name == 'Seasons'
|
|
103
|
+
|
|
104
|
+
|
|
82
105
|
def test_binary_operator_cross():
|
|
83
106
|
for query in (
|
|
84
107
|
r'Shows x Seasons',
|
|
108
|
+
r'Shows times Seasons',
|
|
85
109
|
):
|
|
86
110
|
root = RAParser.parse_query(query)
|
|
87
111
|
|
|
@@ -292,6 +316,52 @@ def test_binary_operator_fjoin():
|
|
|
292
316
|
]
|
|
293
317
|
|
|
294
318
|
|
|
319
|
+
def test_binary_operator_lsjoin():
|
|
320
|
+
for query in (
|
|
321
|
+
r'Users ⋉ BannedUsers',
|
|
322
|
+
r'Users lsjoin BannedUsers'
|
|
323
|
+
):
|
|
324
|
+
root = RAParser.parse_query(query)
|
|
325
|
+
|
|
326
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
327
|
+
assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
|
|
328
|
+
assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
|
|
329
|
+
|
|
330
|
+
with Connection() as con:
|
|
331
|
+
cols, rows = con.execute_ra_return_cols(root)
|
|
332
|
+
|
|
333
|
+
assert [c.lower() for c in cols] == [
|
|
334
|
+
'id',
|
|
335
|
+
'username'
|
|
336
|
+
]
|
|
337
|
+
assert rows == [
|
|
338
|
+
(2, 'Bob')
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def test_binary_operator_rsjoin():
|
|
343
|
+
for query in (
|
|
344
|
+
r'Users ⋊ BannedUsers',
|
|
345
|
+
r'Users rsjoin BannedUsers'
|
|
346
|
+
):
|
|
347
|
+
root = RAParser.parse_query(query)
|
|
348
|
+
|
|
349
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
350
|
+
assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
|
|
351
|
+
assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
|
|
352
|
+
|
|
353
|
+
with Connection() as con:
|
|
354
|
+
cols, rows = con.execute_ra_return_cols(root)
|
|
355
|
+
|
|
356
|
+
assert [c.lower() for c in cols] == [
|
|
357
|
+
'id',
|
|
358
|
+
'bannedusername'
|
|
359
|
+
]
|
|
360
|
+
assert rows == [
|
|
361
|
+
(2, 'Bob')
|
|
362
|
+
]
|
|
363
|
+
|
|
364
|
+
|
|
295
365
|
def test_binary_operator_union():
|
|
296
366
|
for query in (
|
|
297
367
|
r'Users ∪ BannedUsers',
|
|
@@ -566,7 +636,7 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
566
636
|
assert root.right.name == 'c'
|
|
567
637
|
|
|
568
638
|
# outer join
|
|
569
|
-
root = RAParser.parse_query(r'a ⟕ b ⟕ c')
|
|
639
|
+
root = RAParser.parse_query(r'a ⟕ b ⟕ c') # left outer, left outer
|
|
570
640
|
assert isinstance(root, BinaryOperators.LeftOuterJoin)
|
|
571
641
|
assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
|
|
572
642
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -576,7 +646,7 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
576
646
|
assert isinstance(root.right, RAOperand)
|
|
577
647
|
assert root.right.name == 'c'
|
|
578
648
|
|
|
579
|
-
root = RAParser.parse_query(r'a ⟖ b ⟖ c')
|
|
649
|
+
root = RAParser.parse_query(r'a ⟖ b ⟖ c') # right outer, right outer
|
|
580
650
|
assert isinstance(root, BinaryOperators.RightOuterJoin)
|
|
581
651
|
assert isinstance(root.left, BinaryOperators.RightOuterJoin)
|
|
582
652
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -586,7 +656,7 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
586
656
|
assert isinstance(root.right, RAOperand)
|
|
587
657
|
assert root.right.name == 'c'
|
|
588
658
|
|
|
589
|
-
root = RAParser.parse_query(r'a ⟗ b ⟗ c')
|
|
659
|
+
root = RAParser.parse_query(r'a ⟗ b ⟗ c') # full outer, full outer
|
|
590
660
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
591
661
|
assert isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
592
662
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -596,8 +666,29 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
596
666
|
assert isinstance(root.right, RAOperand)
|
|
597
667
|
assert root.right.name == 'c'
|
|
598
668
|
|
|
599
|
-
#
|
|
600
|
-
root = RAParser.parse_query(r'a
|
|
669
|
+
# semi join
|
|
670
|
+
root = RAParser.parse_query(r'a ⋉ b ⋉ c') # left semi, left semi
|
|
671
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
672
|
+
assert isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
673
|
+
assert isinstance(root.left.left, RAOperand)
|
|
674
|
+
assert root.left.left.name == 'a'
|
|
675
|
+
assert isinstance(root.left.right, RAOperand)
|
|
676
|
+
assert root.left.right.name == 'b'
|
|
677
|
+
assert isinstance(root.right, RAOperand)
|
|
678
|
+
assert root.right.name == 'c'
|
|
679
|
+
|
|
680
|
+
root = RAParser.parse_query(r'a ⋊ b ⋊ c') # right semi, right semi
|
|
681
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
682
|
+
assert isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
683
|
+
assert isinstance(root.left.left, RAOperand)
|
|
684
|
+
assert root.left.left.name == 'a'
|
|
685
|
+
assert isinstance(root.left.right, RAOperand)
|
|
686
|
+
assert root.left.right.name == 'b'
|
|
687
|
+
assert isinstance(root.right, RAOperand)
|
|
688
|
+
assert root.right.name == 'c'
|
|
689
|
+
|
|
690
|
+
# mixed outer and semi joins
|
|
691
|
+
root = RAParser.parse_query(r'a ⟕ b ⟖ c') # left outer, right outer
|
|
601
692
|
assert isinstance(root, BinaryOperators.RightOuterJoin)
|
|
602
693
|
assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
|
|
603
694
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -607,7 +698,7 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
607
698
|
assert isinstance(root.right, RAOperand)
|
|
608
699
|
assert root.right.name == 'c'
|
|
609
700
|
|
|
610
|
-
root = RAParser.parse_query(r'a ⟕ b ⟗ c')
|
|
701
|
+
root = RAParser.parse_query(r'a ⟕ b ⟗ c') # left outer, full outer
|
|
611
702
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
612
703
|
assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
|
|
613
704
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -617,7 +708,27 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
617
708
|
assert isinstance(root.right, RAOperand)
|
|
618
709
|
assert root.right.name == 'c'
|
|
619
710
|
|
|
620
|
-
root = RAParser.parse_query(r'a
|
|
711
|
+
root = RAParser.parse_query(r'a ⟕ b ⋉ c') # left outer, left semi
|
|
712
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
713
|
+
assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
|
|
714
|
+
assert isinstance(root.left.left, RAOperand)
|
|
715
|
+
assert root.left.left.name == 'a'
|
|
716
|
+
assert isinstance(root.left.right, RAOperand)
|
|
717
|
+
assert root.left.right.name == 'b'
|
|
718
|
+
assert isinstance(root.right, RAOperand)
|
|
719
|
+
assert root.right.name == 'c'
|
|
720
|
+
|
|
721
|
+
root = RAParser.parse_query(r'a ⟕ b ⋊ c') # left outer, right semi
|
|
722
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
723
|
+
assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
|
|
724
|
+
assert isinstance(root.left.left, RAOperand)
|
|
725
|
+
assert root.left.left.name == 'a'
|
|
726
|
+
assert isinstance(root.left.right, RAOperand)
|
|
727
|
+
assert root.left.right.name == 'b'
|
|
728
|
+
assert isinstance(root.right, RAOperand)
|
|
729
|
+
assert root.right.name == 'c'
|
|
730
|
+
|
|
731
|
+
root = RAParser.parse_query(r'a ⟖ b ⟕ c') # right outer, left outer
|
|
621
732
|
assert isinstance(root, BinaryOperators.LeftOuterJoin)
|
|
622
733
|
assert isinstance(root.left, BinaryOperators.RightOuterJoin)
|
|
623
734
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -627,7 +738,7 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
627
738
|
assert isinstance(root.right, RAOperand)
|
|
628
739
|
assert root.right.name == 'c'
|
|
629
740
|
|
|
630
|
-
root = RAParser.parse_query(r'a ⟖ b ⟗ c')
|
|
741
|
+
root = RAParser.parse_query(r'a ⟖ b ⟗ c') # right outer, full outer
|
|
631
742
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
632
743
|
assert isinstance(root.left, BinaryOperators.RightOuterJoin)
|
|
633
744
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -637,7 +748,27 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
637
748
|
assert isinstance(root.right, RAOperand)
|
|
638
749
|
assert root.right.name == 'c'
|
|
639
750
|
|
|
640
|
-
root = RAParser.parse_query(r'a
|
|
751
|
+
root = RAParser.parse_query(r'a ⟖ b ⋉ c') # right outer, left semi
|
|
752
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
753
|
+
assert isinstance(root.left, BinaryOperators.RightOuterJoin)
|
|
754
|
+
assert isinstance(root.left.left, RAOperand)
|
|
755
|
+
assert root.left.left.name == 'a'
|
|
756
|
+
assert isinstance(root.left.right, RAOperand)
|
|
757
|
+
assert root.left.right.name == 'b'
|
|
758
|
+
assert isinstance(root.right, RAOperand)
|
|
759
|
+
assert root.right.name == 'c'
|
|
760
|
+
|
|
761
|
+
root = RAParser.parse_query(r'a ⟖ b ⋊ c') # right outer, right semi
|
|
762
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
763
|
+
assert isinstance(root.left, BinaryOperators.RightOuterJoin)
|
|
764
|
+
assert isinstance(root.left.left, RAOperand)
|
|
765
|
+
assert root.left.left.name == 'a'
|
|
766
|
+
assert isinstance(root.left.right, RAOperand)
|
|
767
|
+
assert root.left.right.name == 'b'
|
|
768
|
+
assert isinstance(root.right, RAOperand)
|
|
769
|
+
assert root.right.name == 'c'
|
|
770
|
+
|
|
771
|
+
root = RAParser.parse_query(r'a ⟗ b ⟕ c') # full outer, left outer
|
|
641
772
|
assert isinstance(root, BinaryOperators.LeftOuterJoin)
|
|
642
773
|
assert isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
643
774
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -647,7 +778,7 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
647
778
|
assert isinstance(root.right, RAOperand)
|
|
648
779
|
assert root.right.name == 'c'
|
|
649
780
|
|
|
650
|
-
root = RAParser.parse_query(r'a ⟗ b ⟖ c')
|
|
781
|
+
root = RAParser.parse_query(r'a ⟗ b ⟖ c') # full outer, right outer
|
|
651
782
|
assert isinstance(root, BinaryOperators.RightOuterJoin)
|
|
652
783
|
assert isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
653
784
|
assert isinstance(root.left.left, RAOperand)
|
|
@@ -657,6 +788,106 @@ def test_binary_left_to_right_evaluation_order():
|
|
|
657
788
|
assert isinstance(root.right, RAOperand)
|
|
658
789
|
assert root.right.name == 'c'
|
|
659
790
|
|
|
791
|
+
root = RAParser.parse_query(r'a ⟗ b ⋉ c') # full outer, left semi
|
|
792
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
793
|
+
assert isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
794
|
+
assert isinstance(root.left.left, RAOperand)
|
|
795
|
+
assert root.left.left.name == 'a'
|
|
796
|
+
assert isinstance(root.left.right, RAOperand)
|
|
797
|
+
assert root.left.right.name == 'b'
|
|
798
|
+
assert isinstance(root.right, RAOperand)
|
|
799
|
+
assert root.right.name == 'c'
|
|
800
|
+
|
|
801
|
+
root = RAParser.parse_query(r'a ⟗ b ⋊ c') # full outer, right semi
|
|
802
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
803
|
+
assert isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
804
|
+
assert isinstance(root.left.left, RAOperand)
|
|
805
|
+
assert root.left.left.name == 'a'
|
|
806
|
+
assert isinstance(root.left.right, RAOperand)
|
|
807
|
+
assert root.left.right.name == 'b'
|
|
808
|
+
assert isinstance(root.right, RAOperand)
|
|
809
|
+
assert root.right.name == 'c'
|
|
810
|
+
|
|
811
|
+
root = RAParser.parse_query(r'a ⋉ b ⟕ c') # left semi, left outer
|
|
812
|
+
assert isinstance(root, BinaryOperators.LeftOuterJoin)
|
|
813
|
+
assert isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
814
|
+
assert isinstance(root.left.left, RAOperand)
|
|
815
|
+
assert root.left.left.name == 'a'
|
|
816
|
+
assert isinstance(root.left.right, RAOperand)
|
|
817
|
+
assert root.left.right.name == 'b'
|
|
818
|
+
assert isinstance(root.right, RAOperand)
|
|
819
|
+
assert root.right.name == 'c'
|
|
820
|
+
|
|
821
|
+
root = RAParser.parse_query(r'a ⋉ b ⟖ c') # left semi, right outer
|
|
822
|
+
assert isinstance(root, BinaryOperators.RightOuterJoin)
|
|
823
|
+
assert isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
824
|
+
assert isinstance(root.left.left, RAOperand)
|
|
825
|
+
assert root.left.left.name == 'a'
|
|
826
|
+
assert isinstance(root.left.right, RAOperand)
|
|
827
|
+
assert root.left.right.name == 'b'
|
|
828
|
+
assert isinstance(root.right, RAOperand)
|
|
829
|
+
assert root.right.name == 'c'
|
|
830
|
+
|
|
831
|
+
root = RAParser.parse_query(r'a ⋉ b ⟗ c') # left semi, full outer
|
|
832
|
+
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
833
|
+
assert isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
834
|
+
assert isinstance(root.left.left, RAOperand)
|
|
835
|
+
assert root.left.left.name == 'a'
|
|
836
|
+
assert isinstance(root.left.right, RAOperand)
|
|
837
|
+
assert root.left.right.name == 'b'
|
|
838
|
+
assert isinstance(root.right, RAOperand)
|
|
839
|
+
assert root.right.name == 'c'
|
|
840
|
+
|
|
841
|
+
root = RAParser.parse_query(r'a ⋉ b ⋊ c') # left semi, right semi
|
|
842
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
843
|
+
assert isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
844
|
+
assert isinstance(root.left.left, RAOperand)
|
|
845
|
+
assert root.left.left.name == 'a'
|
|
846
|
+
assert isinstance(root.left.right, RAOperand)
|
|
847
|
+
assert root.left.right.name == 'b'
|
|
848
|
+
assert isinstance(root.right, RAOperand)
|
|
849
|
+
assert root.right.name == 'c'
|
|
850
|
+
|
|
851
|
+
root = RAParser.parse_query(r'a ⋊ b ⟕ c') # right semi, left outer
|
|
852
|
+
assert isinstance(root, BinaryOperators.LeftOuterJoin)
|
|
853
|
+
assert isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
854
|
+
assert isinstance(root.left.left, RAOperand)
|
|
855
|
+
assert root.left.left.name == 'a'
|
|
856
|
+
assert isinstance(root.left.right, RAOperand)
|
|
857
|
+
assert root.left.right.name == 'b'
|
|
858
|
+
assert isinstance(root.right, RAOperand)
|
|
859
|
+
assert root.right.name == 'c'
|
|
860
|
+
|
|
861
|
+
root = RAParser.parse_query(r'a ⋊ b ⟖ c') # right semi, right outer
|
|
862
|
+
assert isinstance(root, BinaryOperators.RightOuterJoin)
|
|
863
|
+
assert isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
864
|
+
assert isinstance(root.left.left, RAOperand)
|
|
865
|
+
assert root.left.left.name == 'a'
|
|
866
|
+
assert isinstance(root.left.right, RAOperand)
|
|
867
|
+
assert root.left.right.name == 'b'
|
|
868
|
+
assert isinstance(root.right, RAOperand)
|
|
869
|
+
assert root.right.name == 'c'
|
|
870
|
+
|
|
871
|
+
root = RAParser.parse_query(r'a ⋊ b ⟗ c') # right semi, full outer
|
|
872
|
+
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
873
|
+
assert isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
874
|
+
assert isinstance(root.left.left, RAOperand)
|
|
875
|
+
assert root.left.left.name == 'a'
|
|
876
|
+
assert isinstance(root.left.right, RAOperand)
|
|
877
|
+
assert root.left.right.name == 'b'
|
|
878
|
+
assert isinstance(root.right, RAOperand)
|
|
879
|
+
assert root.right.name == 'c'
|
|
880
|
+
|
|
881
|
+
root = RAParser.parse_query(r'a ⋊ b ⋉ c') # right semi, left semi
|
|
882
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
883
|
+
assert isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
884
|
+
assert isinstance(root.left.left, RAOperand)
|
|
885
|
+
assert root.left.left.name == 'a'
|
|
886
|
+
assert isinstance(root.left.right, RAOperand)
|
|
887
|
+
assert root.left.right.name == 'b'
|
|
888
|
+
assert isinstance(root.right, RAOperand)
|
|
889
|
+
assert root.right.name == 'c'
|
|
890
|
+
|
|
660
891
|
# cross join
|
|
661
892
|
root = RAParser.parse_query(r'a x b x c')
|
|
662
893
|
assert isinstance(root, BinaryOperators.Cross)
|
|
@@ -747,6 +978,22 @@ def test_binary_evaluation_order():
|
|
|
747
978
|
assert isinstance(root, BinaryOperators.Difference)
|
|
748
979
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
749
980
|
|
|
981
|
+
root = RAParser.parse_query(r'a \ b ⋉ c')
|
|
982
|
+
assert isinstance(root, BinaryOperators.Difference)
|
|
983
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftSemiJoin)
|
|
984
|
+
|
|
985
|
+
root = RAParser.parse_query(r'a ⋉ b \ c')
|
|
986
|
+
assert isinstance(root, BinaryOperators.Difference)
|
|
987
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
988
|
+
|
|
989
|
+
root = RAParser.parse_query(r'a \ b ⋊ c')
|
|
990
|
+
assert isinstance(root, BinaryOperators.Difference)
|
|
991
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightSemiJoin)
|
|
992
|
+
|
|
993
|
+
root = RAParser.parse_query(r'a ⋊ b \ c')
|
|
994
|
+
assert isinstance(root, BinaryOperators.Difference)
|
|
995
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
996
|
+
|
|
750
997
|
# difference <-> cross
|
|
751
998
|
root = RAParser.parse_query(r'a \ b x c')
|
|
752
999
|
assert isinstance(root, BinaryOperators.Difference)
|
|
@@ -807,6 +1054,22 @@ def test_binary_evaluation_order():
|
|
|
807
1054
|
assert isinstance(root, BinaryOperators.Union)
|
|
808
1055
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
809
1056
|
|
|
1057
|
+
root = RAParser.parse_query(r'a ∪ b ⋉ c')
|
|
1058
|
+
assert isinstance(root, BinaryOperators.Union)
|
|
1059
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftSemiJoin)
|
|
1060
|
+
|
|
1061
|
+
root = RAParser.parse_query(r'a ⋉ b ∪ c')
|
|
1062
|
+
assert isinstance(root, BinaryOperators.Union)
|
|
1063
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
1064
|
+
|
|
1065
|
+
root = RAParser.parse_query(r'a ∪ b ⋊ c')
|
|
1066
|
+
assert isinstance(root, BinaryOperators.Union)
|
|
1067
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightSemiJoin)
|
|
1068
|
+
|
|
1069
|
+
root = RAParser.parse_query(r'a ⋊ b ∪ c')
|
|
1070
|
+
assert isinstance(root, BinaryOperators.Union)
|
|
1071
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
1072
|
+
|
|
810
1073
|
# union <-> cross
|
|
811
1074
|
root = RAParser.parse_query(r'a ∪ b x c')
|
|
812
1075
|
assert isinstance(root, BinaryOperators.Union)
|
|
@@ -858,6 +1121,22 @@ def test_binary_evaluation_order():
|
|
|
858
1121
|
assert isinstance(root, BinaryOperators.Intersection)
|
|
859
1122
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
860
1123
|
|
|
1124
|
+
root = RAParser.parse_query(r'a ∩ b ⋉ c')
|
|
1125
|
+
assert isinstance(root, BinaryOperators.Intersection)
|
|
1126
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftSemiJoin)
|
|
1127
|
+
|
|
1128
|
+
root = RAParser.parse_query(r'a ⋉ b ∩ c')
|
|
1129
|
+
assert isinstance(root, BinaryOperators.Intersection)
|
|
1130
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
1131
|
+
|
|
1132
|
+
root = RAParser.parse_query(r'a ∩ b ⋊ c')
|
|
1133
|
+
assert isinstance(root, BinaryOperators.Intersection)
|
|
1134
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightSemiJoin)
|
|
1135
|
+
|
|
1136
|
+
root = RAParser.parse_query(r'a ⋊ b ∩ c')
|
|
1137
|
+
assert isinstance(root, BinaryOperators.Intersection)
|
|
1138
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
1139
|
+
|
|
861
1140
|
# intersection <-> cross
|
|
862
1141
|
root = RAParser.parse_query(r'a ∩ b x c')
|
|
863
1142
|
assert isinstance(root, BinaryOperators.Intersection)
|
|
@@ -909,6 +1188,22 @@ def test_binary_evaluation_order():
|
|
|
909
1188
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
910
1189
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
|
|
911
1190
|
|
|
1191
|
+
root = RAParser.parse_query(r'a ⋉ b x c')
|
|
1192
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1193
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
|
|
1194
|
+
|
|
1195
|
+
root = RAParser.parse_query(r'a x b ⋉ c')
|
|
1196
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1197
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
|
|
1198
|
+
|
|
1199
|
+
root = RAParser.parse_query(r'a ⋊ b x c')
|
|
1200
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1201
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
|
|
1202
|
+
|
|
1203
|
+
root = RAParser.parse_query(r'a x b ⋊ c')
|
|
1204
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1205
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
|
|
1206
|
+
|
|
912
1207
|
# join <-> division
|
|
913
1208
|
root = RAParser.parse_query(r'a ⋈ b ÷ c')
|
|
914
1209
|
assert isinstance(root, BinaryOperators.Join)
|
|
@@ -942,6 +1237,22 @@ def test_binary_evaluation_order():
|
|
|
942
1237
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
943
1238
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
|
|
944
1239
|
|
|
1240
|
+
root = RAParser.parse_query(r'a ⋉ b ÷ c')
|
|
1241
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1242
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
|
|
1243
|
+
|
|
1244
|
+
root = RAParser.parse_query(r'a ÷ b ⋉ c')
|
|
1245
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1246
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
|
|
1247
|
+
|
|
1248
|
+
root = RAParser.parse_query(r'a ⋊ b ÷ c')
|
|
1249
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1250
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
|
|
1251
|
+
|
|
1252
|
+
root = RAParser.parse_query(r'a ÷ b ⋊ c')
|
|
1253
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1254
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
|
|
1255
|
+
|
|
945
1256
|
# natural join <-> outer join
|
|
946
1257
|
root = RAParser.parse_query(r'a ⋈ b ⟕ c')
|
|
947
1258
|
assert isinstance(root, BinaryOperators.Join)
|
|
@@ -967,6 +1278,22 @@ def test_binary_evaluation_order():
|
|
|
967
1278
|
assert isinstance(root, BinaryOperators.Join)
|
|
968
1279
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
|
|
969
1280
|
|
|
1281
|
+
root = RAParser.parse_query(r'a ⋈ b ⋉ c')
|
|
1282
|
+
assert isinstance(root, BinaryOperators.Join)
|
|
1283
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftSemiJoin)
|
|
1284
|
+
|
|
1285
|
+
root = RAParser.parse_query(r'a ⋉ b ⋈ c')
|
|
1286
|
+
assert isinstance(root, BinaryOperators.Join)
|
|
1287
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftSemiJoin)
|
|
1288
|
+
|
|
1289
|
+
root = RAParser.parse_query(r'a ⋈ b ⋊ c')
|
|
1290
|
+
assert isinstance(root, BinaryOperators.Join)
|
|
1291
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightSemiJoin)
|
|
1292
|
+
|
|
1293
|
+
root = RAParser.parse_query(r'a ⋊ b ⋈ c')
|
|
1294
|
+
assert isinstance(root, BinaryOperators.Join)
|
|
1295
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightSemiJoin)
|
|
1296
|
+
|
|
970
1297
|
# cross <-> division
|
|
971
1298
|
root = RAParser.parse_query(r'a x b ÷ c')
|
|
972
1299
|
assert isinstance(root, BinaryOperators.Cross)
|
|
@@ -1092,6 +1419,22 @@ def test_mixed_evaluation_order():
|
|
|
1092
1419
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
1093
1420
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
|
|
1094
1421
|
|
|
1422
|
+
root = RAParser.parse_query(r'a ⋉ π [ Id ] b')
|
|
1423
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1424
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
|
|
1425
|
+
|
|
1426
|
+
root = RAParser.parse_query(r'π [ Id ] a ⋉ b')
|
|
1427
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1428
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
|
|
1429
|
+
|
|
1430
|
+
root = RAParser.parse_query(r'a ⋊ π [ Id ] b')
|
|
1431
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1432
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
|
|
1433
|
+
|
|
1434
|
+
root = RAParser.parse_query(r'π [ Id ] a ⋊ b')
|
|
1435
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1436
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
|
|
1437
|
+
|
|
1095
1438
|
# join <-> rename
|
|
1096
1439
|
root = RAParser.parse_query(r'a ⋈ β [ Id2 ← Id ] b')
|
|
1097
1440
|
assert isinstance(root, BinaryOperators.Join)
|
|
@@ -1125,6 +1468,22 @@ def test_mixed_evaluation_order():
|
|
|
1125
1468
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
1126
1469
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
|
|
1127
1470
|
|
|
1471
|
+
root = RAParser.parse_query(r'a ⋉ β [ Id2 ← Id ] b')
|
|
1472
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1473
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
|
|
1474
|
+
|
|
1475
|
+
root = RAParser.parse_query(r'β [ Id2 ← Id ] a ⋉ b')
|
|
1476
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1477
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
|
|
1478
|
+
|
|
1479
|
+
root = RAParser.parse_query(r'a ⋊ β [ Id2 ← Id ] b')
|
|
1480
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1481
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
|
|
1482
|
+
|
|
1483
|
+
root = RAParser.parse_query(r'β [ Id2 ← Id ] a ⋊ b')
|
|
1484
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1485
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
|
|
1486
|
+
|
|
1128
1487
|
# join <-> selection
|
|
1129
1488
|
root = RAParser.parse_query(r'a ⋈ σ [ Id > 1 ] b')
|
|
1130
1489
|
assert isinstance(root, BinaryOperators.Join)
|
|
@@ -1158,6 +1517,22 @@ def test_mixed_evaluation_order():
|
|
|
1158
1517
|
assert isinstance(root, BinaryOperators.FullOuterJoin)
|
|
1159
1518
|
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
|
|
1160
1519
|
|
|
1520
|
+
root = RAParser.parse_query(r'a ⋉ σ [ Id > 1 ] b')
|
|
1521
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1522
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
|
|
1523
|
+
|
|
1524
|
+
root = RAParser.parse_query(r'σ [ Id > 1 ] a ⋉ b')
|
|
1525
|
+
assert isinstance(root, BinaryOperators.LeftSemiJoin)
|
|
1526
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
|
|
1527
|
+
|
|
1528
|
+
root = RAParser.parse_query(r'a ⋊ σ [ Id > 1 ] b')
|
|
1529
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1530
|
+
assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
|
|
1531
|
+
|
|
1532
|
+
root = RAParser.parse_query(r'σ [ Id > 1 ] a ⋊ b')
|
|
1533
|
+
assert isinstance(root, BinaryOperators.RightSemiJoin)
|
|
1534
|
+
assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
|
|
1535
|
+
|
|
1161
1536
|
# cross <-> projection
|
|
1162
1537
|
root = RAParser.parse_query(r'a x π [ Id ] b')
|
|
1163
1538
|
assert isinstance(root, BinaryOperators.Cross)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jupyter-duckdb
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.106
|
|
4
4
|
Summary: a basic wrapper kernel for DuckDB
|
|
5
5
|
Home-page: https://github.com/erictroebs/jupyter-duckdb
|
|
6
6
|
Author: Eric Tröbs
|
|
@@ -45,6 +45,7 @@ There are some magic commands that make teaching easier with this kernel.
|
|
|
45
45
|
- [Ship Tests With Your Notebook](#ship-tests-with-your-notebooks)
|
|
46
46
|
- [Relational Algebra](#relational-algebra)
|
|
47
47
|
- [Domain Calculus](#domain-calculus)
|
|
48
|
+
- [Automated Parser Selection](#automated-parser-selection)
|
|
48
49
|
|
|
49
50
|
## Setup
|
|
50
51
|
|
|
@@ -281,6 +282,8 @@ The supported operations are:
|
|
|
281
282
|
The optional flag `ANALYZE` can be used to add an execution diagram to the
|
|
282
283
|
output.
|
|
283
284
|
|
|
285
|
+
You can also add comments to queries using `--` or `/* */`, just like in SQL.
|
|
286
|
+
|
|
284
287
|
The Dockerfile also installs the Jupyter Lab plugin
|
|
285
288
|
[jupyter-ra-extension](https://pypi.org/project/jupyter-ra-extension/). It adds
|
|
286
289
|
the symbols mentioned above and some other supported symbols to the toolbar for
|
|
@@ -304,4 +307,3 @@ executed cells.
|
|
|
304
307
|
If the magic command `%AUTO_PARSER` is added to a cell, a parser is
|
|
305
308
|
automatically selected. If `%GUESS_PARSER` is executed, the parser is
|
|
306
309
|
automatically selected for all subsequent cells.
|
|
307
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
duckdb_kernel/__init__.py,sha256=6auU6zeJrsA4fxPSr2PYamS8fG-SMXTn5YQFXF2cseo,33
|
|
2
2
|
duckdb_kernel/__main__.py,sha256=Z3GwHEBWoQjNm2Y84ijnbA0Lk66L7nsFREuqhZ_ptk0,165
|
|
3
3
|
duckdb_kernel/kernel.json,sha256=_7E8Ci2FSdCvnzCjsOaue8QE8AvpS5JLQuxORO5IGtA,127
|
|
4
|
-
duckdb_kernel/kernel.py,sha256=
|
|
4
|
+
duckdb_kernel/kernel.py,sha256=pn7I_mZj910A1o_RF2_n1fU2aIRIU0fKgvUzU0GdxUw,28386
|
|
5
5
|
duckdb_kernel/db/Column.py,sha256=GM5P6sFdlYK92hiKln5-6038gIDOTxh1AYbR4kiga_w,559
|
|
6
6
|
duckdb_kernel/db/Connection.py,sha256=tBXQBYt9c52RLbpl9sakNuAm0Z84--fhZ4efo8ACz-U,670
|
|
7
7
|
duckdb_kernel/db/Constraint.py,sha256=1YgUHk7s8mHCVedbcuJKyXDykj7_ybbwT3Dk9p2VMis,287
|
|
@@ -27,23 +27,23 @@ duckdb_kernel/magics/__init__.py,sha256=ggxzDzDEsKMZzYsWw9JqYVJhciJPvPVYGV7oNo9Y
|
|
|
27
27
|
duckdb_kernel/parser/DCParser.py,sha256=16c1mxa494KP9OreUKQHsSQKoDGZ7NNp2u_gi_D-dkw,2293
|
|
28
28
|
duckdb_kernel/parser/LogicParser.py,sha256=_vZwE5OPRUEN8aEC_fSZAYKR_dpexqNthXog9OFHYRY,1233
|
|
29
29
|
duckdb_kernel/parser/ParserError.py,sha256=qJQVloFtID1HgVDQ1Io247bODT1ic3oO9Z1ZrWR-2Mk,321
|
|
30
|
-
duckdb_kernel/parser/RAParser.py,sha256=
|
|
30
|
+
duckdb_kernel/parser/RAParser.py,sha256=YiXjJGdofnajqDbL3UYofIEj3mXll8Hb6dc2bX58ZNY,3284
|
|
31
31
|
duckdb_kernel/parser/__init__.py,sha256=nTmDm1ADvNPDHhVJQLxKYmArNJk6967EUXqn5AkT8FM,126
|
|
32
32
|
duckdb_kernel/parser/elements/DCOperand.py,sha256=qEg_6Us4WV1eK4Bq6oUsmFt_L_x5pJPGce_wSapzIYA,1149
|
|
33
33
|
duckdb_kernel/parser/elements/LogicElement.py,sha256=YasKHxWLDDP8UdyLIKbXzqIRA8-XaakjmvTj-1Iuzyc,280
|
|
34
34
|
duckdb_kernel/parser/elements/LogicOperand.py,sha256=B9NvriloQE5eP734dNMZBZwrdaaIfsuAmZlG1t2eMhs,1021
|
|
35
35
|
duckdb_kernel/parser/elements/LogicOperator.py,sha256=lkM4TAGkXUhsO4w4PLKVA0bgCRGPQQFpNA1FcWWOW9Q,1028
|
|
36
36
|
duckdb_kernel/parser/elements/RABinaryOperator.py,sha256=XN41stGc1e-a4dZ1AQVtQ3lEgjUGNt3dMfYXp85LEeE,538
|
|
37
|
-
duckdb_kernel/parser/elements/RAElement.py,sha256=
|
|
37
|
+
duckdb_kernel/parser/elements/RAElement.py,sha256=3qf-ZLQU5WAH_3TvEnfXUg8Y9lE2Fg01D82XutIfgjg,1661
|
|
38
38
|
duckdb_kernel/parser/elements/RAOperand.py,sha256=pghnTYCrrT6MkvynJRgVFPRoMvxIGNB3FTjaq-uCpDQ,1078
|
|
39
39
|
duckdb_kernel/parser/elements/RAOperator.py,sha256=rtqMFBIBBqT-Bwg7Qm4WQwbDrE28Nb74F_7XMeR3ks4,255
|
|
40
40
|
duckdb_kernel/parser/elements/RAUnaryOperator.py,sha256=XC1nphkSm88JaEu5V_HKnb_8JNoeBfE3EvNL4o0qh2c,654
|
|
41
41
|
duckdb_kernel/parser/elements/__init__.py,sha256=t5H6SVOm3z8r6UWRYOI7HmMIuB4Yh6TLNu4_N3jg2t0,566
|
|
42
42
|
duckdb_kernel/parser/elements/binary/Add.py,sha256=XGkZMfab01huk9EaI6JUfzkd2STbV1C_-TyC2guKE8I,190
|
|
43
|
-
duckdb_kernel/parser/elements/binary/And.py,sha256=
|
|
43
|
+
duckdb_kernel/parser/elements/binary/And.py,sha256=p6TQE49DtHlMlTkH9GqyrQVcYWsVgdIMTTCxNuNORuQ,267
|
|
44
44
|
duckdb_kernel/parser/elements/binary/ArrowLeft.py,sha256=u4fZSoyT9lfvWXBwuhUl4DdjVZAOqyVIKmMVbpElLD4,203
|
|
45
|
-
duckdb_kernel/parser/elements/binary/ConditionalSet.py,sha256=
|
|
46
|
-
duckdb_kernel/parser/elements/binary/Cross.py,sha256=
|
|
45
|
+
duckdb_kernel/parser/elements/binary/ConditionalSet.py,sha256=yzUPWO4KL8BEIQ-SiV00bdLulwHoxgoJRxebyhsRSPU,17884
|
|
46
|
+
duckdb_kernel/parser/elements/binary/Cross.py,sha256=2tsPBPGN2Bmbx50-cCdZcSj7AqQwJugA7B4gECrMdlI,696
|
|
47
47
|
duckdb_kernel/parser/elements/binary/Difference.py,sha256=4nyHhjo09UmYjtGNC3xGQxV5ROzCGOpPkyerUN-AlF4,746
|
|
48
48
|
duckdb_kernel/parser/elements/binary/Divide.py,sha256=ubekU4C1wkCTidUSMLEj5neheRx0QjhWSrsPGuXTa1g,265
|
|
49
49
|
duckdb_kernel/parser/elements/binary/Division.py,sha256=FOYxmeNGljRETwygtVn102POCUmt71t9DXdAnLFCXZM,1535
|
|
@@ -54,15 +54,17 @@ duckdb_kernel/parser/elements/binary/GreaterThanEqual.py,sha256=gO1I3J5CZwrpqZb5
|
|
|
54
54
|
duckdb_kernel/parser/elements/binary/Intersection.py,sha256=K6NrwcLTJ1BUccifVQ2R2YHR5B71LhjuLhOalxZcM0g,757
|
|
55
55
|
duckdb_kernel/parser/elements/binary/Join.py,sha256=9Z3JinEav3ulLbZZZM9DLbprloHC0oGuSm40Kf1A0mM,836
|
|
56
56
|
duckdb_kernel/parser/elements/binary/LeftOuterJoin.py,sha256=qCyHLMboNjzSDK_FREtYVwZx4UYkfRcy3FMo2pEf93o,846
|
|
57
|
+
duckdb_kernel/parser/elements/binary/LeftSemiJoin.py,sha256=u5Xv-vS0wfZsG4X5go--r3JnoDVoeeaI07zKaS2pJiM,828
|
|
57
58
|
duckdb_kernel/parser/elements/binary/LessThan.py,sha256=gNlyAvew0VyvU8x3ol88zbdh_t9GU51Kd2P_PmHUrAc,201
|
|
58
59
|
duckdb_kernel/parser/elements/binary/LessThanEqual.py,sha256=BKKXBZ4TZQ4EBxBDSNzIpFl4Oz1IGI0tI0Oj7KZUoDo,283
|
|
59
60
|
duckdb_kernel/parser/elements/binary/Minus.py,sha256=1XUTXCA0GcAcCnv9nq4B9ewT0XHJ91LlZGEW1y60POA,192
|
|
60
61
|
duckdb_kernel/parser/elements/binary/Multiply.py,sha256=OctlfN71izFYxIpMo09Qwq-3BmQmSh6-PNKBljMsBrg,195
|
|
61
62
|
duckdb_kernel/parser/elements/binary/Or.py,sha256=e1H_BuY7TQD7XHKGqJYLOebYQvrh1euWOUsXF0FT5dY,264
|
|
62
63
|
duckdb_kernel/parser/elements/binary/RightOuterJoin.py,sha256=bDxTEQ_dJZ-MUuGbU1YL69kPFR0Hdkc1fow3wxOxfAY,867
|
|
64
|
+
duckdb_kernel/parser/elements/binary/RightSemiJoin.py,sha256=QYDR27H4sA57vCKIip5o_UKfHAOLpYW6cq36fdUUO4M,848
|
|
63
65
|
duckdb_kernel/parser/elements/binary/Unequal.py,sha256=1hnC1RcPMxwKKv65OL5prunGgh9cRVDmzJutmtl7gtY,269
|
|
64
66
|
duckdb_kernel/parser/elements/binary/Union.py,sha256=VYTj4M2PVEhWiDwjnyP8qpVVbGvIBSVshlEt-SZYCBY,739
|
|
65
|
-
duckdb_kernel/parser/elements/binary/__init__.py,sha256
|
|
67
|
+
duckdb_kernel/parser/elements/binary/__init__.py,sha256=-4ZY6vpsiyT8eDkLwyFGHJFWXIMCNYNz9ejFFcjEG5U,1451
|
|
66
68
|
duckdb_kernel/parser/elements/unary/Not.py,sha256=kG0a-dp3TNjPodUMPiQ6ihtsBrbvn1iWeIPCvtyAwdo,632
|
|
67
69
|
duckdb_kernel/parser/elements/unary/Projection.py,sha256=CJ-MIf1-__1ewTjNZVy5hOz3Z18CWnCDNJBxUdpWXVQ,1112
|
|
68
70
|
duckdb_kernel/parser/elements/unary/Rename.py,sha256=Zr2n9EJ3nA476lND0Djz2b6493nnsbSpJ9kkEgk5B_Y,1273
|
|
@@ -71,12 +73,13 @@ duckdb_kernel/parser/elements/unary/__init__.py,sha256=48EDygy0pD7l3J_BlXGc-b7HY
|
|
|
71
73
|
duckdb_kernel/parser/tokenizer/Token.py,sha256=gsCzgU_zLiA-yD0FWvd2qS9LQUXbivESYH-34Glffqs,2404
|
|
72
74
|
duckdb_kernel/parser/tokenizer/Tokenizer.py,sha256=PWGgS7gYgpULiKGDho842UbaXuqmwEkccixuF10oi5g,5081
|
|
73
75
|
duckdb_kernel/parser/tokenizer/__init__.py,sha256=EOSmfc2RJwtB5cE1Hhj1JAra97tckxxS8-legybPy60,58
|
|
76
|
+
duckdb_kernel/parser/util/QuerySplitter.py,sha256=CXpF--muxC5NuSr1xc6-EVaP-ZBXLCkDNZb6zYkRTJk,2222
|
|
74
77
|
duckdb_kernel/parser/util/RenamableColumn.py,sha256=LxJhFDMUv_OxYYDLwKn63QGpBRfs08jVvhuJTzRtc9c,704
|
|
75
78
|
duckdb_kernel/parser/util/RenamableColumnList.py,sha256=5oEDbtvl4YfHbkxu_Ny2pc0EYnhCZsf7EeoNQvftbrU,3281
|
|
76
79
|
duckdb_kernel/parser/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
|
-
duckdb_kernel/tests/__init__.py,sha256
|
|
80
|
+
duckdb_kernel/tests/__init__.py,sha256=-BoPfo1FNQKnvAYt22Ioc21dbuO67QVFaV_SmS1zQw8,2731
|
|
78
81
|
duckdb_kernel/tests/test_dc.py,sha256=HPJ6JGB7yXVKIOnDHB8KwX1A16ljU0I5Y8VFcJs-KVI,15192
|
|
79
|
-
duckdb_kernel/tests/test_ra.py,sha256=
|
|
82
|
+
duckdb_kernel/tests/test_ra.py,sha256=z7ItArWzCIoPp07pgnk8NCxZ0WsjSYIeHChmi-jWE5c,69610
|
|
80
83
|
duckdb_kernel/tests/test_result_comparison.py,sha256=TQVLPKKNyV2k3i4jCfasetPfVfCzgYZr92wxQmlzPnA,3859
|
|
81
84
|
duckdb_kernel/tests/test_sql.py,sha256=p7UEokoJs2xc-url7xQ4PmWKxtExrDDYnMeoyR1JD0A,1208
|
|
82
85
|
duckdb_kernel/util/ResultSetComparator.py,sha256=RZDIfjJyx8-eR-HIqQlEYgZd_V1ympbszpVRF4TlA7o,2262
|
|
@@ -93,7 +96,7 @@ duckdb_kernel/visualization/lib/__init__.py,sha256=LYi0YPtn5fXOejbLIqbt_3KzP-Xrw
|
|
|
93
96
|
duckdb_kernel/visualization/lib/plotly-3.0.1.min.js,sha256=oy6Be7Eh6eiQFs5M7oXuPxxm9qbJXEtTpfSI93dW16Q,4653932
|
|
94
97
|
duckdb_kernel/visualization/lib/ra.css,sha256=foz1v69EQ117BDduB9QyHH978PbRs2TG1kBS4VGqZbI,57
|
|
95
98
|
duckdb_kernel/visualization/lib/ra.js,sha256=VzMRn55ztcd5Kfu2B6gdRPARpi8n-fvs8oNFnfp55Ec,1845
|
|
96
|
-
jupyter_duckdb-1.4.
|
|
97
|
-
jupyter_duckdb-1.4.
|
|
98
|
-
jupyter_duckdb-1.4.
|
|
99
|
-
jupyter_duckdb-1.4.
|
|
99
|
+
jupyter_duckdb-1.4.106.dist-info/METADATA,sha256=IRd1kZI21gT9o4a8GopNzkctOcDixojRttR3ZMk7V4I,9272
|
|
100
|
+
jupyter_duckdb-1.4.106.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
101
|
+
jupyter_duckdb-1.4.106.dist-info/top_level.txt,sha256=KvRRPMnmkQNuhyBsXoPmwyt26LRDp0O-0HN6u0Dm5jA,14
|
|
102
|
+
jupyter_duckdb-1.4.106.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|