jupyter-duckdb 1.2.7__py3-none-any.whl → 1.2.100__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.
@@ -0,0 +1,1250 @@
1
+ import pytest
2
+
3
+ import duckdb_kernel.parser.elements.binary as BinaryOperators
4
+ import duckdb_kernel.parser.elements.unary as UnaryOperators
5
+ from duckdb_kernel.parser import RAParser, RAParserError
6
+ from duckdb_kernel.parser.elements import RAOperand, LogicElement
7
+ from . import Connection
8
+
9
+
10
+ def test_case_insensitivity():
11
+ for query in (
12
+ 'Users',
13
+ 'users',
14
+ 'USERS',
15
+ 'userS'
16
+ ):
17
+ root = RAParser.parse_query(query)
18
+
19
+ # root is an RAOperand
20
+ assert isinstance(root, RAOperand)
21
+
22
+ # Root's name is the relation name in whatever case
23
+ # it has been written.
24
+ assert root.name == query
25
+
26
+ # execute to test case insensitivity
27
+ with Connection() as con:
28
+ cols, rows = con.execute_ra_return_cols(root)
29
+
30
+ assert [c.lower() for c in cols] == [
31
+ 'id',
32
+ 'username'
33
+ ]
34
+ assert rows == [
35
+ (1, 'Alice'),
36
+ (2, 'Bob'),
37
+ (3, 'Charlie')
38
+ ]
39
+
40
+ for query in (
41
+ 'π [ Username ] ( Users )',
42
+ 'π [ username ] ( Users )',
43
+ 'π [ userName ] ( Users )'
44
+ ):
45
+ root = RAParser.parse_query(query)
46
+
47
+ # execute to test case insensitivity
48
+ with (Connection() as con):
49
+ cols, rows = con.execute_ra_return_cols(root)
50
+
51
+ assert [c.lower() for c in cols] == [
52
+ 'username'
53
+ ]
54
+ assert rows == [
55
+ ('Alice',),
56
+ ('Bob',),
57
+ ('Charlie',)
58
+ ]
59
+
60
+ for query in (
61
+ 'π [ Id, Username ] ( Users )',
62
+ 'π [ id, username ] ( Users )',
63
+ 'π [ iD, userName ] ( Users )'
64
+ ):
65
+ root = RAParser.parse_query(query)
66
+
67
+ # execute to test case insensitivity
68
+ with Connection() as con:
69
+ cols, rows = con.execute_ra_return_cols(root)
70
+
71
+ assert [c.lower() for c in cols] == [
72
+ 'id',
73
+ 'username'
74
+ ]
75
+ assert rows == [
76
+ (1, 'Alice'),
77
+ (2, 'Bob'),
78
+ (3, 'Charlie')
79
+ ]
80
+
81
+
82
+ def test_binary_operator_cross():
83
+ for query in (
84
+ r'Shows x Seasons',
85
+ ):
86
+ root = RAParser.parse_query(query)
87
+
88
+ assert isinstance(root, BinaryOperators.Cross)
89
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Shows'
90
+ assert isinstance(root.right, RAOperand) and root.right.name == 'Seasons'
91
+
92
+ with Connection() as con:
93
+ cols, rows = con.execute_ra_return_cols(root)
94
+
95
+ assert [c.lower() for c in cols] == [
96
+ 'shows.showid',
97
+ 'shows.showname',
98
+ 'seasons.seasonnumber',
99
+ 'seasons.showid',
100
+ 'seasons.seasonname'
101
+ ]
102
+ assert rows == [
103
+ (1, 'Show 1', 1, 1, 'Show 1 / Season 1'),
104
+ (1, 'Show 1', 1, 2, 'Show 2 / Season 1'),
105
+ (1, 'Show 1', 2, 1, 'Show 1 / Season 2'),
106
+ (1, 'Show 1', 2, 2, 'Show 2 / Season 2'),
107
+ (2, 'Show 2', 1, 1, 'Show 1 / Season 1'),
108
+ (2, 'Show 2', 1, 2, 'Show 2 / Season 1'),
109
+ (2, 'Show 2', 2, 1, 'Show 1 / Season 2'),
110
+ (2, 'Show 2', 2, 2, 'Show 2 / Season 2')
111
+ ]
112
+
113
+
114
+ def test_binary_operator_difference():
115
+ for query in (
116
+ r'Users - BannedUsers',
117
+ r'Users \ BannedUsers',
118
+ ):
119
+ root = RAParser.parse_query(query)
120
+
121
+ assert isinstance(root, BinaryOperators.Difference)
122
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
123
+ assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
124
+
125
+ with Connection() as con:
126
+ cols, rows = con.execute_ra_return_cols(root)
127
+
128
+ assert [c.lower() for c in cols] == [
129
+ 'id',
130
+ 'username'
131
+ ]
132
+ assert rows == [
133
+ (1, 'Alice'),
134
+ (3, 'Charlie')
135
+ ]
136
+
137
+
138
+ def test_binary_operator_division():
139
+ for query in (
140
+ r'π [ShowId, SeasonNumber, EpisodeNumber] (Episodes) ÷ π [ EpisodeNumber ] (σ [ ShowId = 1 AND SeasonNumber = 1 ] (Episodes))',
141
+ r'π [ShowId, SeasonNumber, EpisodeNumber] (Episodes) : π [ EpisodeNumber ] (σ [ ShowId = 1 AND SeasonNumber = 1 ] (Episodes))',
142
+ ):
143
+ root = RAParser.parse_query(query)
144
+
145
+ assert isinstance(root, BinaryOperators.Division)
146
+ assert isinstance(root.left, UnaryOperators.Projection)
147
+ assert isinstance(root.left.target, RAOperand) and root.left.target.name == 'Episodes'
148
+ assert isinstance(root.right, UnaryOperators.Projection)
149
+ assert isinstance(root.right.target, UnaryOperators.Selection)
150
+ assert isinstance(root.right.target.target, RAOperand) and root.right.target.target.name == 'Episodes'
151
+
152
+ with Connection() as con:
153
+ cols, rows = con.execute_ra_return_cols(root)
154
+
155
+ assert [c.lower() for c in cols] == [
156
+ 'showid',
157
+ 'seasonnumber'
158
+ ]
159
+ assert rows == [
160
+ (1, 1),
161
+ (1, 2)
162
+ ]
163
+
164
+
165
+ def test_binary_operator_intersection():
166
+ for query in (
167
+ r'Users ∩ BannedUsers',
168
+ r'Users cap BannedUsers'
169
+ ):
170
+ root = RAParser.parse_query(query)
171
+
172
+ assert isinstance(root, BinaryOperators.Intersection)
173
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
174
+ assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
175
+
176
+ with Connection() as con:
177
+ cols, rows = con.execute_ra_return_cols(root)
178
+
179
+ assert [c.lower() for c in cols] == [
180
+ 'id',
181
+ 'username'
182
+ ]
183
+ assert rows == [
184
+ (2, 'Bob')
185
+ ]
186
+
187
+
188
+ def test_binary_operator_join():
189
+ for query in (
190
+ r'Shows ⋈ Seasons',
191
+ r'Shows join Seasons'
192
+ ):
193
+ root = RAParser.parse_query(query)
194
+
195
+ assert isinstance(root, BinaryOperators.Join)
196
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Shows'
197
+ assert isinstance(root.right, RAOperand) and root.right.name == 'Seasons'
198
+
199
+ with Connection() as con:
200
+ cols, rows = con.execute_ra_return_cols(root)
201
+
202
+ assert [c.lower() for c in cols] == [
203
+ 'shows.showid',
204
+ 'shows.showname',
205
+ 'seasons.seasonnumber',
206
+ 'seasons.seasonname'
207
+ ]
208
+ assert rows == [
209
+ (1, 'Show 1', 1, 'Show 1 / Season 1'),
210
+ (1, 'Show 1', 2, 'Show 1 / Season 2'),
211
+ (2, 'Show 2', 1, 'Show 2 / Season 1'),
212
+ (2, 'Show 2', 2, 'Show 2 / Season 2')
213
+ ]
214
+
215
+
216
+ def test_binary_operator_ljoin():
217
+ for query in (
218
+ r'Users ⟕ BannedUsers',
219
+ r'Users ljoin BannedUsers'
220
+ ):
221
+ root = RAParser.parse_query(query)
222
+
223
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
224
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
225
+ assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
226
+
227
+ with Connection() as con:
228
+ cols, rows = con.execute_ra_return_cols(root)
229
+
230
+ assert [c.lower() for c in cols] == [
231
+ 'users.id',
232
+ 'users.username',
233
+ 'bannedusers.bannedusername'
234
+ ]
235
+ assert rows == [
236
+ (1, 'Alice', None),
237
+ (2, 'Bob', 'Bob'),
238
+ (3, 'Charlie', None),
239
+ ]
240
+
241
+
242
+ def test_binary_operator_rjoin():
243
+ for query in (
244
+ r'Users ⟖ BannedUsers',
245
+ r'Users rjoin BannedUsers'
246
+ ):
247
+ root = RAParser.parse_query(query)
248
+
249
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
250
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
251
+ assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
252
+
253
+ with Connection() as con:
254
+ cols, rows = con.execute_ra_return_cols(root)
255
+
256
+ assert [c.lower() for c in cols] == [
257
+ 'bannedusers.id',
258
+ 'users.username',
259
+ 'bannedusers.bannedusername'
260
+ ]
261
+ assert rows == [
262
+ (2, 'Bob', 'Bob'),
263
+ (4, None, 'David')
264
+ ]
265
+
266
+
267
+ def test_binary_operator_fjoin():
268
+ for query in (
269
+ r'Users ⟗ BannedUsers',
270
+ r'Users fjoin BannedUsers',
271
+ r'Users ojoin BannedUsers'
272
+ ):
273
+ root = RAParser.parse_query(query)
274
+
275
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
276
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
277
+ assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
278
+
279
+ with Connection() as con:
280
+ cols, rows = con.execute_ra_return_cols(root)
281
+
282
+ assert [c.lower() for c in cols] == [
283
+ 'users.id',
284
+ 'users.username',
285
+ 'bannedusers.bannedusername'
286
+ ]
287
+ assert rows == [
288
+ (1, 'Alice', None),
289
+ (2, 'Bob', 'Bob'),
290
+ (3, 'Charlie', None),
291
+ (4, None, 'David')
292
+ ]
293
+
294
+
295
+ def test_binary_operator_union():
296
+ for query in (
297
+ r'Users ∪ BannedUsers',
298
+ r'Users cup BannedUsers'
299
+ ):
300
+ root = RAParser.parse_query(query)
301
+
302
+ assert isinstance(root, BinaryOperators.Union)
303
+ assert isinstance(root.left, RAOperand) and root.left.name == 'Users'
304
+ assert isinstance(root.right, RAOperand) and root.right.name == 'BannedUsers'
305
+
306
+ with Connection() as con:
307
+ cols, rows = con.execute_ra_return_cols(root)
308
+
309
+ assert [c.lower() for c in cols] == [
310
+ 'id',
311
+ 'username'
312
+ ]
313
+ assert rows == [
314
+ (1, 'Alice'),
315
+ (2, 'Bob'),
316
+ (3, 'Charlie'),
317
+ (4, 'David')
318
+ ]
319
+
320
+
321
+ def test_unary_operator_projection():
322
+ with Connection() as con:
323
+ for query in (
324
+ r'π Id Users',
325
+ r'π [ Id ] Users',
326
+ r'π [ Id ] ( Users )',
327
+ r'π[Id](Users)',
328
+ r'Pi Id Users',
329
+ r'Pi [ Id ] Users',
330
+ r'Pi [ Id ] ( Users )',
331
+ r'Pi[Id](Users)'
332
+ ):
333
+ root = RAParser.parse_query(query)
334
+
335
+ assert isinstance(root, UnaryOperators.Projection)
336
+ assert isinstance(root.arg, LogicElement)
337
+ assert isinstance(root.target, RAOperand) and root.target.name == 'Users'
338
+
339
+ cols, rows = con.execute_ra_return_cols(root)
340
+
341
+ assert [c.lower() for c in cols] == [
342
+ 'id'
343
+ ]
344
+ assert rows == [
345
+ (1,),
346
+ (2,),
347
+ (3,)
348
+ ]
349
+
350
+ for query in (
351
+ r'π Id π Id, Username Users',
352
+ r'π [ Id ] (π [ Id, Username ] (Users))',
353
+ r'π[Id]π[Id,Username]Users',
354
+ r'Pi Id Pi Id, Username Users',
355
+ r'Pi [ Id ] (Pi [ Id, Username ] (Users))',
356
+ r'Pi[Id]Pi[Id,Username]Users'
357
+ ):
358
+ root = RAParser.parse_query(query)
359
+
360
+ assert isinstance(root, UnaryOperators.Projection)
361
+ assert isinstance(root.arg, LogicElement)
362
+ assert isinstance(root.target, UnaryOperators.Projection)
363
+ assert isinstance(root.target.arg, LogicElement)
364
+ assert isinstance(root.target.target, RAOperand) and root.target.target.name == 'Users'
365
+
366
+ cols, rows = con.execute_ra_return_cols(root)
367
+
368
+ assert [c.lower() for c in cols] == [
369
+ 'id'
370
+ ]
371
+ assert rows == [
372
+ (1,),
373
+ (2,),
374
+ (3,)
375
+ ]
376
+
377
+
378
+ def test_unary_operator_rename():
379
+ with Connection() as con:
380
+ for query in (
381
+ r'β Id2 ← Id Users',
382
+ r'β [ Id2 ← Id ] Users',
383
+ r'β [ Id2 ← Id ] ( Users )',
384
+ r'β[Id2←Id](Users)',
385
+ r'Beta Id2 ← Id Users',
386
+ r'Beta [ Id2 ← Id ] Users',
387
+ r'Beta [ Id2 ← Id ] ( Users )',
388
+ r'Beta[Id2←Id](Users)',
389
+ r'Beta Id2 <- Id Users',
390
+ r'Beta [ Id2 <- Id ] Users',
391
+ r'Beta [ Id2 <- Id ] ( Users )',
392
+ r'Beta[Id2<-Id](Users)'
393
+ ):
394
+ root = RAParser.parse_query(query)
395
+
396
+ assert isinstance(root, UnaryOperators.Rename)
397
+ assert isinstance(root.arg, LogicElement)
398
+ assert isinstance(root.target, RAOperand) and root.target.name == 'Users'
399
+
400
+ cols, rows = con.execute_ra_return_cols(root)
401
+
402
+ assert [c.lower() for c in cols] == [
403
+ 'id2',
404
+ 'username'
405
+ ]
406
+ assert rows == [
407
+ (1, 'Alice'),
408
+ (2, 'Bob'),
409
+ (3, 'Charlie')
410
+ ]
411
+
412
+ for query in (
413
+ r'β Id ← Id2 β Id2 ← Id Users',
414
+ r'β [Id ← Id2] (β [Id2 ← Id] (Users))',
415
+ r'βId←Id2βId2←Id Users',
416
+ r'beta Id ← Id2 beta Id2 ← Id Users',
417
+ r'beta [Id ← Id2] (beta [Id2 ← Id] (Users))',
418
+ r'beta Id←Id2 beta Id2←Id Users',
419
+ r'beta Id <- Id2 beta Id2 <- Id Users',
420
+ r'beta [Id <- Id2] (beta [Id2 <- Id] (Users))',
421
+ r'beta Id<-Id2 beta Id2<-Id Users'
422
+ ):
423
+ root = RAParser.parse_query(query)
424
+
425
+ assert isinstance(root, UnaryOperators.Rename)
426
+ assert isinstance(root.arg, LogicElement)
427
+ assert isinstance(root.target, UnaryOperators.Rename)
428
+ assert isinstance(root.target.arg, LogicElement)
429
+ assert isinstance(root.target.target, RAOperand) and root.target.target.name == 'Users'
430
+
431
+ cols, rows = con.execute_ra_return_cols(root)
432
+
433
+ assert [c.lower() for c in cols] == [
434
+ 'id',
435
+ 'username'
436
+ ]
437
+ assert rows == [
438
+ (1, 'Alice'),
439
+ (2, 'Bob'),
440
+ (3, 'Charlie')
441
+ ]
442
+
443
+
444
+ def test_unary_operator_selection():
445
+ with Connection() as con:
446
+ for query in (
447
+ r'σ Id > 1 Users',
448
+ r'σ [ Id > 1 ] Users',
449
+ r'σ [ Id > 1 ] ( Users )',
450
+ r'σ[Id>1](Users)',
451
+ r'Sigma Id > 1 Users',
452
+ r'Sigma [ Id > 1 ] Users',
453
+ r'Sigma [ Id > 1 ] ( Users )',
454
+ r'Sigma[Id>1](Users)'
455
+ ):
456
+ root = RAParser.parse_query(query)
457
+
458
+ assert isinstance(root, UnaryOperators.Selection)
459
+ assert isinstance(root.target, RAOperand) and root.target.name == 'Users'
460
+ assert isinstance(root.arg, LogicElement)
461
+
462
+ cols, rows = con.execute_ra_return_cols(root)
463
+
464
+ assert [c.lower() for c in cols] == [
465
+ 'id',
466
+ 'username'
467
+ ]
468
+ assert rows == [
469
+ (2, 'Bob'),
470
+ (3, 'Charlie')
471
+ ]
472
+
473
+ for query in (
474
+ r'σ Id > 1 σ Id > 0 Users',
475
+ r'σ [ Id > 1 ] (σ [Id > 1] (Users))',
476
+ r'σ[Id>1]σ[Id>1]Users',
477
+ r'Sigma Id > 1 Sigma Id > 0 Users',
478
+ r'Sigma [ Id > 1 ] (Sigma [Id > 1] (Users))',
479
+ r'Sigma[Id>1]Sigma[Id>1]Users'
480
+ ):
481
+ root = RAParser.parse_query(query)
482
+
483
+ assert isinstance(root, UnaryOperators.Selection)
484
+ assert isinstance(root.arg, LogicElement)
485
+ assert isinstance(root.target, UnaryOperators.Selection)
486
+ assert isinstance(root.target.arg, LogicElement)
487
+ assert isinstance(root.target.target, RAOperand) and root.target.target.name == 'Users'
488
+
489
+ cols, rows = con.execute_ra_return_cols(root)
490
+
491
+ assert [c.lower() for c in cols] == [
492
+ 'id',
493
+ 'username'
494
+ ]
495
+ assert rows == [
496
+ (2, 'Bob'),
497
+ (3, 'Charlie')
498
+ ]
499
+
500
+
501
+ def test_unary_inner_to_outer_evaluation_order():
502
+ root = RAParser.parse_query(r'π [ Id ] π [ Id, Username ] (Users)')
503
+ assert isinstance(root, UnaryOperators.Projection) and root.columns == ('Id',)
504
+ assert isinstance(root.target, UnaryOperators.Projection) and root.target.columns == ('Id', 'Username')
505
+
506
+ root = RAParser.parse_query(r'σ [ Id > 2 ] σ [ Id > 1 ] (Users)')
507
+ assert isinstance(root, UnaryOperators.Selection)
508
+ assert isinstance(root.condition, BinaryOperators.GreaterThan)
509
+ assert root.condition.left == ('Id',) and root.condition.right == ('2',)
510
+ assert isinstance(root.target, UnaryOperators.Selection)
511
+ assert isinstance(root.target.condition, BinaryOperators.GreaterThan)
512
+ assert root.target.condition.left == ('Id',) and root.target.condition.right == ('1',)
513
+
514
+ root = RAParser.parse_query(r'β [ Id3 ← Id2 ] β [ Id2 ← Id ] (Users)')
515
+ assert isinstance(root, UnaryOperators.Rename)
516
+ assert isinstance(root.arrow, BinaryOperators.ArrowLeft)
517
+ assert root.arrow.left == ('Id3',) and root.arrow.right == ('Id2',)
518
+ assert isinstance(root.target, UnaryOperators.Rename)
519
+ assert isinstance(root.target.arrow, BinaryOperators.ArrowLeft)
520
+ assert root.target.arrow.left == ('Id2',) and root.target.arrow.right == ('Id',)
521
+
522
+
523
+ def test_binary_left_to_right_evaluation_order():
524
+ # difference
525
+ root = RAParser.parse_query(r'a \ b \ c')
526
+ assert isinstance(root, BinaryOperators.Difference)
527
+ assert isinstance(root.left, BinaryOperators.Difference)
528
+ assert isinstance(root.left.left, RAOperand)
529
+ assert root.left.left.name == 'a'
530
+ assert isinstance(root.left.right, RAOperand)
531
+ assert root.left.right.name == 'b'
532
+ assert isinstance(root.right, RAOperand)
533
+ assert root.right.name == 'c'
534
+
535
+ # union
536
+ root = RAParser.parse_query(r'a ∪ b ∪ c')
537
+ assert isinstance(root, BinaryOperators.Union)
538
+ assert isinstance(root.left, BinaryOperators.Union)
539
+ assert isinstance(root.left.left, RAOperand)
540
+ assert root.left.left.name == 'a'
541
+ assert isinstance(root.left.right, RAOperand)
542
+ assert root.left.right.name == 'b'
543
+ assert isinstance(root.right, RAOperand)
544
+ assert root.right.name == 'c'
545
+
546
+ # intersection
547
+ root = RAParser.parse_query(r'a ∩ b ∩ c')
548
+ assert isinstance(root, BinaryOperators.Intersection)
549
+ assert isinstance(root.left, BinaryOperators.Intersection)
550
+ assert isinstance(root.left.left, RAOperand)
551
+ assert root.left.left.name == 'a'
552
+ assert isinstance(root.left.right, RAOperand)
553
+ assert root.left.right.name == 'b'
554
+ assert isinstance(root.right, RAOperand)
555
+ assert root.right.name == 'c'
556
+
557
+ # natural join
558
+ root = RAParser.parse_query(r'a ⋈ b ⋈ c')
559
+ assert isinstance(root, BinaryOperators.Join)
560
+ assert isinstance(root.left, BinaryOperators.Join)
561
+ assert isinstance(root.left.left, RAOperand)
562
+ assert root.left.left.name == 'a'
563
+ assert isinstance(root.left.right, RAOperand)
564
+ assert root.left.right.name == 'b'
565
+ assert isinstance(root.right, RAOperand)
566
+ assert root.right.name == 'c'
567
+
568
+ # outer join
569
+ root = RAParser.parse_query(r'a ⟕ b ⟕ c')
570
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
571
+ assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
572
+ assert isinstance(root.left.left, RAOperand)
573
+ assert root.left.left.name == 'a'
574
+ assert isinstance(root.left.right, RAOperand)
575
+ assert root.left.right.name == 'b'
576
+ assert isinstance(root.right, RAOperand)
577
+ assert root.right.name == 'c'
578
+
579
+ root = RAParser.parse_query(r'a ⟖ b ⟖ c')
580
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
581
+ assert isinstance(root.left, BinaryOperators.RightOuterJoin)
582
+ assert isinstance(root.left.left, RAOperand)
583
+ assert root.left.left.name == 'a'
584
+ assert isinstance(root.left.right, RAOperand)
585
+ assert root.left.right.name == 'b'
586
+ assert isinstance(root.right, RAOperand)
587
+ assert root.right.name == 'c'
588
+
589
+ root = RAParser.parse_query(r'a ⟗ b ⟗ c')
590
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
591
+ assert isinstance(root.left, BinaryOperators.FullOuterJoin)
592
+ assert isinstance(root.left.left, RAOperand)
593
+ assert root.left.left.name == 'a'
594
+ assert isinstance(root.left.right, RAOperand)
595
+ assert root.left.right.name == 'b'
596
+ assert isinstance(root.right, RAOperand)
597
+ assert root.right.name == 'c'
598
+
599
+ # mixed outer joins
600
+ root = RAParser.parse_query(r'a ⟕ b ⟖ c')
601
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
602
+ assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
603
+ assert isinstance(root.left.left, RAOperand)
604
+ assert root.left.left.name == 'a'
605
+ assert isinstance(root.left.right, RAOperand)
606
+ assert root.left.right.name == 'b'
607
+ assert isinstance(root.right, RAOperand)
608
+ assert root.right.name == 'c'
609
+
610
+ root = RAParser.parse_query(r'a ⟕ b ⟗ c')
611
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
612
+ assert isinstance(root.left, BinaryOperators.LeftOuterJoin)
613
+ assert isinstance(root.left.left, RAOperand)
614
+ assert root.left.left.name == 'a'
615
+ assert isinstance(root.left.right, RAOperand)
616
+ assert root.left.right.name == 'b'
617
+ assert isinstance(root.right, RAOperand)
618
+ assert root.right.name == 'c'
619
+
620
+ root = RAParser.parse_query(r'a ⟖ b ⟕ c')
621
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
622
+ assert isinstance(root.left, BinaryOperators.RightOuterJoin)
623
+ assert isinstance(root.left.left, RAOperand)
624
+ assert root.left.left.name == 'a'
625
+ assert isinstance(root.left.right, RAOperand)
626
+ assert root.left.right.name == 'b'
627
+ assert isinstance(root.right, RAOperand)
628
+ assert root.right.name == 'c'
629
+
630
+ root = RAParser.parse_query(r'a ⟖ b ⟗ c')
631
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
632
+ assert isinstance(root.left, BinaryOperators.RightOuterJoin)
633
+ assert isinstance(root.left.left, RAOperand)
634
+ assert root.left.left.name == 'a'
635
+ assert isinstance(root.left.right, RAOperand)
636
+ assert root.left.right.name == 'b'
637
+ assert isinstance(root.right, RAOperand)
638
+ assert root.right.name == 'c'
639
+
640
+ root = RAParser.parse_query(r'a ⟗ b ⟕ c')
641
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
642
+ assert isinstance(root.left, BinaryOperators.FullOuterJoin)
643
+ assert isinstance(root.left.left, RAOperand)
644
+ assert root.left.left.name == 'a'
645
+ assert isinstance(root.left.right, RAOperand)
646
+ assert root.left.right.name == 'b'
647
+ assert isinstance(root.right, RAOperand)
648
+ assert root.right.name == 'c'
649
+
650
+ root = RAParser.parse_query(r'a ⟗ b ⟖ c')
651
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
652
+ assert isinstance(root.left, BinaryOperators.FullOuterJoin)
653
+ assert isinstance(root.left.left, RAOperand)
654
+ assert root.left.left.name == 'a'
655
+ assert isinstance(root.left.right, RAOperand)
656
+ assert root.left.right.name == 'b'
657
+ assert isinstance(root.right, RAOperand)
658
+ assert root.right.name == 'c'
659
+
660
+ # cross join
661
+ root = RAParser.parse_query(r'a x b x c')
662
+ assert isinstance(root, BinaryOperators.Cross)
663
+ assert isinstance(root.left, BinaryOperators.Cross)
664
+ assert isinstance(root.left.left, RAOperand)
665
+ assert root.left.left.name == 'a'
666
+ assert isinstance(root.left.right, RAOperand)
667
+ assert root.left.right.name == 'b'
668
+ assert isinstance(root.right, RAOperand)
669
+ assert root.right.name == 'c'
670
+
671
+
672
+ def test_unary_evaluation_order():
673
+ root = RAParser.parse_query(r'π [ Id2 ] β [ Id2 ← Id ] (Users)')
674
+ assert isinstance(root, UnaryOperators.Projection)
675
+ assert isinstance(root.target, UnaryOperators.Rename)
676
+
677
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] π [ Id ] (Users)')
678
+ assert isinstance(root, UnaryOperators.Rename)
679
+ assert isinstance(root.target, UnaryOperators.Projection)
680
+
681
+ root = RAParser.parse_query(r'π [ Id ] σ [ Id > 1 ] (Users)')
682
+ assert isinstance(root, UnaryOperators.Projection)
683
+ assert isinstance(root.target, UnaryOperators.Selection)
684
+
685
+ root = RAParser.parse_query(r'σ [ Id > 1 ] π [ Id ] (Users)')
686
+ assert isinstance(root, UnaryOperators.Selection)
687
+ assert isinstance(root.target, UnaryOperators.Projection)
688
+
689
+ root = RAParser.parse_query(r'σ [ Id2 > 1 ] β [ Id2 ← Id ] (Users)')
690
+ assert isinstance(root, UnaryOperators.Selection)
691
+ assert isinstance(root.target, UnaryOperators.Rename)
692
+
693
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] σ [ Id > 1 ] (Users)')
694
+ assert isinstance(root, UnaryOperators.Rename)
695
+ assert isinstance(root.target, UnaryOperators.Selection)
696
+
697
+
698
+ def test_binary_evaluation_order():
699
+ # difference <-> union
700
+ root = RAParser.parse_query(r'a \ b ∪ c')
701
+ assert isinstance(root, BinaryOperators.Difference)
702
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Union)
703
+
704
+ root = RAParser.parse_query(r'a ∪ b \ c')
705
+ assert isinstance(root, BinaryOperators.Difference)
706
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Union)
707
+
708
+ # difference <-> intersection
709
+ root = RAParser.parse_query(r'a \ b ∩ c')
710
+ assert isinstance(root, BinaryOperators.Difference)
711
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Intersection)
712
+
713
+ root = RAParser.parse_query(r'a ∩ b \ c')
714
+ assert isinstance(root, BinaryOperators.Difference)
715
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Intersection)
716
+
717
+ # difference <-> join
718
+ root = RAParser.parse_query(r'a \ b ⋈ c')
719
+ assert isinstance(root, BinaryOperators.Difference)
720
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Join)
721
+
722
+ root = RAParser.parse_query(r'a ⋈ b \ c')
723
+ assert isinstance(root, BinaryOperators.Difference)
724
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Join)
725
+
726
+ root = RAParser.parse_query(r'a \ b ⟕ c')
727
+ assert isinstance(root, BinaryOperators.Difference)
728
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftOuterJoin)
729
+
730
+ root = RAParser.parse_query(r'a ⟕ b \ c')
731
+ assert isinstance(root, BinaryOperators.Difference)
732
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftOuterJoin)
733
+
734
+ root = RAParser.parse_query(r'a \ b ⟖ c')
735
+ assert isinstance(root, BinaryOperators.Difference)
736
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightOuterJoin)
737
+
738
+ root = RAParser.parse_query(r'a ⟖ b \ c')
739
+ assert isinstance(root, BinaryOperators.Difference)
740
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightOuterJoin)
741
+
742
+ root = RAParser.parse_query(r'a \ b ⟗ c')
743
+ assert isinstance(root, BinaryOperators.Difference)
744
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.FullOuterJoin)
745
+
746
+ root = RAParser.parse_query(r'a ⟗ b \ c')
747
+ assert isinstance(root, BinaryOperators.Difference)
748
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
749
+
750
+ # difference <-> cross
751
+ root = RAParser.parse_query(r'a \ b x c')
752
+ assert isinstance(root, BinaryOperators.Difference)
753
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
754
+
755
+ root = RAParser.parse_query(r'a x b \ c')
756
+ assert isinstance(root, BinaryOperators.Difference)
757
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
758
+
759
+ # difference <-> division
760
+ root = RAParser.parse_query(r'a \ b ÷ c')
761
+ assert isinstance(root, BinaryOperators.Difference)
762
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
763
+
764
+ root = RAParser.parse_query(r'a ÷ b \ c')
765
+ assert isinstance(root, BinaryOperators.Difference)
766
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
767
+
768
+ # union <-> intersection
769
+ root = RAParser.parse_query(r'a ∪ b ∩ c')
770
+ assert isinstance(root, BinaryOperators.Union)
771
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Intersection)
772
+
773
+ root = RAParser.parse_query(r'a ∩ b ∪ c')
774
+ assert isinstance(root, BinaryOperators.Union)
775
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Intersection)
776
+
777
+ # union <-> join
778
+ root = RAParser.parse_query(r'a ∪ b ⋈ c')
779
+ assert isinstance(root, BinaryOperators.Union)
780
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Join)
781
+
782
+ root = RAParser.parse_query(r'a ⋈ b ∪ c')
783
+ assert isinstance(root, BinaryOperators.Union)
784
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Join)
785
+
786
+ root = RAParser.parse_query(r'a ∪ b ⟕ c')
787
+ assert isinstance(root, BinaryOperators.Union)
788
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftOuterJoin)
789
+
790
+ root = RAParser.parse_query(r'a ⟕ b ∪ c')
791
+ assert isinstance(root, BinaryOperators.Union)
792
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftOuterJoin)
793
+
794
+ root = RAParser.parse_query(r'a ∪ b ⟖ c')
795
+ assert isinstance(root, BinaryOperators.Union)
796
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightOuterJoin)
797
+
798
+ root = RAParser.parse_query(r'a ⟖ b ∪ c')
799
+ assert isinstance(root, BinaryOperators.Union)
800
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightOuterJoin)
801
+
802
+ root = RAParser.parse_query(r'a ∪ b ⟗ c')
803
+ assert isinstance(root, BinaryOperators.Union)
804
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.FullOuterJoin)
805
+
806
+ root = RAParser.parse_query(r'a ⟗ b ∪ c')
807
+ assert isinstance(root, BinaryOperators.Union)
808
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
809
+
810
+ # union <-> cross
811
+ root = RAParser.parse_query(r'a ∪ b x c')
812
+ assert isinstance(root, BinaryOperators.Union)
813
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
814
+
815
+ root = RAParser.parse_query(r'a x b ∪ c')
816
+ assert isinstance(root, BinaryOperators.Union)
817
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
818
+
819
+ # union <-> division
820
+ root = RAParser.parse_query(r'a ∪ b ÷ c')
821
+ assert isinstance(root, BinaryOperators.Union)
822
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
823
+
824
+ root = RAParser.parse_query(r'a ÷ b ∪ c')
825
+ assert isinstance(root, BinaryOperators.Union)
826
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
827
+
828
+ # intersection <-> join
829
+ root = RAParser.parse_query(r'a ∩ b ⋈ c')
830
+ assert isinstance(root, BinaryOperators.Intersection)
831
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Join)
832
+
833
+ root = RAParser.parse_query(r'a ⋈ b ∩ c')
834
+ assert isinstance(root, BinaryOperators.Intersection)
835
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Join)
836
+
837
+ root = RAParser.parse_query(r'a ∩ b ⟕ c')
838
+ assert isinstance(root, BinaryOperators.Intersection)
839
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftOuterJoin)
840
+
841
+ root = RAParser.parse_query(r'a ⟕ b ∩ c')
842
+ assert isinstance(root, BinaryOperators.Intersection)
843
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftOuterJoin)
844
+
845
+ root = RAParser.parse_query(r'a ∩ b ⟖ c')
846
+ assert isinstance(root, BinaryOperators.Intersection)
847
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightOuterJoin)
848
+
849
+ root = RAParser.parse_query(r'a ⟖ b ∩ c')
850
+ assert isinstance(root, BinaryOperators.Intersection)
851
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightOuterJoin)
852
+
853
+ root = RAParser.parse_query(r'a ∩ b ⟗ c')
854
+ assert isinstance(root, BinaryOperators.Intersection)
855
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.FullOuterJoin)
856
+
857
+ root = RAParser.parse_query(r'a ⟗ b ∩ c')
858
+ assert isinstance(root, BinaryOperators.Intersection)
859
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
860
+
861
+ # intersection <-> cross
862
+ root = RAParser.parse_query(r'a ∩ b x c')
863
+ assert isinstance(root, BinaryOperators.Intersection)
864
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
865
+
866
+ root = RAParser.parse_query(r'a x b ∩ c')
867
+ assert isinstance(root, BinaryOperators.Intersection)
868
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
869
+
870
+ # intersection <-> division
871
+ root = RAParser.parse_query(r'a ∩ b ÷ c')
872
+ assert isinstance(root, BinaryOperators.Intersection)
873
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
874
+
875
+ root = RAParser.parse_query(r'a ÷ b ∩ c')
876
+ assert isinstance(root, BinaryOperators.Intersection)
877
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
878
+
879
+ # join <-> cross
880
+ root = RAParser.parse_query(r'a ⋈ b x c')
881
+ assert isinstance(root, BinaryOperators.Join)
882
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
883
+
884
+ root = RAParser.parse_query(r'a x b ⋈ c')
885
+ assert isinstance(root, BinaryOperators.Join)
886
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
887
+
888
+ root = RAParser.parse_query(r'a ⟕ b x c')
889
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
890
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
891
+
892
+ root = RAParser.parse_query(r'a x b ⟕ c')
893
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
894
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
895
+
896
+ root = RAParser.parse_query(r'a ⟖ b x c')
897
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
898
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
899
+
900
+ root = RAParser.parse_query(r'a x b ⟖ c')
901
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
902
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
903
+
904
+ root = RAParser.parse_query(r'a ⟗ b x c')
905
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
906
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Cross)
907
+
908
+ root = RAParser.parse_query(r'a x b ⟗ c')
909
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
910
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Cross)
911
+
912
+ # join <-> division
913
+ root = RAParser.parse_query(r'a ⋈ b ÷ c')
914
+ assert isinstance(root, BinaryOperators.Join)
915
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
916
+
917
+ root = RAParser.parse_query(r'a ÷ b ⋈ c')
918
+ assert isinstance(root, BinaryOperators.Join)
919
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
920
+
921
+ root = RAParser.parse_query(r'a ⟕ b ÷ c')
922
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
923
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
924
+
925
+ root = RAParser.parse_query(r'a ÷ b ⟕ c')
926
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
927
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
928
+
929
+ root = RAParser.parse_query(r'a ⟖ b ÷ c')
930
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
931
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
932
+
933
+ root = RAParser.parse_query(r'a ÷ b ⟖ c')
934
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
935
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
936
+
937
+ root = RAParser.parse_query(r'a ⟗ b ÷ c')
938
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
939
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
940
+
941
+ root = RAParser.parse_query(r'a ÷ b ⟗ c')
942
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
943
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
944
+
945
+ # natural join <-> outer join
946
+ root = RAParser.parse_query(r'a ⋈ b ⟕ c')
947
+ assert isinstance(root, BinaryOperators.Join)
948
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.LeftOuterJoin)
949
+
950
+ root = RAParser.parse_query(r'a ⟕ b ⋈ c')
951
+ assert isinstance(root, BinaryOperators.Join)
952
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.LeftOuterJoin)
953
+
954
+ root = RAParser.parse_query(r'a ⋈ b ⟖ c')
955
+ assert isinstance(root, BinaryOperators.Join)
956
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.RightOuterJoin)
957
+
958
+ root = RAParser.parse_query(r'a ⟖ b ⋈ c')
959
+ assert isinstance(root, BinaryOperators.Join)
960
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.RightOuterJoin)
961
+
962
+ root = RAParser.parse_query(r'a ⋈ b ⟗ c')
963
+ assert isinstance(root, BinaryOperators.Join)
964
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.FullOuterJoin)
965
+
966
+ root = RAParser.parse_query(r'a ⟗ b ⋈ c')
967
+ assert isinstance(root, BinaryOperators.Join)
968
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.FullOuterJoin)
969
+
970
+ # cross <-> division
971
+ root = RAParser.parse_query(r'a x b ÷ c')
972
+ assert isinstance(root, BinaryOperators.Cross)
973
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, BinaryOperators.Division)
974
+
975
+ root = RAParser.parse_query(r'a ÷ b x c')
976
+ assert isinstance(root, BinaryOperators.Cross)
977
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, BinaryOperators.Division)
978
+
979
+
980
+ def test_mixed_evaluation_order():
981
+ # difference <-> projection
982
+ root = RAParser.parse_query(r'a \ π [ Id ] b')
983
+ assert isinstance(root, BinaryOperators.Difference)
984
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
985
+
986
+ root = RAParser.parse_query(r'π [ Id ] a \ b')
987
+ assert isinstance(root, BinaryOperators.Difference)
988
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
989
+
990
+ # difference <-> rename
991
+ root = RAParser.parse_query(r'a \ β [ Id2 ← Id ] b')
992
+ assert isinstance(root, BinaryOperators.Difference)
993
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
994
+
995
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a \ b')
996
+ assert isinstance(root, BinaryOperators.Difference)
997
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
998
+
999
+ # difference <-> selection
1000
+ root = RAParser.parse_query(r'a \ σ [ Id > 1 ] b')
1001
+ assert isinstance(root, BinaryOperators.Difference)
1002
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1003
+
1004
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a \ b')
1005
+ assert isinstance(root, BinaryOperators.Difference)
1006
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1007
+
1008
+ # union <-> projection
1009
+ root = RAParser.parse_query(r'a ∪ π [ Id ] b')
1010
+ assert isinstance(root, BinaryOperators.Union)
1011
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1012
+
1013
+ root = RAParser.parse_query(r'π [ Id ] a ∪ b')
1014
+ assert isinstance(root, BinaryOperators.Union)
1015
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1016
+
1017
+ # union <-> rename
1018
+ root = RAParser.parse_query(r'a ∪ β [ Id2 ← Id ] b')
1019
+ assert isinstance(root, BinaryOperators.Union)
1020
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1021
+
1022
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a ∪ b')
1023
+ assert isinstance(root, BinaryOperators.Union)
1024
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1025
+
1026
+ # union <-> selection
1027
+ root = RAParser.parse_query(r'a ∪ σ [ Id > 1 ] b')
1028
+ assert isinstance(root, BinaryOperators.Union)
1029
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1030
+
1031
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a ∪ b')
1032
+ assert isinstance(root, BinaryOperators.Union)
1033
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1034
+
1035
+ # intersection <-> projection
1036
+ root = RAParser.parse_query(r'a ∩ π [ Id ] b')
1037
+ assert isinstance(root, BinaryOperators.Intersection)
1038
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1039
+
1040
+ root = RAParser.parse_query(r'π [ Id ] a ∩ b')
1041
+ assert isinstance(root, BinaryOperators.Intersection)
1042
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1043
+
1044
+ # intersection <-> rename
1045
+ root = RAParser.parse_query(r'a ∩ β [ Id2 ← Id ] b')
1046
+ assert isinstance(root, BinaryOperators.Intersection)
1047
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1048
+
1049
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a ∩ b')
1050
+ assert isinstance(root, BinaryOperators.Intersection)
1051
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1052
+
1053
+ # intersection <-> selection
1054
+ root = RAParser.parse_query(r'a ∩ σ [ Id > 1 ] b')
1055
+ assert isinstance(root, BinaryOperators.Intersection)
1056
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1057
+
1058
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a ∩ b')
1059
+ assert isinstance(root, BinaryOperators.Intersection)
1060
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1061
+
1062
+ # join <-> projection
1063
+ root = RAParser.parse_query(r'a ⋈ π [ Id ] b')
1064
+ assert isinstance(root, BinaryOperators.Join)
1065
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1066
+
1067
+ root = RAParser.parse_query(r'π [ Id ] a ⋈ b')
1068
+ assert isinstance(root, BinaryOperators.Join)
1069
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1070
+
1071
+ root = RAParser.parse_query(r'a ⟕ π [ Id ] b')
1072
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
1073
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1074
+
1075
+ root = RAParser.parse_query(r'π [ Id ] a ⟕ b')
1076
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
1077
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1078
+
1079
+ root = RAParser.parse_query(r'a ⟖ π [ Id ] b')
1080
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
1081
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1082
+
1083
+ root = RAParser.parse_query(r'π [ Id ] a ⟖ b')
1084
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
1085
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1086
+
1087
+ root = RAParser.parse_query(r'a ⟗ π [ Id ] b')
1088
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
1089
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1090
+
1091
+ root = RAParser.parse_query(r'π [ Id ] a ⟗ b')
1092
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
1093
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1094
+
1095
+ # join <-> rename
1096
+ root = RAParser.parse_query(r'a ⋈ β [ Id2 ← Id ] b')
1097
+ assert isinstance(root, BinaryOperators.Join)
1098
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1099
+
1100
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a ⋈ b')
1101
+ assert isinstance(root, BinaryOperators.Join)
1102
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1103
+
1104
+ root = RAParser.parse_query(r'a ⟕ β [ Id2 ← Id ] b')
1105
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
1106
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1107
+
1108
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a ⟕ b')
1109
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
1110
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1111
+
1112
+ root = RAParser.parse_query(r'a ⟖ β [ Id2 ← Id ] b')
1113
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
1114
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1115
+
1116
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a ⟖ b')
1117
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
1118
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1119
+
1120
+ root = RAParser.parse_query(r'a ⟗ β [ Id2 ← Id ] b')
1121
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
1122
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1123
+
1124
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a ⟗ b')
1125
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
1126
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1127
+
1128
+ # join <-> selection
1129
+ root = RAParser.parse_query(r'a ⋈ σ [ Id > 1 ] b')
1130
+ assert isinstance(root, BinaryOperators.Join)
1131
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1132
+
1133
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a ⋈ b')
1134
+ assert isinstance(root, BinaryOperators.Join)
1135
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1136
+
1137
+ root = RAParser.parse_query(r'a ⟕ σ [ Id > 1 ] b')
1138
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
1139
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1140
+
1141
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a ⟕ b')
1142
+ assert isinstance(root, BinaryOperators.LeftOuterJoin)
1143
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1144
+
1145
+ root = RAParser.parse_query(r'a ⟖ σ [ Id > 1 ] b')
1146
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
1147
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1148
+
1149
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a ⟖ b')
1150
+ assert isinstance(root, BinaryOperators.RightOuterJoin)
1151
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1152
+
1153
+ root = RAParser.parse_query(r'a ⟗ σ [ Id > 1 ] b')
1154
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
1155
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1156
+
1157
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a ⟗ b')
1158
+ assert isinstance(root, BinaryOperators.FullOuterJoin)
1159
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1160
+
1161
+ # cross <-> projection
1162
+ root = RAParser.parse_query(r'a x π [ Id ] b')
1163
+ assert isinstance(root, BinaryOperators.Cross)
1164
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1165
+
1166
+ root = RAParser.parse_query(r'π [ Id ] a x b')
1167
+ assert isinstance(root, BinaryOperators.Cross)
1168
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1169
+
1170
+ # cross <-> rename
1171
+ root = RAParser.parse_query(r'a x β [ Id2 ← Id ] b')
1172
+ assert isinstance(root, BinaryOperators.Cross)
1173
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1174
+
1175
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a x b')
1176
+ assert isinstance(root, BinaryOperators.Cross)
1177
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1178
+
1179
+ # cross <-> selection
1180
+ root = RAParser.parse_query(r'a x σ [ Id > 1 ] b')
1181
+ assert isinstance(root, BinaryOperators.Cross)
1182
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1183
+
1184
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a x b')
1185
+ assert isinstance(root, BinaryOperators.Cross)
1186
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1187
+
1188
+ # division <-> projection
1189
+ root = RAParser.parse_query(r'a ÷ π [ Id ] b')
1190
+ assert isinstance(root, BinaryOperators.Division)
1191
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Projection)
1192
+
1193
+ root = RAParser.parse_query(r'π [ Id ] a ÷ b')
1194
+ assert isinstance(root, BinaryOperators.Division)
1195
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Projection)
1196
+
1197
+ # division <-> rename
1198
+ root = RAParser.parse_query(r'a ÷ β [ Id2 ← Id ] b')
1199
+ assert isinstance(root, BinaryOperators.Division)
1200
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Rename)
1201
+
1202
+ root = RAParser.parse_query(r'β [ Id2 ← Id ] a ÷ b')
1203
+ assert isinstance(root, BinaryOperators.Division)
1204
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Rename)
1205
+
1206
+ # division <-> selection
1207
+ root = RAParser.parse_query(r'a ÷ σ [ Id > 1 ] b')
1208
+ assert isinstance(root, BinaryOperators.Division)
1209
+ assert isinstance(root.left, RAOperand) and isinstance(root.right, UnaryOperators.Selection)
1210
+
1211
+ root = RAParser.parse_query(r'σ [ Id > 1 ] a ÷ b')
1212
+ assert isinstance(root, BinaryOperators.Division)
1213
+ assert isinstance(root.right, RAOperand) and isinstance(root.left, UnaryOperators.Selection)
1214
+
1215
+
1216
+ def test_special_queries():
1217
+ with Connection() as con:
1218
+ # Consecutive operators triggered a recursion error in a previous
1219
+ # version, leading to an infinite loop / stack overflow.
1220
+ with pytest.raises(RAParserError, match='right operand missing after x'):
1221
+ RAParser.parse_query(r'''
1222
+ Users x x BannedUsers
1223
+ ''')
1224
+
1225
+ # Enclosing parentheses are removed. In the following case
1226
+ # the parentheses may only be removed from each subquery
1227
+ # independently *after* the cross join is applied. Otherwise,
1228
+ # the result is a parsing error.
1229
+ root = RAParser.parse_query(r'''
1230
+ (
1231
+ Sigma [ Id > 1 ] Pi [ Username, Id ] (Users)
1232
+ ) x (
1233
+ Beta [ Username2 <- BannedUsername ] Beta [ Id2 <- Id ] (BannedUsers)
1234
+ )
1235
+ ''')
1236
+
1237
+ assert isinstance(root, BinaryOperators.Cross)
1238
+ assert isinstance(root.left, UnaryOperators.Selection)
1239
+ assert isinstance(root.left.target, UnaryOperators.Projection)
1240
+ assert isinstance(root.left.target.target, RAOperand) and root.left.target.target.name == 'Users'
1241
+ assert isinstance(root.right, UnaryOperators.Rename)
1242
+ assert isinstance(root.right.target, UnaryOperators.Rename)
1243
+ assert isinstance(root.right.target.target, RAOperand) and root.right.target.target.name == 'BannedUsers'
1244
+
1245
+ assert con.execute_ra(root) == [
1246
+ ('Bob', 2, 2, 'Bob'),
1247
+ ('Bob', 2, 4, 'David'),
1248
+ ('Charlie', 3, 2, 'Bob'),
1249
+ ('Charlie', 3, 4, 'David')
1250
+ ]