jupyter-duckdb 0.9.2.5__tar.gz → 0.9.2.6__tar.gz

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.
Files changed (90) hide show
  1. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/PKG-INFO +2 -1
  2. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/README.md +1 -0
  3. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Difference.py +1 -1
  4. jupyter-duckdb-0.9.2.6/src/duckdb_kernel/parser/elements/binary/Division.py +48 -0
  5. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/__init__.py +3 -1
  6. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/util/RenamableColumnList.py +12 -0
  7. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/jupyter_duckdb.egg-info/PKG-INFO +2 -1
  8. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/jupyter_duckdb.egg-info/SOURCES.txt +1 -0
  9. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/test/test_ra.py +97 -4
  10. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/setup.cfg +0 -0
  11. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/setup.py +0 -0
  12. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/__init__.py +0 -0
  13. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/__main__.py +0 -0
  14. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/Column.py +0 -0
  15. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/Connection.py +0 -0
  16. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/Constraint.py +0 -0
  17. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/DatabaseError.py +0 -0
  18. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/ForeignKey.py +0 -0
  19. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/Table.py +0 -0
  20. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/__init__.py +0 -0
  21. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/error/EmptyResultError.py +0 -0
  22. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/error/__init__.py +0 -0
  23. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/implementation/duckdb/Connection.py +0 -0
  24. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/implementation/duckdb/__init__.py +0 -0
  25. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/implementation/postgres/Connection.py +0 -0
  26. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/implementation/postgres/__init__.py +0 -0
  27. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/implementation/postgres/util.py +0 -0
  28. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/implementation/sqlite/Connection.py +0 -0
  29. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/db/implementation/sqlite/__init__.py +0 -0
  30. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/kernel.json +0 -0
  31. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/kernel.py +0 -0
  32. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/magics/MagicCommand.py +0 -0
  33. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/magics/MagicCommandCallback.py +0 -0
  34. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/magics/MagicCommandException.py +0 -0
  35. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/magics/MagicCommandHandler.py +0 -0
  36. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/magics/__init__.py +0 -0
  37. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/DCParser.py +0 -0
  38. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/LogicParser.py +0 -0
  39. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/RAParser.py +0 -0
  40. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/__init__.py +0 -0
  41. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/DCOperand.py +0 -0
  42. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/LogicElement.py +0 -0
  43. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/LogicOperand.py +0 -0
  44. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/LogicOperator.py +0 -0
  45. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/RABinaryOperator.py +0 -0
  46. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/RAElement.py +0 -0
  47. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/RAOperand.py +0 -0
  48. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/RAOperator.py +0 -0
  49. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/RAUnaryOperator.py +0 -0
  50. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/__init__.py +0 -0
  51. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Add.py +0 -0
  52. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/And.py +0 -0
  53. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/ArrowLeft.py +0 -0
  54. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/ConditionalSet.py +0 -0
  55. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Cross.py +0 -0
  56. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Divide.py +0 -0
  57. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Equal.py +0 -0
  58. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/GreaterThan.py +0 -0
  59. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/GreaterThanEqual.py +0 -0
  60. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Intersection.py +0 -0
  61. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Join.py +0 -0
  62. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/LessThan.py +0 -0
  63. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/LessThanEqual.py +0 -0
  64. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Minus.py +0 -0
  65. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Multiply.py +0 -0
  66. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Or.py +0 -0
  67. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Unequal.py +0 -0
  68. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/binary/Union.py +0 -0
  69. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/unary/Not.py +0 -0
  70. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/unary/Projection.py +0 -0
  71. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/unary/Rename.py +0 -0
  72. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/unary/Selection.py +0 -0
  73. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/elements/unary/__init__.py +0 -0
  74. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/tokenizer/Token.py +0 -0
  75. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/tokenizer/Tokenizer.py +0 -0
  76. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/tokenizer/__init__.py +0 -0
  77. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/util/RenamableColumn.py +0 -0
  78. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/parser/util/__init__.py +0 -0
  79. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/util/ResultSetComparator.py +0 -0
  80. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/util/__init__.py +0 -0
  81. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/util/formatting.py +0 -0
  82. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/visualization/Drawer.py +0 -0
  83. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/visualization/RATreeDrawer.py +0 -0
  84. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/visualization/SchemaDrawer.py +0 -0
  85. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/duckdb_kernel/visualization/__init__.py +0 -0
  86. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/jupyter_duckdb.egg-info/dependency_links.txt +0 -0
  87. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/jupyter_duckdb.egg-info/requires.txt +0 -0
  88. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/src/jupyter_duckdb.egg-info/top_level.txt +0 -0
  89. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/test/test_dc.py +0 -0
  90. {jupyter-duckdb-0.9.2.5 → jupyter-duckdb-0.9.2.6}/test/test_result_comparison.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jupyter-duckdb
3
- Version: 0.9.2.5
3
+ Version: 0.9.2.6
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
@@ -247,6 +247,7 @@ The supported operations are:
247
247
  - Difference `\`
248
248
  - Natural Join `⋈`
249
249
  - Cross Product `×`
250
+ - Division `÷`
250
251
 
251
252
  The Dockerfile also installs the Jupyter Lab plugin
252
253
  [jupyter-ra-extension](https://pypi.org/project/jupyter-ra-extension/). It adds
@@ -229,6 +229,7 @@ The supported operations are:
229
229
  - Difference `\`
230
230
  - Natural Join `⋈`
231
231
  - Cross Product `×`
232
+ - Division `÷`
232
233
 
233
234
  The Dockerfile also installs the Jupyter Lab plugin
234
235
  [jupyter-ra-extension](https://pypi.org/project/jupyter-ra-extension/). It adds
@@ -17,7 +17,7 @@ class Difference(RABinaryOperator):
17
17
 
18
18
  # check number of columns
19
19
  if len(lcols) != len(rcols):
20
- raise AssertionError(f'difference can only be applied to relations with the same number of columns')
20
+ raise AssertionError('difference can only be applied to relations with the same number of columns')
21
21
 
22
22
  # create sql
23
23
  return f'{lq} EXCEPT {rq}', lcols
@@ -0,0 +1,48 @@
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 Division(RABinaryOperator):
9
+ @staticmethod
10
+ def symbols() -> Tuple[str, ...]:
11
+ return '÷', ':'
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
+ # difference and intersection preparation
19
+ diff_cols = lcols.difference(rcols)
20
+ diff_name = ', '.join(c.current_name for c in diff_cols)
21
+
22
+ inter_cols, p = lcols.intersect(rcols)
23
+ if len(inter_cols) == 0:
24
+ raise AssertionError('divison can only be applied to relations with common columns')
25
+
26
+ inter_name = ' AND '.join(f'{r.current_name} = {l.current_name}' for l, r in inter_cols)
27
+ # inter_name_left = ', '.join(l.current_name for l, _ in inter_cols)
28
+ inter_name_right = ', '.join(r.current_name for _, r in inter_cols)
29
+
30
+ print('-', diff_name)
31
+ print(inter_name)
32
+ print(inter_name_right)
33
+
34
+ # create sql
35
+ return f'''
36
+ SELECT {diff_name}
37
+ FROM ({lq}) {self._name()}
38
+ WHERE EXISTS (
39
+ SELECT {inter_name_right}
40
+ FROM ({rq}) {self._name()}
41
+ WHERE {inter_name}
42
+ )
43
+ GROUP BY {diff_name}
44
+ HAVING COUNT(*) = (
45
+ SELECT COUNT(*)
46
+ FROM ({rq}) {self._name()}
47
+ )
48
+ ''', diff_cols
@@ -9,6 +9,7 @@ from .And import And
9
9
  from .ArrowLeft import ArrowLeft
10
10
  from .ConditionalSet import ConditionalSet
11
11
  from .Divide import Divide
12
+ from .Division import Division
12
13
  from .Equal import Equal
13
14
  from .GreaterThan import GreaterThan
14
15
  from .GreaterThanEqual import GreaterThanEqual
@@ -33,7 +34,8 @@ RA_BINARY_OPERATORS = [
33
34
  Union,
34
35
  Intersection,
35
36
  Join,
36
- Cross
37
+ Cross,
38
+ Division
37
39
  ]
38
40
 
39
41
  DC_SET = ConditionalSet
@@ -61,6 +61,18 @@ class RenamableColumnList(list[RenamableColumn]):
61
61
 
62
62
  return RenamableColumnList(cols.values())
63
63
 
64
+ def difference(self, other: 'RenamableColumnList') -> 'RenamableColumnList':
65
+ cols: Dict[str, RenamableColumn] = {}
66
+
67
+ for col in self:
68
+ cols[col.name] = col
69
+
70
+ for col in other:
71
+ if col.name in cols:
72
+ del cols[col.name]
73
+
74
+ return RenamableColumnList(cols.values())
75
+
64
76
  def intersect(self, other: 'RenamableColumnList') \
65
77
  -> Tuple[List[Tuple[RenamableColumn, RenamableColumn]], 'RenamableColumnList']:
66
78
  self_cols: Dict[str, RenamableColumn] = {col.name: col for col in self}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jupyter-duckdb
3
- Version: 0.9.2.5
3
+ Version: 0.9.2.6
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
@@ -247,6 +247,7 @@ The supported operations are:
247
247
  - Difference `\`
248
248
  - Natural Join `⋈`
249
249
  - Cross Product `×`
250
+ - Division `÷`
250
251
 
251
252
  The Dockerfile also installs the Jupyter Lab plugin
252
253
  [jupyter-ra-extension](https://pypi.org/project/jupyter-ra-extension/). It adds
@@ -46,6 +46,7 @@ src/duckdb_kernel/parser/elements/binary/ConditionalSet.py
46
46
  src/duckdb_kernel/parser/elements/binary/Cross.py
47
47
  src/duckdb_kernel/parser/elements/binary/Difference.py
48
48
  src/duckdb_kernel/parser/elements/binary/Divide.py
49
+ src/duckdb_kernel/parser/elements/binary/Division.py
49
50
  src/duckdb_kernel/parser/elements/binary/Equal.py
50
51
  src/duckdb_kernel/parser/elements/binary/GreaterThan.py
51
52
  src/duckdb_kernel/parser/elements/binary/GreaterThanEqual.py
@@ -9,10 +9,10 @@ from . import Connection
9
9
 
10
10
  def test_case_insensitivity():
11
11
  for query in (
12
- 'users',
13
- 'Users',
14
- 'USERS',
15
- 'userS'
12
+ 'users',
13
+ 'Users',
14
+ 'USERS',
15
+ 'userS'
16
16
  ):
17
17
  root = RAParser.parse_query(query)
18
18
 
@@ -72,6 +72,27 @@ def test_binary_operator_difference():
72
72
  ]
73
73
 
74
74
 
75
+ def test_binary_operator_division():
76
+ for query in (
77
+ r'π [show_id, season_number, episode_number] (episodes) ÷ π [ episode_number ] (σ [ show_id = 1 AND season_number = 1 ] (episodes))',
78
+ r'π [show_id, season_number, episode_number] (episodes) : π [ episode_number ] (σ [ show_id = 1 AND season_number = 1 ] (episodes))',
79
+ ):
80
+ root = RAParser.parse_query(query)
81
+
82
+ assert isinstance(root, BinaryOperators.Division)
83
+ assert isinstance(root.left, UnaryOperators.Projection)
84
+ assert isinstance(root.left.target, RAOperand) and root.left.target.name == 'episodes'
85
+ assert isinstance(root.right, UnaryOperators.Projection)
86
+ assert isinstance(root.right.target, UnaryOperators.Selection)
87
+ assert isinstance(root.right.target.target, RAOperand) and root.right.target.target.name == 'episodes'
88
+
89
+ with Connection() as con:
90
+ assert con.execute_ra(root) == [
91
+ (1, 1),
92
+ (1, 2)
93
+ ]
94
+
95
+
75
96
  def test_binary_operator_intersection():
76
97
  for query in (
77
98
  r'users ∩ banned_users',
@@ -325,6 +346,15 @@ def test_binary_evaluation_order():
325
346
  assert isinstance(root, BinaryOperators.Difference)
326
347
  assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
327
348
 
349
+ # difference <-> division
350
+ root = RAParser.parse_query(r'a \ b ÷ c')
351
+ assert isinstance(root, BinaryOperators.Difference)
352
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
353
+
354
+ root = RAParser.parse_query(r'a ÷ b \ c')
355
+ assert isinstance(root, BinaryOperators.Difference)
356
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
357
+
328
358
  # union <-> intersection
329
359
  root = RAParser.parse_query(r'a ∪ b ∩ c')
330
360
  assert isinstance(root, BinaryOperators.Union)
@@ -352,6 +382,15 @@ def test_binary_evaluation_order():
352
382
  assert isinstance(root, BinaryOperators.Union)
353
383
  assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
354
384
 
385
+ # union <-> division
386
+ root = RAParser.parse_query(r'a ∪ b ÷ c')
387
+ assert isinstance(root, BinaryOperators.Union)
388
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
389
+
390
+ root = RAParser.parse_query(r'a ÷ b ∪ c')
391
+ assert isinstance(root, BinaryOperators.Union)
392
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
393
+
355
394
  # intersection <-> join
356
395
  root = RAParser.parse_query(r'a ∩ b ⋈ c')
357
396
  assert isinstance(root, BinaryOperators.Intersection)
@@ -370,6 +409,15 @@ def test_binary_evaluation_order():
370
409
  assert isinstance(root, BinaryOperators.Intersection)
371
410
  assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
372
411
 
412
+ # intersection <-> division
413
+ root = RAParser.parse_query(r'a ∩ b ÷ c')
414
+ assert isinstance(root, BinaryOperators.Intersection)
415
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
416
+
417
+ root = RAParser.parse_query(r'a ÷ b ∩ c')
418
+ assert isinstance(root, BinaryOperators.Intersection)
419
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
420
+
373
421
  # join <-> cross
374
422
  root = RAParser.parse_query(r'a ⋈ b x c')
375
423
  assert isinstance(root, BinaryOperators.Join)
@@ -379,6 +427,24 @@ def test_binary_evaluation_order():
379
427
  assert isinstance(root, BinaryOperators.Join)
380
428
  assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
381
429
 
430
+ # join <-> division
431
+ root = RAParser.parse_query(r'a ⋈ b ÷ c')
432
+ assert isinstance(root, BinaryOperators.Join)
433
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
434
+
435
+ root = RAParser.parse_query(r'a ÷ b ⋈ c')
436
+ assert isinstance(root, BinaryOperators.Join)
437
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
438
+
439
+ # cross <-> division
440
+ root = RAParser.parse_query(r'a x b ÷ c')
441
+ assert isinstance(root, BinaryOperators.Cross)
442
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
443
+
444
+ root = RAParser.parse_query(r'a ÷ b x c')
445
+ assert isinstance(root, BinaryOperators.Cross)
446
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
447
+
382
448
 
383
449
  def test_mixed_evaluation_order():
384
450
  # difference <-> projection
@@ -516,6 +582,33 @@ def test_mixed_evaluation_order():
516
582
  assert isinstance(root, BinaryOperators.Cross)
517
583
  assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
518
584
 
585
+ # division <-> projection
586
+ root = RAParser.parse_query(r'a ÷ π [ id ] b')
587
+ assert isinstance(root, BinaryOperators.Division)
588
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
589
+
590
+ root = RAParser.parse_query(r'π [ id ] a ÷ b')
591
+ assert isinstance(root, BinaryOperators.Division)
592
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
593
+
594
+ # division <-> rename
595
+ root = RAParser.parse_query(r'a ÷ β [ id2 ← id ] b')
596
+ assert isinstance(root, BinaryOperators.Division)
597
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
598
+
599
+ root = RAParser.parse_query(r'β [ id2 ← id ] a ÷ b')
600
+ assert isinstance(root, BinaryOperators.Division)
601
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
602
+
603
+ # division <-> selection
604
+ root = RAParser.parse_query(r'a ÷ σ [ id > 1 ] b')
605
+ assert isinstance(root, BinaryOperators.Division)
606
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
607
+
608
+ root = RAParser.parse_query(r'σ [ id > 1 ] a ÷ b')
609
+ assert isinstance(root, BinaryOperators.Division)
610
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
611
+
519
612
 
520
613
  def test_special_queries():
521
614
  with Connection() as con: