veritas-sql-generator 0.0.3 → 0.0.4

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 (117) hide show
  1. data/.gemtest +0 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +20 -10
  5. data/Guardfile +22 -0
  6. data/README.rdoc +2 -0
  7. data/Rakefile +4 -2
  8. data/TODO +16 -2
  9. data/config/flay.yml +2 -2
  10. data/config/flog.yml +1 -1
  11. data/config/roodi.yml +5 -5
  12. data/config/site.reek +21 -19
  13. data/lib/veritas/sql/generator.rb +25 -2
  14. data/lib/veritas/sql/generator/core_ext/date.rb +20 -0
  15. data/lib/veritas/sql/generator/core_ext/date_time.rb +45 -0
  16. data/lib/veritas/sql/generator/direction.rb +3 -1
  17. data/lib/veritas/sql/generator/function.rb +54 -0
  18. data/lib/veritas/sql/generator/function/aggregate.rb +134 -0
  19. data/lib/veritas/sql/generator/function/connective.rb +53 -0
  20. data/lib/veritas/sql/generator/function/numeric.rb +135 -0
  21. data/lib/veritas/sql/generator/function/predicate.rb +266 -0
  22. data/lib/veritas/sql/generator/function/proposition.rb +38 -0
  23. data/lib/veritas/sql/generator/function/string.rb +29 -0
  24. data/lib/veritas/sql/generator/identifier.rb +2 -1
  25. data/lib/veritas/sql/generator/literal.rb +15 -18
  26. data/lib/veritas/sql/generator/relation.rb +144 -17
  27. data/lib/veritas/sql/generator/relation/binary.rb +16 -64
  28. data/lib/veritas/sql/generator/relation/set.rb +30 -22
  29. data/lib/veritas/sql/generator/relation/unary.rb +131 -78
  30. data/lib/veritas/sql/generator/version.rb +1 -1
  31. data/spec/unit/date/iso8601_spec.rb +23 -0
  32. data/spec/unit/date_time/iso_8601_spec.rb +115 -0
  33. data/spec/unit/veritas/sql/generator/class_methods/parenthesize_spec.rb +18 -0
  34. data/spec/unit/veritas/sql/generator/function/aggregate/visit_veritas_aggregate_count_spec.rb +16 -0
  35. data/spec/unit/veritas/sql/generator/function/aggregate/visit_veritas_aggregate_maximum_spec.rb +16 -0
  36. data/spec/unit/veritas/sql/generator/function/aggregate/visit_veritas_aggregate_mean_spec.rb +16 -0
  37. data/spec/unit/veritas/sql/generator/function/aggregate/visit_veritas_aggregate_minimum_spec.rb +16 -0
  38. data/spec/unit/veritas/sql/generator/function/aggregate/visit_veritas_aggregate_standard_deviation_spec.rb +16 -0
  39. data/spec/unit/veritas/sql/generator/function/aggregate/visit_veritas_aggregate_sum_spec.rb +16 -0
  40. data/spec/unit/veritas/sql/generator/function/aggregate/visit_veritas_aggregate_variance_spec.rb +16 -0
  41. data/spec/unit/veritas/sql/generator/function/connective/visit_veritas_function_connective_conjunction_spec.rb +20 -0
  42. data/spec/unit/veritas/sql/generator/function/connective/visit_veritas_function_connective_disjunction_spec.rb +20 -0
  43. data/spec/unit/veritas/sql/generator/function/connective/visit_veritas_function_connective_negation_spec.rb +20 -0
  44. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_absolute_spec.rb +15 -0
  45. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_addition_spec.rb +15 -0
  46. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_division_spec.rb +15 -0
  47. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_exponentiation_spec.rb +15 -0
  48. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_modulo_spec.rb +15 -0
  49. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_multiplication_spec.rb +15 -0
  50. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_square_root_spec.rb +15 -0
  51. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_subtraction_spec.rb +15 -0
  52. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_unary_minus_spec.rb +15 -0
  53. data/spec/unit/veritas/sql/generator/function/numeric/visit_veritas_function_numeric_unary_plus_spec.rb +15 -0
  54. data/spec/unit/veritas/sql/generator/{logic/visit_veritas_logic_predicate_equality_spec.rb → function/predicate/visit_veritas_function_predicate_equality_spec.rb} +5 -5
  55. data/spec/unit/veritas/sql/generator/{logic/visit_veritas_logic_predicate_exclusion_spec.rb → function/predicate/visit_veritas_function_predicate_exclusion_spec.rb} +10 -6
  56. data/spec/unit/veritas/sql/generator/function/predicate/visit_veritas_function_predicate_greater_than_or_equal_to_spec.rb +15 -0
  57. data/spec/unit/veritas/sql/generator/{logic/visit_veritas_logic_predicate_greater_than_spec.rb → function/predicate/visit_veritas_function_predicate_greater_than_spec.rb} +5 -5
  58. data/spec/unit/veritas/sql/generator/{logic/visit_veritas_logic_predicate_inclusion_spec.rb → function/predicate/visit_veritas_function_predicate_inclusion_spec.rb} +10 -6
  59. data/spec/unit/veritas/sql/generator/{logic/visit_veritas_logic_predicate_inequality_spec.rb → function/predicate/visit_veritas_function_predicate_inequality_spec.rb} +8 -8
  60. data/spec/unit/veritas/sql/generator/function/predicate/visit_veritas_function_predicate_less_than_or_equal_to_spec.rb +15 -0
  61. data/spec/unit/veritas/sql/generator/{logic/visit_veritas_logic_predicate_less_than_spec.rb → function/predicate/visit_veritas_function_predicate_less_than_spec.rb} +5 -5
  62. data/spec/unit/veritas/sql/generator/function/proposition/visit_veritas_function_proposition_contradiction_spec.rb +15 -0
  63. data/spec/unit/veritas/sql/generator/function/proposition/visit_veritas_function_proposition_tautology_spec.rb +15 -0
  64. data/spec/unit/veritas/sql/generator/function/string/visit_veritas_function_string_length_spec.rb +15 -0
  65. data/spec/unit/veritas/sql/generator/literal/class_methods/dup_frozen_spec.rb +2 -2
  66. data/spec/unit/veritas/sql/generator/relation/binary/base/to_subquery_spec.rb +1 -1
  67. data/spec/unit/veritas/sql/generator/relation/binary/base/{visit_veritas_base_relation_spec.rb → visit_veritas_relation_base_spec.rb} +3 -3
  68. data/spec/unit/veritas/sql/generator/relation/binary/to_s_spec.rb +2 -2
  69. data/spec/unit/veritas/sql/generator/relation/binary/to_subquery_spec.rb +2 -2
  70. data/spec/unit/veritas/sql/generator/relation/binary/visit_veritas_algebra_join_spec.rb +74 -33
  71. data/spec/unit/veritas/sql/generator/relation/binary/visit_veritas_algebra_product_spec.rb +63 -19
  72. data/spec/unit/veritas/sql/generator/relation/class_methods/visit_spec.rb +1 -1
  73. data/spec/unit/veritas/sql/generator/relation/set/class_methods/normalize_operand_headers_spec.rb +35 -0
  74. data/spec/unit/veritas/sql/generator/relation/set/to_s_spec.rb +1 -1
  75. data/spec/unit/veritas/sql/generator/relation/set/to_subquery_spec.rb +4 -4
  76. data/spec/unit/veritas/sql/generator/relation/set/visit_veritas_algebra_difference_spec.rb +83 -30
  77. data/spec/unit/veritas/sql/generator/relation/set/visit_veritas_algebra_intersection_spec.rb +80 -30
  78. data/spec/unit/veritas/sql/generator/relation/set/visit_veritas_algebra_union_spec.rb +80 -30
  79. data/spec/unit/veritas/sql/generator/relation/to_s_spec.rb +50 -0
  80. data/spec/unit/veritas/sql/generator/relation/to_subquery_spec.rb +49 -0
  81. data/spec/unit/veritas/sql/generator/relation/unary/to_s_spec.rb +1 -1
  82. data/spec/unit/veritas/sql/generator/relation/unary/to_subquery_spec.rb +6 -6
  83. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_extension_spec.rb +165 -0
  84. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_projection_spec.rb +84 -29
  85. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_rename_spec.rb +69 -27
  86. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_restriction_spec.rb +64 -22
  87. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_summarization_spec.rb +652 -0
  88. data/spec/unit/veritas/sql/generator/relation/unary/{visit_veritas_base_relation_spec.rb → visit_veritas_relation_base_spec.rb} +4 -4
  89. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_limit_spec.rb +60 -20
  90. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_offset_spec.rb +60 -20
  91. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_order_spec.rb +63 -23
  92. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_reverse_spec.rb +60 -20
  93. data/spec/unit/veritas/sql/generator/relation/visited_spec.rb +1 -1
  94. data/tasks/metrics/ci.rake +7 -0
  95. data/tasks/{quality → metrics}/flay.rake +0 -0
  96. data/tasks/{quality → metrics}/flog.rake +0 -0
  97. data/tasks/{quality → metrics}/heckle.rake +1 -0
  98. data/tasks/{quality → metrics}/metric_fu.rake +3 -0
  99. data/tasks/{quality → metrics}/reek.rake +0 -0
  100. data/tasks/{quality → metrics}/roodi.rake +0 -0
  101. data/tasks/{quality → metrics}/yardstick.rake +0 -0
  102. data/tasks/spec.rake +1 -0
  103. data/veritas-sql-generator.gemspec +82 -114
  104. metadata +137 -125
  105. data/lib/veritas/base_relation.rb +0 -36
  106. data/lib/veritas/sql/generator/logic.rb +0 -349
  107. data/spec/unit/veritas/base_relation/name_spec.rb +0 -45
  108. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_connective_conjunction_spec.rb +0 -16
  109. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_connective_disjunction_spec.rb +0 -16
  110. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_connective_negation_spec.rb +0 -16
  111. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_greater_than_or_equal_to_spec.rb +0 -15
  112. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_less_than_or_equal_to_spec.rb +0 -15
  113. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_proposition_contradiction_spec.rb +0 -15
  114. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_proposition_tautology_spec.rb +0 -15
  115. data/spec/unit/veritas/sql/generator/relation/binary/class_methods/subquery_spec.rb +0 -42
  116. data/spec/unit/veritas/sql/generator/relation/class_methods/subquery_spec.rb +0 -33
  117. data/tasks/quality/ci.rake +0 -2
@@ -12,8 +12,8 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
12
12
  let(:header) { [ id, name, age ] }
13
13
  let(:other_header) { [ id.rename(:other_id), name.rename(:other_name), age.rename(:other_age) ] }
14
14
  let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
15
- let(:users) { BaseRelation.new('users', header, body) }
16
- let(:other) { BaseRelation.new('other', other_header, body) }
15
+ let(:users) { Relation::Base.new('users', header, body) }
16
+ let(:other) { Relation::Base.new('other', other_header, body) }
17
17
  let(:product) { left.product(right) }
18
18
  let(:object) { described_class.new }
19
19
 
@@ -23,8 +23,8 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
23
23
 
24
24
  it_should_behave_like 'a generated SQL SELECT query'
25
25
 
26
- its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM "users" CROSS JOIN "other"') }
27
- its(:to_subquery) { should eql('SELECT * FROM "users" CROSS JOIN "other"') }
26
+ its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM "users" AS "left" CROSS JOIN "other" AS "right"') }
27
+ its(:to_subquery) { should eql('(SELECT * FROM "users" AS "left" CROSS JOIN "other" AS "right")') }
28
28
  end
29
29
 
30
30
  context 'when the operands are a projection' do
@@ -34,7 +34,17 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
34
34
  it_should_behave_like 'a generated SQL SELECT query'
35
35
 
36
36
  its(:to_s) { should eql('SELECT "id", "name", "other_id", "other_name" FROM (SELECT DISTINCT "id", "name" FROM "users") AS "left" CROSS JOIN (SELECT DISTINCT "other_id", "other_name" FROM "other") AS "right"') }
37
- its(:to_subquery) { should eql('SELECT * FROM (SELECT DISTINCT "id", "name" FROM "users") AS "left" CROSS JOIN (SELECT DISTINCT "other_id", "other_name" FROM "other") AS "right"') }
37
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT DISTINCT "id", "name" FROM "users") AS "left" CROSS JOIN (SELECT DISTINCT "other_id", "other_name" FROM "other") AS "right")') }
38
+ end
39
+
40
+ context 'when the operand is an extension' do
41
+ let(:left) { users.extend { |r| r.add(:one, 1) } }
42
+ let(:right) { other.extend { |r| r.add(:two, 2) } }
43
+
44
+ it_should_behave_like 'a generated SQL SELECT query'
45
+
46
+ its(:to_s) { should eql('SELECT "id", "name", "age", "one", "other_id", "other_name", "other_age", "two" FROM (SELECT *, 1 AS "one" FROM "users") AS "left" CROSS JOIN (SELECT *, 2 AS "two" FROM "other") AS "right"') }
47
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT *, 1 AS "one" FROM "users") AS "left" CROSS JOIN (SELECT *, 2 AS "two" FROM "other") AS "right")') }
38
48
  end
39
49
 
40
50
  context 'when the operand is a rename' do
@@ -44,7 +54,7 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
44
54
  it_should_behave_like 'a generated SQL SELECT query'
45
55
 
46
56
  its(:to_s) { should eql('SELECT "user_id", "name", "age", "other_user_id", "other_name", "other_age" FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "left" CROSS JOIN (SELECT "other_id" AS "other_user_id", "other_name", "other_age" FROM "other") AS "right"') }
47
- its(:to_subquery) { should eql('SELECT * FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "left" CROSS JOIN (SELECT "other_id" AS "other_user_id", "other_name", "other_age" FROM "other") AS "right"') }
57
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "left" CROSS JOIN (SELECT "other_id" AS "other_user_id", "other_name", "other_age" FROM "other") AS "right")') }
48
58
  end
49
59
 
50
60
  context 'when the operand is a restriction' do
@@ -54,7 +64,41 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
54
64
  it_should_behave_like 'a generated SQL SELECT query'
55
65
 
56
66
  its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM (SELECT * FROM "users" WHERE "id" = 1) AS "left" CROSS JOIN (SELECT * FROM "other" WHERE "other_id" = 1) AS "right"') }
57
- its(:to_subquery) { should eql('SELECT * FROM (SELECT * FROM "users" WHERE "id" = 1) AS "left" CROSS JOIN (SELECT * FROM "other" WHERE "other_id" = 1) AS "right"') }
67
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT * FROM "users" WHERE "id" = 1) AS "left" CROSS JOIN (SELECT * FROM "other" WHERE "other_id" = 1) AS "right")') }
68
+ end
69
+
70
+ context 'when the operand is a summarization' do
71
+ context 'summarize per table dee' do
72
+ let(:summarize_per) { TABLE_DEE }
73
+ let(:left) { users.summarize(summarize_per) { |r| r.add(:count, r[:id].count) } }
74
+ let(:right) { other.summarize(summarize_per) { |r| r.add(:other_count, r[:other_id].count) } }
75
+
76
+ it_should_behave_like 'a generated SQL SELECT query'
77
+
78
+ its(:to_s) { should eql('SELECT "count", "other_count" FROM (SELECT COUNT ("id") AS "count" FROM "users") AS "left" CROSS JOIN (SELECT COUNT ("other_id") AS "other_count" FROM "other") AS "right"') }
79
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT COUNT ("id") AS "count" FROM "users") AS "left" CROSS JOIN (SELECT COUNT ("other_id") AS "other_count" FROM "other") AS "right")') }
80
+ end
81
+
82
+ context 'summarize per table dum' do
83
+ let(:summarize_per) { TABLE_DUM }
84
+ let(:left) { users.summarize(summarize_per) { |r| r.add(:count, r[:id].count) } }
85
+ let(:right) { other.summarize(summarize_per) { |r| r.add(:other_count, r[:other_id].count) } }
86
+
87
+ it_should_behave_like 'a generated SQL SELECT query'
88
+
89
+ its(:to_s) { should eql('SELECT "count", "other_count" FROM (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) AS "left" CROSS JOIN (SELECT COUNT ("other_id") AS "other_count" FROM "other" HAVING FALSE) AS "right"') }
90
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) AS "left" CROSS JOIN (SELECT COUNT ("other_id") AS "other_count" FROM "other" HAVING FALSE) AS "right")') }
91
+ end
92
+
93
+ context 'summarize by a subset of the operand header' do
94
+ let(:left) { users.summarize([ :id, :name ]) { |r| r.add(:count, r[:age].count) } }
95
+ let(:right) { other.summarize([ :other_id, :other_name ]) { |r| r.add(:other_count, r[:other_age].count) } }
96
+
97
+ it_should_behave_like 'a generated SQL SELECT query'
98
+
99
+ its(:to_s) { should eql('SELECT "id", "name", "count", "other_id", "other_name", "other_count" FROM (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) AS "left" CROSS JOIN (SELECT "other_id", "other_name", COUNT ("other_age") AS "other_count" FROM "other" GROUP BY "other_id", "other_name" HAVING COUNT (*) > 0) AS "right"') }
100
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) AS "left" CROSS JOIN (SELECT "other_id", "other_name", COUNT ("other_age") AS "other_count" FROM "other" GROUP BY "other_id", "other_name" HAVING COUNT (*) > 0) AS "right")') }
101
+ end
58
102
  end
59
103
 
60
104
  context 'when the operand is ordered' do
@@ -64,7 +108,7 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
64
108
  it_should_behave_like 'a generated SQL SELECT query'
65
109
 
66
110
  its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age") AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age") AS "right"') }
67
- its(:to_subquery) { should eql('SELECT * FROM (SELECT * FROM "users" ORDER BY "id", "name", "age") AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age") AS "right"') }
111
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT * FROM "users" ORDER BY "id", "name", "age") AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age") AS "right")') }
68
112
  end
69
113
 
70
114
  context 'when the operand is reversed' do
@@ -74,7 +118,7 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
74
118
  it_should_behave_like 'a generated SQL SELECT query'
75
119
 
76
120
  its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id" DESC, "other_name" DESC, "other_age" DESC) AS "right"') }
77
- its(:to_subquery) { should eql('SELECT * FROM (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id" DESC, "other_name" DESC, "other_age" DESC) AS "right"') }
121
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id" DESC, "other_name" DESC, "other_age" DESC) AS "right")') }
78
122
  end
79
123
 
80
124
  context 'when the operand is limited' do
@@ -84,7 +128,7 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
84
128
  it_should_behave_like 'a generated SQL SELECT query'
85
129
 
86
130
  its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age" LIMIT 1) AS "right"') }
87
- its(:to_subquery) { should eql('SELECT * FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age" LIMIT 1) AS "right"') }
131
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age" LIMIT 1) AS "right")') }
88
132
  end
89
133
 
90
134
  context 'when the operand is an offset' do
@@ -94,7 +138,7 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
94
138
  it_should_behave_like 'a generated SQL SELECT query'
95
139
 
96
140
  its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age" OFFSET 1) AS "right"') }
97
- its(:to_subquery) { should eql('SELECT * FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age" OFFSET 1) AS "right"') }
141
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) AS "left" CROSS JOIN (SELECT * FROM "other" ORDER BY "other_id", "other_name", "other_age" OFFSET 1) AS "right")') }
98
142
  end
99
143
 
100
144
  context 'when the operand is a difference' do
@@ -103,8 +147,8 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
103
147
 
104
148
  it_should_behave_like 'a generated SQL SELECT query'
105
149
 
106
- its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM ((SELECT * FROM "users") EXCEPT (SELECT * FROM "users")) AS "left" CROSS JOIN ((SELECT * FROM "other") EXCEPT (SELECT * FROM "other")) AS "right"') }
107
- its(:to_subquery) { should eql('SELECT * FROM ((SELECT * FROM "users") EXCEPT (SELECT * FROM "users")) AS "left" CROSS JOIN ((SELECT * FROM "other") EXCEPT (SELECT * FROM "other")) AS "right"') }
150
+ its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) AS "left" CROSS JOIN ((SELECT "other_id", "other_name", "other_age" FROM "other") EXCEPT (SELECT "other_id", "other_name", "other_age" FROM "other")) AS "right"') }
151
+ its(:to_subquery) { should eql('(SELECT * FROM ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) AS "left" CROSS JOIN ((SELECT "other_id", "other_name", "other_age" FROM "other") EXCEPT (SELECT "other_id", "other_name", "other_age" FROM "other")) AS "right")') }
108
152
  end
109
153
 
110
154
  context 'when the operand is a intersection' do
@@ -113,8 +157,8 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
113
157
 
114
158
  it_should_behave_like 'a generated SQL SELECT query'
115
159
 
116
- its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM ((SELECT * FROM "users") INTERSECT (SELECT * FROM "users")) AS "left" CROSS JOIN ((SELECT * FROM "other") INTERSECT (SELECT * FROM "other")) AS "right"') }
117
- its(:to_subquery) { should eql('SELECT * FROM ((SELECT * FROM "users") INTERSECT (SELECT * FROM "users")) AS "left" CROSS JOIN ((SELECT * FROM "other") INTERSECT (SELECT * FROM "other")) AS "right"') }
160
+ its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) AS "left" CROSS JOIN ((SELECT "other_id", "other_name", "other_age" FROM "other") INTERSECT (SELECT "other_id", "other_name", "other_age" FROM "other")) AS "right"') }
161
+ its(:to_subquery) { should eql('(SELECT * FROM ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) AS "left" CROSS JOIN ((SELECT "other_id", "other_name", "other_age" FROM "other") INTERSECT (SELECT "other_id", "other_name", "other_age" FROM "other")) AS "right")') }
118
162
  end
119
163
 
120
164
  context 'when the operand is a union' do
@@ -123,8 +167,8 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
123
167
 
124
168
  it_should_behave_like 'a generated SQL SELECT query'
125
169
 
126
- its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM ((SELECT * FROM "users") UNION (SELECT * FROM "users")) AS "left" CROSS JOIN ((SELECT * FROM "other") UNION (SELECT * FROM "other")) AS "right"') }
127
- its(:to_subquery) { should eql('SELECT * FROM ((SELECT * FROM "users") UNION (SELECT * FROM "users")) AS "left" CROSS JOIN ((SELECT * FROM "other") UNION (SELECT * FROM "other")) AS "right"') }
170
+ its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) AS "left" CROSS JOIN ((SELECT "other_id", "other_name", "other_age" FROM "other") UNION (SELECT "other_id", "other_name", "other_age" FROM "other")) AS "right"') }
171
+ its(:to_subquery) { should eql('(SELECT * FROM ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) AS "left" CROSS JOIN ((SELECT "other_id", "other_name", "other_age" FROM "other") UNION (SELECT "other_id", "other_name", "other_age" FROM "other")) AS "right")') }
128
172
  end
129
173
 
130
174
  context 'when the operand is a join' do
@@ -133,7 +177,7 @@ describe SQL::Generator::Relation::Binary, '#visit_veritas_algebra_product' do
133
177
 
134
178
  it_should_behave_like 'a generated SQL SELECT query'
135
179
 
136
- its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM (SELECT * FROM "users" NATURAL JOIN "users") AS "left" CROSS JOIN (SELECT * FROM "other" NATURAL JOIN "other") AS "right"') }
137
- its(:to_subquery) { should eql('SELECT * FROM (SELECT * FROM "users" NATURAL JOIN "users") AS "left" CROSS JOIN (SELECT * FROM "other" NATURAL JOIN "other") AS "right"') }
180
+ its(:to_s) { should eql('SELECT "id", "name", "age", "other_id", "other_name", "other_age" FROM (SELECT * FROM "users" AS "left" NATURAL JOIN "users" AS "right") AS "left" CROSS JOIN (SELECT * FROM "other" AS "left" NATURAL JOIN "other" AS "right") AS "right"') }
181
+ its(:to_subquery) { should eql('(SELECT * FROM (SELECT * FROM "users" AS "left" NATURAL JOIN "users" AS "right") AS "left" CROSS JOIN (SELECT * FROM "other" AS "left" NATURAL JOIN "other" AS "right") AS "right")') }
138
182
  end
139
183
  end
@@ -10,7 +10,7 @@ describe SQL::Generator::Relation, '.visit' do
10
10
  let(:age) { Attribute::Integer.new(:age, :required => false) }
11
11
  let(:header) { [ id, name, age ] }
12
12
  let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
13
- let(:base_relation) { BaseRelation.new('users', header, body) }
13
+ let(:base_relation) { Relation::Base.new('users', header, body) }
14
14
  let(:object) { described_class }
15
15
 
16
16
  context 'when the relation is a set operation' do
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Set, '.normalize_operand_headers' do
6
+ subject { object.normalize_operand_headers(relation) }
7
+
8
+ let(:object) { described_class }
9
+ let(:relation) { left.union(right) }
10
+ let(:relation_name) { 'test' }
11
+ let(:header) { [ [ :id, Integer, ], [ :name, String ] ] }
12
+ let(:body) { [].each }
13
+
14
+ context 'when the left and right headers are sorted in the same order' do
15
+ let(:left) { Relation::Base.new(relation_name, header, body) }
16
+ let(:right) { Relation::Base.new(relation_name, header, body) }
17
+
18
+ it { should equal(relation) }
19
+ end
20
+
21
+ context 'when the left and right headers are sorted in different order' do
22
+ let(:left) { Relation::Base.new(relation_name, header, body) }
23
+ let(:right) { Relation::Base.new(relation_name, header.reverse, body) }
24
+
25
+ it { should_not equal(relation) }
26
+
27
+ its(:right) { should_not equal(right) }
28
+
29
+ it { should be_kind_of(relation.class) }
30
+
31
+ its(:left) { should equal(left) }
32
+
33
+ its(:right) { should eql(right.project(header)) }
34
+ end
35
+ end
@@ -10,7 +10,7 @@ describe SQL::Generator::Relation::Set, '#to_s' do
10
10
  let(:age) { Attribute::Integer.new(:age, :required => false) }
11
11
  let(:header) { [ id, name, age ] }
12
12
  let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
13
- let(:base_relation) { BaseRelation.new('users', header, body) }
13
+ let(:base_relation) { Relation::Base.new('users', header, body) }
14
14
  let(:object) { described_class.new }
15
15
 
16
16
  context 'when no object visited' do
@@ -10,7 +10,7 @@ describe SQL::Generator::Relation::Set, '#to_subquery' do
10
10
  let(:age) { Attribute::Integer.new(:age, :required => false) }
11
11
  let(:header) { [ id, name, age ] }
12
12
  let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
13
- let(:base_relation) { BaseRelation.new('users', header, body) }
13
+ let(:base_relation) { Relation::Base.new('users', header, body) }
14
14
  let(:object) { described_class.new }
15
15
 
16
16
  context 'when no object visited' do
@@ -30,7 +30,7 @@ describe SQL::Generator::Relation::Set, '#to_subquery' do
30
30
 
31
31
  it_should_behave_like 'a generated SQL expression'
32
32
 
33
- its(:to_s) { should eql('(SELECT * FROM "users") EXCEPT (SELECT * FROM "users")') }
33
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users"))') }
34
34
  end
35
35
 
36
36
  context 'when an intersection is visited' do
@@ -40,7 +40,7 @@ describe SQL::Generator::Relation::Set, '#to_subquery' do
40
40
 
41
41
  it_should_behave_like 'a generated SQL expression'
42
42
 
43
- its(:to_s) { should eql('(SELECT * FROM "users") INTERSECT (SELECT * FROM "users")') }
43
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users"))') }
44
44
  end
45
45
 
46
46
  context 'when a union is visited' do
@@ -50,6 +50,6 @@ describe SQL::Generator::Relation::Set, '#to_subquery' do
50
50
 
51
51
  it_should_behave_like 'a generated SQL expression'
52
52
 
53
- its(:to_s) { should eql('(SELECT * FROM "users") UNION (SELECT * FROM "users")') }
53
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users"))') }
54
54
  end
55
55
  end
@@ -11,7 +11,7 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
11
11
  let(:age) { Attribute::Integer.new(:age, :required => false) }
12
12
  let(:header) { [ id, name, age ] }
13
13
  let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
14
- let(:base_relation) { BaseRelation.new(relation_name, header, body) }
14
+ let(:base_relation) { Relation::Base.new(relation_name, header, body) }
15
15
  let(:left) { operand }
16
16
  let(:right) { operand }
17
17
  let(:difference) { left.difference(right) }
@@ -22,8 +22,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
22
22
 
23
23
  it_should_behave_like 'a generated SQL SELECT query'
24
24
 
25
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")') }
26
- its(:to_subquery) { should eql('(SELECT * FROM "users") EXCEPT (SELECT * FROM "users")') }
25
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")') }
26
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users"))') }
27
27
  end
28
28
 
29
29
  context 'when the operands are projections' do
@@ -31,8 +31,17 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
31
31
 
32
32
  it_should_behave_like 'a generated SQL SELECT query'
33
33
 
34
- its(:to_s) { should eql('(SELECT DISTINCT "id", "name" FROM "users") EXCEPT (SELECT DISTINCT "id", "name" FROM "users")') }
35
- its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM "users") EXCEPT (SELECT DISTINCT "id", "name" FROM "users")') }
34
+ its(:to_s) { should eql('(SELECT DISTINCT "id", "name" FROM "users") EXCEPT (SELECT DISTINCT "id", "name" FROM "users")') }
35
+ its(:to_subquery) { should eql('((SELECT DISTINCT "id", "name" FROM "users") EXCEPT (SELECT DISTINCT "id", "name" FROM "users"))') }
36
+ end
37
+
38
+ context 'when the operand is an extension' do
39
+ let(:operand) { base_relation.extend { |r| r.add(:one, 1) } }
40
+
41
+ it_should_behave_like 'a generated SQL SELECT query'
42
+
43
+ its(:to_s) { should eql('(SELECT "id", "name", "age", 1 AS "one" FROM "users") EXCEPT (SELECT "id", "name", "age", 1 AS "one" FROM "users")') }
44
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age", 1 AS "one" FROM "users") EXCEPT (SELECT "id", "name", "age", 1 AS "one" FROM "users"))') }
36
45
  end
37
46
 
38
47
  context 'when the operands are renames' do
@@ -40,8 +49,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
40
49
 
41
50
  it_should_behave_like 'a generated SQL SELECT query'
42
51
 
43
- its(:to_s) { should eql('(SELECT "id" AS "user_id", "name", "age" FROM "users") EXCEPT (SELECT "id" AS "user_id", "name", "age" FROM "users")') }
44
- its(:to_subquery) { should eql('(SELECT "id" AS "user_id", "name", "age" FROM "users") EXCEPT (SELECT "id" AS "user_id", "name", "age" FROM "users")') }
52
+ its(:to_s) { should eql('(SELECT "id" AS "user_id", "name", "age" FROM "users") EXCEPT (SELECT "id" AS "user_id", "name", "age" FROM "users")') }
53
+ its(:to_subquery) { should eql('((SELECT "id" AS "user_id", "name", "age" FROM "users") EXCEPT (SELECT "id" AS "user_id", "name", "age" FROM "users"))') }
45
54
  end
46
55
 
47
56
  context 'when the operands are restrictions' do
@@ -49,8 +58,42 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
49
58
 
50
59
  it_should_behave_like 'a generated SQL SELECT query'
51
60
 
52
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) EXCEPT (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1)') }
53
- its(:to_subquery) { should eql('(SELECT * FROM "users" WHERE "id" = 1) EXCEPT (SELECT * FROM "users" WHERE "id" = 1)') }
61
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) EXCEPT (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1)') }
62
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) EXCEPT (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1))') }
63
+ end
64
+
65
+ context 'when the operand is a summarization' do
66
+ end
67
+
68
+ context 'when the operand is a summarization' do
69
+ context 'summarize per table dee' do
70
+ let(:summarize_per) { TABLE_DEE }
71
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r[:id].count) } }
72
+
73
+ it_should_behave_like 'a generated SQL SELECT query'
74
+
75
+ its(:to_s) { should eql('(SELECT COUNT ("id") AS "count" FROM "users") EXCEPT (SELECT COUNT ("id") AS "count" FROM "users")') }
76
+ its(:to_subquery) { should eql('((SELECT COUNT ("id") AS "count" FROM "users") EXCEPT (SELECT COUNT ("id") AS "count" FROM "users"))') }
77
+ end
78
+
79
+ context 'summarize per table dum' do
80
+ let(:summarize_per) { TABLE_DUM }
81
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r[:id].count) } }
82
+
83
+ it_should_behave_like 'a generated SQL SELECT query'
84
+
85
+ its(:to_s) { should eql('(SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) EXCEPT (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE)') }
86
+ its(:to_subquery) { should eql('((SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) EXCEPT (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE))') }
87
+ end
88
+
89
+ context 'summarize by a subset of the operand header' do
90
+ let(:operand) { base_relation.summarize([ :id, :name ]) { |r| r.add(:count, r[:age].count) } }
91
+
92
+ it_should_behave_like 'a generated SQL SELECT query'
93
+
94
+ its(:to_s) { should eql('(SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) EXCEPT (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0)') }
95
+ its(:to_subquery) { should eql('((SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) EXCEPT (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0))') }
96
+ end
54
97
  end
55
98
 
56
99
  context 'when the operand is ordered' do
@@ -58,8 +101,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
58
101
 
59
102
  it_should_behave_like 'a generated SQL SELECT query'
60
103
 
61
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age") EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age")') }
62
- its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id", "name", "age") EXCEPT (SELECT * FROM "users" ORDER BY "id", "name", "age")') }
104
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age") EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age")') }
105
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age") EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age"))') }
63
106
  end
64
107
 
65
108
  context 'when the operand is reversed' do
@@ -67,8 +110,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
67
110
 
68
111
  it_should_behave_like 'a generated SQL SELECT query'
69
112
 
70
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC)') }
71
- its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) EXCEPT (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC)') }
113
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC)') }
114
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC))') }
72
115
  end
73
116
 
74
117
  context 'when the operand is limited' do
@@ -76,8 +119,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
76
119
 
77
120
  it_should_behave_like 'a generated SQL SELECT query'
78
121
 
79
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1)') }
80
- its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) EXCEPT (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1)') }
122
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1)') }
123
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1))') }
81
124
  end
82
125
 
83
126
  context 'when the operands are offsets' do
@@ -85,8 +128,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
85
128
 
86
129
  it_should_behave_like 'a generated SQL SELECT query'
87
130
 
88
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1)') }
89
- its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) EXCEPT (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1)') }
131
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1)') }
132
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1) EXCEPT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1))') }
90
133
  end
91
134
 
92
135
  context 'when the operands are differences' do
@@ -94,8 +137,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
94
137
 
95
138
  it_should_behave_like 'a generated SQL SELECT query'
96
139
 
97
- its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users"))') }
98
- its(:to_subquery) { should eql('((SELECT * FROM "users") EXCEPT (SELECT * FROM "users")) EXCEPT ((SELECT * FROM "users") EXCEPT (SELECT * FROM "users"))') }
140
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users"))') }
141
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")))') }
99
142
  end
100
143
 
101
144
  context 'when the operands are intersections' do
@@ -103,8 +146,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
103
146
 
104
147
  it_should_behave_like 'a generated SQL SELECT query'
105
148
 
106
- its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users"))') }
107
- its(:to_subquery) { should eql('((SELECT * FROM "users") INTERSECT (SELECT * FROM "users")) EXCEPT ((SELECT * FROM "users") INTERSECT (SELECT * FROM "users"))') }
149
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users"))') }
150
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")))') }
108
151
  end
109
152
 
110
153
  context 'when the operands are unions' do
@@ -112,8 +155,8 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
112
155
 
113
156
  it_should_behave_like 'a generated SQL SELECT query'
114
157
 
115
- its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users"))') }
116
- its(:to_subquery) { should eql('((SELECT * FROM "users") UNION (SELECT * FROM "users")) EXCEPT ((SELECT * FROM "users") UNION (SELECT * FROM "users"))') }
158
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users"))') }
159
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) EXCEPT ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")))') }
117
160
  end
118
161
 
119
162
  context 'when the operands are joins' do
@@ -121,18 +164,28 @@ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_difference' do
121
164
 
122
165
  it_should_behave_like 'a generated SQL SELECT query'
123
166
 
124
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" NATURAL JOIN "users") EXCEPT (SELECT "id", "name", "age" FROM "users" NATURAL JOIN "users")') }
125
- its(:to_subquery) { should eql('(SELECT * FROM "users" NATURAL JOIN "users") EXCEPT (SELECT * FROM "users" NATURAL JOIN "users")') }
167
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right") EXCEPT (SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right")') }
168
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right") EXCEPT (SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right"))') }
126
169
  end
127
170
 
128
171
  context 'when the operands have different base relations' do
129
- let(:relation_name) { 'users_others' }
130
- let(:left) { BaseRelation.new('users', header, body) }
131
- let(:right) { BaseRelation.new('others', header, body) }
172
+ let(:relation_name) { 'users_others' }
173
+ let(:left) { Relation::Base.new('users', header, body) }
174
+ let(:right) { Relation::Base.new('others', header, body) }
175
+
176
+ it_should_behave_like 'a generated SQL SELECT query'
177
+
178
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "others")') }
179
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "others"))') }
180
+ end
181
+
182
+ context 'when the operands have headers sorted in different orders' do
183
+ let(:left) { Relation::Base.new(relation_name, header, body) }
184
+ let(:right) { Relation::Base.new(relation_name, header.reverse, body) }
132
185
 
133
186
  it_should_behave_like 'a generated SQL SELECT query'
134
187
 
135
- its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "others")') }
136
- its(:to_subquery) { should eql('(SELECT * FROM "users") EXCEPT (SELECT * FROM "others")') }
188
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT DISTINCT "id", "name", "age" FROM "users")') }
189
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT DISTINCT "id", "name", "age" FROM "users"))') }
137
190
  end
138
191
  end