alf 0.14.0 → 0.15.0

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 (67) hide show
  1. data/Gemfile +6 -3
  2. data/Gemfile.lock +65 -19
  3. data/README.md +77 -183
  4. data/Rakefile +25 -0
  5. data/alf.gemspec +5 -3
  6. data/alf.noespec +8 -6
  7. data/lib/alf/loader.rb +1 -0
  8. data/lib/alf/version.rb +1 -1
  9. data/spec/facade/test_query.rb +12 -0
  10. data/spec/key-inference/queries.yml +10 -0
  11. data/spec/key-inference/test_all.rb +21 -0
  12. data/{test → spec}/migrations/test_folder_migration.rb +0 -0
  13. data/{test → spec}/migrations/test_sequel_migration.rb +1 -1
  14. data/spec/operators/ungroup/grouped.json +3 -0
  15. data/spec/operators/ungroup/test_on_json_content.rb +11 -0
  16. data/spec/operators/unwrap/test_on_json_content.rb +11 -0
  17. data/spec/operators/unwrap/wrapped.json +3 -0
  18. data/spec/optimizer/project/extend.yml +20 -0
  19. data/spec/optimizer/project/intersect.yml +10 -0
  20. data/spec/optimizer/project/join.yml +20 -0
  21. data/spec/optimizer/project/matching.yml +21 -0
  22. data/spec/optimizer/project/minus.yml +9 -0
  23. data/spec/optimizer/project/not_matching.yml +21 -0
  24. data/spec/optimizer/project/project.yml +88 -0
  25. data/spec/optimizer/project/rename.yml +30 -0
  26. data/spec/optimizer/project/sort.yml +45 -0
  27. data/spec/optimizer/project/union.yml +8 -0
  28. data/spec/optimizer/restrict/clip.yml +4 -0
  29. data/spec/optimizer/restrict/compact.yml +4 -0
  30. data/spec/optimizer/restrict/generator.yml +4 -0
  31. data/spec/optimizer/restrict/intersect.yml +4 -0
  32. data/spec/optimizer/restrict/leaf_operand.yml +4 -0
  33. data/spec/optimizer/restrict/minus.yml +4 -0
  34. data/spec/optimizer/restrict/page.yml +12 -0
  35. data/spec/optimizer/restrict/project.yml +4 -0
  36. data/spec/optimizer/restrict/sort.yml +4 -0
  37. data/spec/optimizer/restrict/union.yml +4 -0
  38. data/spec/optimizer/test_all.rb +34 -0
  39. data/spec/sql/helpers.rb +25 -0
  40. data/spec/sql/queries/01-leaf-operand.yml +5 -0
  41. data/spec/sql/queries/02-clip.yml +12 -0
  42. data/spec/sql/queries/03-sort.yml +58 -0
  43. data/spec/sql/queries/04-frame.yml +57 -0
  44. data/spec/sql/queries/05-intersect.yml +23 -0
  45. data/spec/sql/queries/06-join.yml +207 -0
  46. data/spec/sql/queries/07-matching.yml +76 -0
  47. data/spec/sql/queries/08-minus.yml +23 -0
  48. data/spec/sql/queries/09-not-matching.yml +57 -0
  49. data/spec/sql/queries/10-page.yml +31 -0
  50. data/spec/sql/queries/11-project.yml +48 -0
  51. data/spec/sql/queries/12-rename.yml +24 -0
  52. data/spec/sql/queries/13-restrict.yml +114 -0
  53. data/spec/sql/queries/15-union.yml +90 -0
  54. data/spec/sql/queries/16-wrap.yml +3 -0
  55. data/spec/sql/queries/91-reuse.yml +28 -0
  56. data/spec/sql/test_sequel_compiler.rb +41 -0
  57. data/spec/sql/test_sql_compiler.rb +52 -0
  58. data/{test → spec}/test_alf.rb +0 -0
  59. data/spec/test_helpers.rb +54 -0
  60. data/tasks/doc.rake +10 -0
  61. data/tasks/fixtures.rake +52 -0
  62. data/tasks/mod.rake +50 -0
  63. data/tasks/release.rake +34 -0
  64. data/tasks/test.rake +2 -2
  65. metadata +150 -19
  66. data/test/seeding/test_seeding.rb +0 -49
  67. data/test/test_helpers.rb +0 -24
@@ -0,0 +1,76 @@
1
+ # matching with no attribute
2
+ - alf: |-
3
+ matching(suppliers, project(supplies, [:qty]))
4
+ sql:
5
+ SELECT t1.sid, t1.name, t1.status, t1.city
6
+ FROM suppliers AS t1
7
+ WHERE EXISTS(SELECT * FROM supplies AS t2)
8
+ # matching on one single attribute
9
+ - alf: |-
10
+ matching(suppliers, supplies)
11
+ sql:
12
+ SELECT t1.sid, t1.name, t1.status, t1.city
13
+ FROM suppliers AS t1
14
+ WHERE t1.sid IN (SELECT t2.sid FROM supplies AS t2)
15
+ # matching on multiple attributes
16
+ - alf: |-
17
+ matching(suppliers, parts)
18
+ sql:
19
+ SELECT t1.sid, t1.name, t1.status, t1.city
20
+ FROM suppliers AS t1
21
+ WHERE EXISTS(
22
+ SELECT t2.name, t2.city
23
+ FROM parts AS t2
24
+ WHERE t1.name = t2.name
25
+ AND t1.city = t2.city)
26
+ # matching when a with_exp at right
27
+ - alf: |-
28
+ matching(suppliers, sort(sort(suppliers, [:sid, :asc]), [:sid, :desc]))
29
+ sql:
30
+ WITH t3 AS (
31
+ SELECT t2.sid, t2.name, t2.status, t2.city
32
+ FROM suppliers AS t2
33
+ ORDER BY t2.sid ASC
34
+ )
35
+ SELECT t1.sid, t1.name, t1.status, t1.city
36
+ FROM suppliers AS t1
37
+ WHERE EXISTS(
38
+ SELECT t3.sid, t3.name, t3.status, t3.city
39
+ FROM t3 AS t3
40
+ WHERE t1.sid = t3.sid
41
+ AND t1.name = t3.name
42
+ AND t1.status = t3.status
43
+ AND t1.city = t3.city
44
+ ORDER BY t3.sid DESC)
45
+ # matching with a union at left
46
+ - alf: |-
47
+ matching(union(suppliers_in_london, suppliers_in_paris), supplies)
48
+ sql: |-
49
+ WITH t4 AS (
50
+ (SELECT t1.sid, t1.name, t1.status, t1.city
51
+ FROM suppliers AS t1
52
+ WHERE t1.city = 'London')
53
+ UNION
54
+ (SELECT t2.sid, t2.name, t2.status, t2.city
55
+ FROM suppliers AS t2
56
+ WHERE t2.city = 'Paris')
57
+ )
58
+ SELECT t4.sid, t4.name, t4.status, t4.city
59
+ FROM t4 AS t4
60
+ WHERE t4.sid IN (SELECT t3.sid FROM supplies AS t3)
61
+ # matching with a union at right
62
+ - alf: |-
63
+ matching(supplies, union(suppliers_in_london, suppliers_in_paris))
64
+ sql: |-
65
+ WITH t4 AS (
66
+ (SELECT t2.sid, t2.name, t2.status, t2.city
67
+ FROM suppliers AS t2
68
+ WHERE t2.city = 'London')
69
+ UNION
70
+ (SELECT t3.sid, t3.name, t3.status, t3.city
71
+ FROM suppliers AS t3
72
+ WHERE t3.city = 'Paris')
73
+ )
74
+ SELECT t1.sid, t1.pid, t1.qty
75
+ FROM supplies AS t1
76
+ WHERE t1.sid IN (SELECT t4.sid FROM t4 AS t4)
@@ -0,0 +1,23 @@
1
+ - alf: |-
2
+ minus(suppliers_in_london, suppliers_in_paris)
3
+ sql:
4
+ (SELECT t1.sid, t1.name, t1.status, t1.city
5
+ FROM suppliers AS t1
6
+ WHERE t1.city = 'London')
7
+ EXCEPT
8
+ (SELECT t2.sid, t2.name, t2.status, t2.city
9
+ FROM suppliers AS t2
10
+ WHERE t2.city = 'Paris')
11
+ - alf: |-
12
+ minus(minus(suppliers_in_london, suppliers_in_paris), suppliers)
13
+ sql:
14
+ ((SELECT t1.sid, t1.name, t1.status, t1.city
15
+ FROM suppliers AS t1
16
+ WHERE t1.city = 'London')
17
+ EXCEPT
18
+ (SELECT t2.sid, t2.name, t2.status, t2.city
19
+ FROM suppliers AS t2
20
+ WHERE t2.city = 'Paris'))
21
+ EXCEPT
22
+ (SELECT t3.sid, t3.name, t3.status, t3.city
23
+ FROM suppliers AS t3)
@@ -0,0 +1,57 @@
1
+ # not_matching with no attribute
2
+ - alf: |-
3
+ not_matching(suppliers, project(supplies, [:qty]))
4
+ sql:
5
+ SELECT t1.sid, t1.name, t1.status, t1.city
6
+ FROM suppliers AS t1
7
+ WHERE NOT(EXISTS(SELECT * FROM supplies AS t2))
8
+ # not matching on one single attribute
9
+ - alf: |-
10
+ not_matching(suppliers, supplies)
11
+ sql:
12
+ SELECT t1.sid, t1.name, t1.status, t1.city
13
+ FROM suppliers AS t1
14
+ WHERE NOT(t1.sid IN (SELECT t2.sid FROM supplies AS t2))
15
+ # not_matching on multiple attributes
16
+ - alf: |-
17
+ not_matching(suppliers, parts)
18
+ sql:
19
+ SELECT t1.sid, t1.name, t1.status, t1.city
20
+ FROM suppliers AS t1
21
+ WHERE NOT(EXISTS(
22
+ SELECT t2.name, t2.city
23
+ FROM parts AS t2
24
+ WHERE t1.name = t2.name
25
+ AND t1.city = t2.city))
26
+ # not_matching with a union at left
27
+ - alf: |-
28
+ not_matching(union(suppliers_in_london, suppliers_in_paris), supplies)
29
+ sql: |-
30
+ WITH t4 AS (
31
+ (SELECT t1.sid, t1.name, t1.status, t1.city
32
+ FROM suppliers AS t1
33
+ WHERE t1.city = 'London')
34
+ UNION
35
+ (SELECT t2.sid, t2.name, t2.status, t2.city
36
+ FROM suppliers AS t2
37
+ WHERE t2.city = 'Paris')
38
+ )
39
+ SELECT t4.sid, t4.name, t4.status, t4.city
40
+ FROM t4 AS t4
41
+ WHERE NOT(t4.sid IN (SELECT t3.sid FROM supplies AS t3))
42
+ # not_matching with a union at right
43
+ - alf: |-
44
+ not_matching(supplies, union(suppliers_in_london, suppliers_in_paris))
45
+ sql: |-
46
+ WITH t4 AS (
47
+ (SELECT t2.sid, t2.name, t2.status, t2.city
48
+ FROM suppliers AS t2
49
+ WHERE t2.city = 'London')
50
+ UNION
51
+ (SELECT t3.sid, t3.name, t3.status, t3.city
52
+ FROM suppliers AS t3
53
+ WHERE t3.city = 'Paris')
54
+ )
55
+ SELECT t1.sid, t1.pid, t1.qty
56
+ FROM supplies AS t1
57
+ WHERE NOT(t1.sid IN (SELECT t4.sid FROM t4 AS t4))
@@ -0,0 +1,31 @@
1
+ - alf: |-
2
+ page(suppliers, [[:city, :desc], [:sid, :asc]], 1, page_size: 10)
3
+ sql: |-
4
+ SELECT t1.sid, t1.name, t1.status, t1.city
5
+ FROM suppliers AS t1
6
+ ORDER BY t1.city DESC, t1.sid ASC
7
+ LIMIT 10 OFFSET 0
8
+ #
9
+ - alf: |-
10
+ page(suppliers, [[:city, :desc], [:sid, :asc]], -1, page_size: 10)
11
+ sql: |-
12
+ SELECT t1.sid, t1.name, t1.status, t1.city
13
+ FROM suppliers AS t1
14
+ ORDER BY t1.city ASC, t1.sid DESC
15
+ LIMIT 10 OFFSET 0
16
+ #
17
+ - alf: |-
18
+ page(suppliers, [[:city, :desc], [:sid, :asc]], 2, page_size: 10)
19
+ sql: |-
20
+ SELECT t1.sid, t1.name, t1.status, t1.city
21
+ FROM suppliers AS t1
22
+ ORDER BY t1.city DESC, t1.sid ASC
23
+ LIMIT 10 OFFSET 10
24
+ #
25
+ - alf: |-
26
+ page(suppliers, [[:city, :desc]], 2, page_size: 10)
27
+ sql: |-
28
+ SELECT t1.sid, t1.name, t1.status, t1.city
29
+ FROM suppliers AS t1
30
+ ORDER BY t1.city DESC, t1.sid ASC
31
+ LIMIT 10 OFFSET 10
@@ -0,0 +1,48 @@
1
+ # normal case
2
+ - alf: |-
3
+ project(suppliers, [:sid])
4
+ sql: |-
5
+ SELECT t1.sid
6
+ FROM suppliers AS t1
7
+ # allbut case
8
+ - alf: |-
9
+ project(suppliers, [:sid], allbut: true)
10
+ sql: |-
11
+ SELECT t1.name, t1.status, t1.city
12
+ FROM suppliers AS t1
13
+ # unique, non primary key
14
+ - alf: |-
15
+ project(suppliers, [:name])
16
+ sql: |-
17
+ SELECT t1.name
18
+ FROM suppliers AS t1
19
+ # requiring distinct
20
+ - alf: |-
21
+ project(suppliers, [:status])
22
+ sql: |-
23
+ SELECT DISTINCT t1.status
24
+ FROM suppliers AS t1
25
+ # a special case where attributes are taken in another order
26
+ - alf: |-
27
+ project(suppliers, [:status, :city, :name])
28
+ sql: |-
29
+ SELECT t1.name, t1.status, t1.city
30
+ FROM suppliers AS t1
31
+ comment: |-
32
+ The projection applies to the heading, that keeps its own order. The
33
+ ordering of fields therefore differs from what could be expected. It's
34
+ not a semantics issue, though.
35
+ # empty projection
36
+ - alf: |-
37
+ project(suppliers, [])
38
+ sql: |-
39
+ SELECT TRUE AS is_table_dee
40
+ WHERE EXISTS(SELECT * FROM suppliers AS t1)
41
+ # empty projection or empty projection
42
+ - alf: |-
43
+ project(project(suppliers, []), [])
44
+ sql: |-
45
+ SELECT TRUE AS is_table_dee
46
+ WHERE EXISTS(
47
+ SELECT TRUE AS is_table_dee
48
+ WHERE EXISTS(SELECT * FROM suppliers AS t1))
@@ -0,0 +1,24 @@
1
+ - alf: |-
2
+ rename(suppliers, :sid => :id)
3
+ sql: |-
4
+ SELECT t1.sid AS id, t1.name, t1.status, t1.city
5
+ FROM suppliers AS t1
6
+ #
7
+ - alf: |-
8
+ project(rename(suppliers, :sid => :id), [:id, :name])
9
+ sql: |-
10
+ SELECT t1.sid AS id, t1.name
11
+ FROM suppliers AS t1
12
+ #
13
+ - alf: |-
14
+ sort(rename(suppliers, :sid => :id), [[:id, :asc], [:city, :desc]])
15
+ sql: |-
16
+ SELECT t1.sid AS id, t1.name, t1.status, t1.city
17
+ FROM suppliers AS t1
18
+ ORDER BY t1.sid ASC, t1.city DESC
19
+ #
20
+ - alf: |-
21
+ rename(cities, country: :name)
22
+ sql: |-
23
+ SELECT t1.city, t1.country AS name
24
+ FROM cities AS t1
@@ -0,0 +1,114 @@
1
+ # base case
2
+ - alf: |-
3
+ restrict(suppliers, sid: 'S1')
4
+ sql: |-
5
+ SELECT t1.sid, t1.name, t1.status, t1.city
6
+ FROM suppliers AS t1
7
+ WHERE t1.sid = 'S1'
8
+ # when renamed: desalisation required
9
+ - alf: |-
10
+ restrict(rename(suppliers, sid: :id), id: 'S1')
11
+ sql: |-
12
+ SELECT t1.sid AS id, t1.name, t1.status, t1.city
13
+ FROM suppliers AS t1
14
+ WHERE t1.sid = 'S1'
15
+ # TRUE
16
+ - alf: |-
17
+ restrict(suppliers, true)
18
+ sql: |-
19
+ SELECT t1.sid, t1.name, t1.status, t1.city
20
+ FROM suppliers AS t1
21
+ WHERE TRUE
22
+ # FALSE
23
+ - alf: |-
24
+ restrict(suppliers, false)
25
+ sql: |-
26
+ SELECT t1.sid, t1.name, t1.status, t1.city
27
+ FROM suppliers AS t1
28
+ WHERE FALSE
29
+ # AND
30
+ - alf: |-
31
+ restrict(suppliers, sid: 'S1', city: 'London')
32
+ sql: |-
33
+ SELECT t1.sid, t1.name, t1.status, t1.city
34
+ FROM suppliers AS t1
35
+ WHERE t1.sid = 'S1' AND t1.city = 'London'
36
+ # OR
37
+ - alf: |-
38
+ restrict(suppliers, Alf::Predicate.or(eq(:sid, 'S1'), eq(:sid, 'S2')))
39
+ sql: |-
40
+ SELECT t1.sid, t1.name, t1.status, t1.city
41
+ FROM suppliers AS t1
42
+ WHERE t1.sid = 'S1' OR t1.sid = 'S2'
43
+ # NOT =
44
+ - alf: |-
45
+ restrict(suppliers, !eq(:sid, 'S1'))
46
+ sql: |-
47
+ SELECT t1.sid, t1.name, t1.status, t1.city
48
+ FROM suppliers AS t1
49
+ WHERE t1.sid <> 'S1'
50
+ # NOT AND
51
+ - alf: |-
52
+ restrict(suppliers, !(eq(:sid, 'S1') & eq(:status, 20)))
53
+ sql: |-
54
+ SELECT t1.sid, t1.name, t1.status, t1.city
55
+ FROM suppliers AS t1
56
+ WHERE NOT(t1.sid = 'S1' AND t1.status = 20)
57
+ # NOT EQUAL
58
+ - alf: |-
59
+ restrict(suppliers, neq(:sid, 'S1'))
60
+ sql: |-
61
+ SELECT t1.sid, t1.name, t1.status, t1.city
62
+ FROM suppliers AS t1
63
+ WHERE t1.sid <> 'S1'
64
+ # GREATER
65
+ - alf: |-
66
+ restrict(suppliers, gt(:status, 10))
67
+ sql: |-
68
+ SELECT t1.sid, t1.name, t1.status, t1.city
69
+ FROM suppliers AS t1
70
+ WHERE t1.status > 10
71
+ # GREATER OR EQUAL
72
+ - alf: |-
73
+ restrict(suppliers, gte(:status, 10))
74
+ sql: |-
75
+ SELECT t1.sid, t1.name, t1.status, t1.city
76
+ FROM suppliers AS t1
77
+ WHERE t1.status >= 10
78
+ # LESS THAN
79
+ - alf: |-
80
+ restrict(suppliers, lt(:status, 10))
81
+ sql: |-
82
+ SELECT t1.sid, t1.name, t1.status, t1.city
83
+ FROM suppliers AS t1
84
+ WHERE t1.status < 10
85
+ # LESS THAN OR EQUAL
86
+ - alf: |-
87
+ restrict(suppliers, lte(:status, 10))
88
+ sql: |-
89
+ SELECT t1.sid, t1.name, t1.status, t1.city
90
+ FROM suppliers AS t1
91
+ WHERE t1.status <= 10
92
+ # IN
93
+ - alf: |-
94
+ restrict(suppliers, Alf::Predicate.in(:sid, ['S1', 'S2']))
95
+ sql: |-
96
+ SELECT t1.sid, t1.name, t1.status, t1.city
97
+ FROM suppliers AS t1
98
+ WHERE t1.sid IN ('S1', 'S2')
99
+ # restrict(restrict)
100
+ - alf: |-
101
+ restrict(restrict(suppliers, sid: 'S1'), status: 20)
102
+ sql:
103
+ SELECT t1.sid, t1.name, t1.status, t1.city
104
+ FROM suppliers AS t1
105
+ WHERE t1.sid = 'S1' AND t1.status = 20
106
+ # restrict(join)
107
+ - alf: |-
108
+ restrict(join(suppliers, supplies), sid: 'S2', qty: 300)
109
+ sql:
110
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
111
+ FROM suppliers AS t1
112
+ JOIN supplies AS t2 ON t1.sid = t2.sid
113
+ WHERE t1.sid = 'S2'
114
+ AND t2.qty = 300
@@ -0,0 +1,90 @@
1
+ # A base union case
2
+ - alf: |-
3
+ union(suppliers_in_london, suppliers_in_paris)
4
+ sql:
5
+ (SELECT t1.sid, t1.name, t1.status, t1.city
6
+ FROM suppliers AS t1
7
+ WHERE t1.city = 'London')
8
+ UNION
9
+ (SELECT t2.sid, t2.name, t2.status, t2.city
10
+ FROM suppliers AS t2
11
+ WHERE t2.city = 'Paris')
12
+ # union requiring a reordering
13
+ - alf: |-
14
+ union(project(suppliers, [:name, :city]), rename(cities, country: :name))
15
+ sql: |-
16
+ (SELECT t1.name, t1.city
17
+ FROM suppliers AS t1)
18
+ UNION
19
+ (SELECT t2.country AS name, t2.city
20
+ FROM cities AS t2)
21
+ # union of union
22
+ - alf: |-
23
+ union(union(suppliers_in_london, suppliers_in_paris), suppliers)
24
+ sql:
25
+ ((SELECT t1.sid, t1.name, t1.status, t1.city
26
+ FROM suppliers AS t1
27
+ WHERE t1.city = 'London')
28
+ UNION
29
+ (SELECT t2.sid, t2.name, t2.status, t2.city
30
+ FROM suppliers AS t2
31
+ WHERE t2.city = 'Paris'))
32
+ UNION
33
+ (SELECT t3.sid, t3.name, t3.status, t3.city
34
+ FROM suppliers AS t3)
35
+ # union with a with_exp at left
36
+ - alf: |-
37
+ union(sort(sort(suppliers_in_london, [:sid, :asc]), [:sid, :desc]), suppliers_in_paris)
38
+ sql:
39
+ WITH t2 AS (
40
+ SELECT t1.sid, t1.name, t1.status, t1.city
41
+ FROM suppliers AS t1
42
+ WHERE t1.city = 'London'
43
+ ORDER BY t1.sid ASC
44
+ )
45
+ (SELECT t2.sid, t2.name, t2.status, t2.city
46
+ FROM t2 AS t2
47
+ ORDER BY t2.sid DESC)
48
+ UNION
49
+ (SELECT t3.sid, t3.name, t3.status, t3.city
50
+ FROM suppliers AS t3
51
+ WHERE t3.city = 'Paris')
52
+ # union with a with_exp at right
53
+ - alf: |-
54
+ union(suppliers_in_london, sort(sort(suppliers_in_paris, [:sid, :asc]), [:sid, :desc]))
55
+ sql:
56
+ WITH t3 AS (
57
+ SELECT t2.sid, t2.name, t2.status, t2.city
58
+ FROM suppliers AS t2
59
+ WHERE t2.city = 'Paris'
60
+ ORDER BY t2.sid ASC
61
+ )
62
+ (SELECT t1.sid, t1.name, t1.status, t1.city
63
+ FROM suppliers AS t1
64
+ WHERE t1.city = 'London')
65
+ UNION
66
+ (SELECT t3.sid, t3.name, t3.status, t3.city
67
+ FROM t3 AS t3
68
+ ORDER BY t3.sid DESC)
69
+ # union of two with_exps
70
+ - alf: |-
71
+ union(sort(sort(suppliers_in_london, [:sid, :asc]), [:sid, :desc]),
72
+ sort(sort(suppliers_in_paris, [:sid, :asc]), [:sid, :desc]))
73
+ sql:
74
+ WITH t2 AS (
75
+ SELECT t1.sid, t1.name, t1.status, t1.city
76
+ FROM suppliers AS t1
77
+ WHERE t1.city = 'London'
78
+ ORDER BY t1.sid ASC),
79
+ t4 AS (
80
+ SELECT t3.sid, t3.name, t3.status, t3.city
81
+ FROM suppliers AS t3
82
+ WHERE t3.city = 'Paris'
83
+ ORDER BY t3.sid ASC)
84
+ (SELECT t2.sid, t2.name, t2.status, t2.city
85
+ FROM t2 AS t2
86
+ ORDER BY t2.sid DESC)
87
+ UNION
88
+ (SELECT t4.sid, t4.name, t4.status, t4.city
89
+ FROM t4 AS t4
90
+ ORDER BY t4.sid DESC)