axiom-sql-generator 0.1.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 (136) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +37 -0
  4. data/.rspec +4 -0
  5. data/.rvmrc +1 -0
  6. data/.travis.yml +35 -0
  7. data/CONTRIBUTING.md +11 -0
  8. data/Gemfile +8 -0
  9. data/Gemfile.devtools +57 -0
  10. data/Guardfile +23 -0
  11. data/LICENSE +20 -0
  12. data/README.md +70 -0
  13. data/Rakefile +5 -0
  14. data/TODO +34 -0
  15. data/axiom-sql-generator.gemspec +25 -0
  16. data/config/flay.yml +3 -0
  17. data/config/flog.yml +2 -0
  18. data/config/mutant.yml +3 -0
  19. data/config/reek.yml +165 -0
  20. data/config/yardstick.yml +2 -0
  21. data/lib/axiom-sql-generator.rb +3 -0
  22. data/lib/axiom/sql/generator.rb +61 -0
  23. data/lib/axiom/sql/generator/attribute.rb +25 -0
  24. data/lib/axiom/sql/generator/core_ext/date.rb +20 -0
  25. data/lib/axiom/sql/generator/core_ext/date_time.rb +46 -0
  26. data/lib/axiom/sql/generator/direction.rb +38 -0
  27. data/lib/axiom/sql/generator/function.rb +55 -0
  28. data/lib/axiom/sql/generator/function/aggregate.rb +134 -0
  29. data/lib/axiom/sql/generator/function/connective.rb +53 -0
  30. data/lib/axiom/sql/generator/function/numeric.rb +135 -0
  31. data/lib/axiom/sql/generator/function/predicate.rb +266 -0
  32. data/lib/axiom/sql/generator/function/proposition.rb +38 -0
  33. data/lib/axiom/sql/generator/function/string.rb +29 -0
  34. data/lib/axiom/sql/generator/identifier.rb +28 -0
  35. data/lib/axiom/sql/generator/literal.rb +157 -0
  36. data/lib/axiom/sql/generator/relation.rb +240 -0
  37. data/lib/axiom/sql/generator/relation/base.rb +14 -0
  38. data/lib/axiom/sql/generator/relation/binary.rb +136 -0
  39. data/lib/axiom/sql/generator/relation/insertion.rb +62 -0
  40. data/lib/axiom/sql/generator/relation/materialized.rb +60 -0
  41. data/lib/axiom/sql/generator/relation/set.rb +107 -0
  42. data/lib/axiom/sql/generator/relation/unary.rb +379 -0
  43. data/lib/axiom/sql/generator/version.rb +12 -0
  44. data/lib/axiom/sql/generator/visitor.rb +121 -0
  45. data/spec/rcov.opts +7 -0
  46. data/spec/shared/generated_sql_behavior.rb +15 -0
  47. data/spec/spec_helper.rb +33 -0
  48. data/spec/support/config_alias.rb +3 -0
  49. data/spec/unit/axiom/sql/generator/attribute/visit_axiom_attribute_spec.rb +15 -0
  50. data/spec/unit/axiom/sql/generator/class_methods/parenthesize_spec.rb +18 -0
  51. data/spec/unit/axiom/sql/generator/direction/visit_axiom_relation_operation_order_ascending_spec.rb +15 -0
  52. data/spec/unit/axiom/sql/generator/direction/visit_axiom_relation_operation_order_descending_spec.rb +15 -0
  53. data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_count_spec.rb +16 -0
  54. data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_maximum_spec.rb +16 -0
  55. data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_mean_spec.rb +16 -0
  56. data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_minimum_spec.rb +16 -0
  57. data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_standard_deviation_spec.rb +16 -0
  58. data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_sum_spec.rb +16 -0
  59. data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_variance_spec.rb +16 -0
  60. data/spec/unit/axiom/sql/generator/function/connective/visit_axiom_function_connective_conjunction_spec.rb +20 -0
  61. data/spec/unit/axiom/sql/generator/function/connective/visit_axiom_function_connective_disjunction_spec.rb +20 -0
  62. data/spec/unit/axiom/sql/generator/function/connective/visit_axiom_function_connective_negation_spec.rb +20 -0
  63. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_absolute_spec.rb +15 -0
  64. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_addition_spec.rb +15 -0
  65. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_division_spec.rb +15 -0
  66. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_exponentiation_spec.rb +15 -0
  67. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_modulo_spec.rb +15 -0
  68. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_multiplication_spec.rb +15 -0
  69. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_square_root_spec.rb +15 -0
  70. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_subtraction_spec.rb +15 -0
  71. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_unary_minus_spec.rb +15 -0
  72. data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_unary_plus_spec.rb +15 -0
  73. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_equality_spec.rb +27 -0
  74. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_exclusion_spec.rb +47 -0
  75. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_greater_than_or_equal_to_spec.rb +15 -0
  76. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_greater_than_spec.rb +15 -0
  77. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_inclusion_spec.rb +47 -0
  78. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_inequality_spec.rb +55 -0
  79. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_less_than_or_equal_to_spec.rb +15 -0
  80. data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_less_than_spec.rb +15 -0
  81. data/spec/unit/axiom/sql/generator/function/proposition/visit_axiom_function_proposition_contradiction_spec.rb +15 -0
  82. data/spec/unit/axiom/sql/generator/function/proposition/visit_axiom_function_proposition_tautology_spec.rb +15 -0
  83. data/spec/unit/axiom/sql/generator/function/string/visit_axiom_function_string_length_spec.rb +15 -0
  84. data/spec/unit/axiom/sql/generator/identifier/visit_identifier_spec.rb +26 -0
  85. data/spec/unit/axiom/sql/generator/literal/class_methods/dup_frozen_spec.rb +23 -0
  86. data/spec/unit/axiom/sql/generator/literal/visit_class_spec.rb +31 -0
  87. data/spec/unit/axiom/sql/generator/literal/visit_date_spec.rb +15 -0
  88. data/spec/unit/axiom/sql/generator/literal/visit_date_time_spec.rb +55 -0
  89. data/spec/unit/axiom/sql/generator/literal/visit_enumerable_spec.rb +15 -0
  90. data/spec/unit/axiom/sql/generator/literal/visit_false_class_spec.rb +14 -0
  91. data/spec/unit/axiom/sql/generator/literal/visit_nil_class_spec.rb +14 -0
  92. data/spec/unit/axiom/sql/generator/literal/visit_numeric_spec.rb +34 -0
  93. data/spec/unit/axiom/sql/generator/literal/visit_string_spec.rb +26 -0
  94. data/spec/unit/axiom/sql/generator/literal/visit_time_spec.rb +97 -0
  95. data/spec/unit/axiom/sql/generator/literal/visit_true_class_spec.rb +14 -0
  96. data/spec/unit/axiom/sql/generator/relation/binary/base/to_subquery_spec.rb +35 -0
  97. data/spec/unit/axiom/sql/generator/relation/binary/base/visit_axiom_relation_base_spec.rb +22 -0
  98. data/spec/unit/axiom/sql/generator/relation/binary/to_s_spec.rb +35 -0
  99. data/spec/unit/axiom/sql/generator/relation/binary/to_subquery_spec.rb +35 -0
  100. data/spec/unit/axiom/sql/generator/relation/binary/visit_axiom_algebra_join_spec.rb +179 -0
  101. data/spec/unit/axiom/sql/generator/relation/binary/visit_axiom_algebra_product_spec.rb +183 -0
  102. data/spec/unit/axiom/sql/generator/relation/class_methods/visit_spec.rb +71 -0
  103. data/spec/unit/axiom/sql/generator/relation/insertion/to_subquery_spec.rb +15 -0
  104. data/spec/unit/axiom/sql/generator/relation/insertion/visit_axiom_relation_operation_insertion_spec.rb +187 -0
  105. data/spec/unit/axiom/sql/generator/relation/materialized/visit_axiom_relation_materialized_spec.rb +28 -0
  106. data/spec/unit/axiom/sql/generator/relation/materialized/visited_spec.rb +26 -0
  107. data/spec/unit/axiom/sql/generator/relation/name_spec.rb +30 -0
  108. data/spec/unit/axiom/sql/generator/relation/set/class_methods/normalize_operand_headers_spec.rb +35 -0
  109. data/spec/unit/axiom/sql/generator/relation/set/to_s_spec.rb +55 -0
  110. data/spec/unit/axiom/sql/generator/relation/set/to_subquery_spec.rb +55 -0
  111. data/spec/unit/axiom/sql/generator/relation/set/visit_axiom_algebra_difference_spec.rb +191 -0
  112. data/spec/unit/axiom/sql/generator/relation/set/visit_axiom_algebra_intersection_spec.rb +188 -0
  113. data/spec/unit/axiom/sql/generator/relation/set/visit_axiom_algebra_union_spec.rb +188 -0
  114. data/spec/unit/axiom/sql/generator/relation/to_s_spec.rb +50 -0
  115. data/spec/unit/axiom/sql/generator/relation/to_sql_spec.rb +52 -0
  116. data/spec/unit/axiom/sql/generator/relation/to_subquery_spec.rb +49 -0
  117. data/spec/unit/axiom/sql/generator/relation/unary/to_s_spec.rb +55 -0
  118. data/spec/unit/axiom/sql/generator/relation/unary/to_subquery_spec.rb +75 -0
  119. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_extension_spec.rb +165 -0
  120. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_projection_spec.rb +193 -0
  121. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_rename_spec.rb +178 -0
  122. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_restriction_spec.rb +199 -0
  123. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_summarization_spec.rb +652 -0
  124. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_base_spec.rb +21 -0
  125. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_limit_spec.rb +165 -0
  126. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_offset_spec.rb +165 -0
  127. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_order_spec.rb +183 -0
  128. data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_reverse_spec.rb +165 -0
  129. data/spec/unit/axiom/sql/generator/relation/visit_spec.rb +54 -0
  130. data/spec/unit/axiom/sql/generator/relation/visited_spec.rb +35 -0
  131. data/spec/unit/axiom/sql/generator/visitor/class_methods/handler_for_spec.rb +71 -0
  132. data/spec/unit/axiom/sql/generator/visitor/visit_spec.rb +12 -0
  133. data/spec/unit/axiom/sql/generator/visitor/visited_spec.rb +11 -0
  134. data/spec/unit/date/iso8601_spec.rb +23 -0
  135. data/spec/unit/date_time/iso8601_spec.rb +112 -0
  136. metadata +325 -0
@@ -0,0 +1,188 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Set, '#visit_axiom_algebra_intersection' do
6
+ subject { object.visit_axiom_algebra_intersection(intersection) }
7
+
8
+ let(:relation_name) { 'users' }
9
+ let(:id) { Attribute::Integer.new(:id) }
10
+ let(:name) { Attribute::String.new(:name) }
11
+ let(:age) { Attribute::Integer.new(:age, :required => false) }
12
+ let(:header) { [ id, name, age ] }
13
+ let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
14
+ let(:base_relation) { Relation::Base.new(relation_name, header, body) }
15
+ let(:left) { operand }
16
+ let(:right) { operand }
17
+ let(:intersection) { left.intersect(right) }
18
+ let(:object) { described_class.new }
19
+
20
+ context 'when the operands are base relations' do
21
+ let(:operand) { base_relation }
22
+
23
+ it_should_behave_like 'a generated SQL SELECT query'
24
+
25
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")') }
26
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users"))') }
27
+ end
28
+
29
+ context 'when the operands are projections' do
30
+ let(:operand) { base_relation.project([ :id, :name ]) }
31
+
32
+ it_should_behave_like 'a generated SQL SELECT query'
33
+
34
+ its(:to_s) { should eql('(SELECT DISTINCT "id", "name" FROM "users") INTERSECT (SELECT DISTINCT "id", "name" FROM "users")') }
35
+ its(:to_subquery) { should eql('((SELECT DISTINCT "id", "name" FROM "users") INTERSECT (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") INTERSECT (SELECT "id", "name", "age", 1 AS "one" FROM "users")') }
44
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age", 1 AS "one" FROM "users") INTERSECT (SELECT "id", "name", "age", 1 AS "one" FROM "users"))') }
45
+ end
46
+
47
+ context 'when the operands are renames' do
48
+ let(:operand) { base_relation.rename(:id => :user_id) }
49
+
50
+ it_should_behave_like 'a generated SQL SELECT query'
51
+
52
+ its(:to_s) { should eql('(SELECT "id" AS "user_id", "name", "age" FROM "users") INTERSECT (SELECT "id" AS "user_id", "name", "age" FROM "users")') }
53
+ its(:to_subquery) { should eql('((SELECT "id" AS "user_id", "name", "age" FROM "users") INTERSECT (SELECT "id" AS "user_id", "name", "age" FROM "users"))') }
54
+ end
55
+
56
+ context 'when the operands are restrictions' do
57
+ let(:operand) { base_relation.restrict { |r| r.id.eq(1) } }
58
+
59
+ it_should_behave_like 'a generated SQL SELECT query'
60
+
61
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) INTERSECT (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1)') }
62
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) INTERSECT (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1))') }
63
+ end
64
+
65
+ context 'when the operand is a summarization' do
66
+ context 'summarize per table dee' do
67
+ let(:summarize_per) { TABLE_DEE }
68
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
69
+
70
+ it_should_behave_like 'a generated SQL SELECT query'
71
+
72
+ its(:to_s) { should eql('(SELECT COUNT ("id") AS "count" FROM "users") INTERSECT (SELECT COUNT ("id") AS "count" FROM "users")') }
73
+ its(:to_subquery) { should eql('((SELECT COUNT ("id") AS "count" FROM "users") INTERSECT (SELECT COUNT ("id") AS "count" FROM "users"))') }
74
+ end
75
+
76
+ context 'summarize per table dum' do
77
+ let(:summarize_per) { TABLE_DUM }
78
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
79
+
80
+ it_should_behave_like 'a generated SQL SELECT query'
81
+
82
+ its(:to_s) { should eql('(SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) INTERSECT (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE)') }
83
+ its(:to_subquery) { should eql('((SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) INTERSECT (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE))') }
84
+ end
85
+
86
+ context 'summarize by a subset of the operand header' do
87
+ let(:operand) { base_relation.summarize([ :id, :name ]) { |r| r.add(:count, r.age.count) } }
88
+
89
+ it_should_behave_like 'a generated SQL SELECT query'
90
+
91
+ its(:to_s) { should eql('(SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) INTERSECT (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0)') }
92
+ its(:to_subquery) { should eql('((SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) INTERSECT (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0))') }
93
+ end
94
+ end
95
+
96
+ context 'when the operand is ordered' do
97
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] } }
98
+
99
+ it_should_behave_like 'a generated SQL SELECT query'
100
+
101
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age") INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age")') }
102
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age") INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age"))') }
103
+ end
104
+
105
+ context 'when the operand is reversed' do
106
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.reverse }
107
+
108
+ it_should_behave_like 'a generated SQL SELECT query'
109
+
110
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC)') }
111
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC))') }
112
+ end
113
+
114
+ context 'when the operand is limited' do
115
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.take(1) }
116
+
117
+ it_should_behave_like 'a generated SQL SELECT query'
118
+
119
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1) INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1)') }
120
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1) INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1))') }
121
+ end
122
+
123
+ context 'when the operands are offsets' do
124
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.drop(1) }
125
+
126
+ it_should_behave_like 'a generated SQL SELECT query'
127
+
128
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1) INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1)') }
129
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1) INTERSECT (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1))') }
130
+ end
131
+
132
+ context 'when the operands are differences' do
133
+ let(:operand) { base_relation.difference(base_relation) }
134
+
135
+ it_should_behave_like 'a generated SQL SELECT query'
136
+
137
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) INTERSECT ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users"))') }
138
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) INTERSECT ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")))') }
139
+ end
140
+
141
+ context 'when the operands are intersections' do
142
+ let(:operand) { base_relation.intersect(base_relation) }
143
+
144
+ it_should_behave_like 'a generated SQL SELECT query'
145
+
146
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) INTERSECT ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users"))') }
147
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) INTERSECT ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")))') }
148
+ end
149
+
150
+ context 'when the operands are unions' do
151
+ let(:operand) { base_relation.union(base_relation) }
152
+
153
+ it_should_behave_like 'a generated SQL SELECT query'
154
+
155
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) INTERSECT ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users"))') }
156
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) INTERSECT ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")))') }
157
+ end
158
+
159
+ context 'when the operands are joins' do
160
+ let(:operand) { base_relation.join(base_relation) }
161
+
162
+ it_should_behave_like 'a generated SQL SELECT query'
163
+
164
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right") INTERSECT (SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right")') }
165
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right") INTERSECT (SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right"))') }
166
+ end
167
+
168
+ context 'when the operands have different base relations' do
169
+ let(:relation_name) { 'users_others' }
170
+ let(:left) { Relation::Base.new('users', header, body) }
171
+ let(:right) { Relation::Base.new('others', header, body) }
172
+
173
+ it_should_behave_like 'a generated SQL SELECT query'
174
+
175
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "others")') }
176
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "others"))') }
177
+ end
178
+
179
+ context 'when the operands have headers sorted in different orders' do
180
+ let(:left) { Relation::Base.new(relation_name, header, body) }
181
+ let(:right) { Relation::Base.new(relation_name, header.reverse, body) }
182
+
183
+ it_should_behave_like 'a generated SQL SELECT query'
184
+
185
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT DISTINCT "id", "name", "age" FROM "users")') }
186
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT DISTINCT "id", "name", "age" FROM "users"))') }
187
+ end
188
+ end
@@ -0,0 +1,188 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Set, '#visit_axiom_algebra_union' do
6
+ subject { object.visit_axiom_algebra_union(union) }
7
+
8
+ let(:relation_name) { 'users' }
9
+ let(:id) { Attribute::Integer.new(:id) }
10
+ let(:name) { Attribute::String.new(:name) }
11
+ let(:age) { Attribute::Integer.new(:age, :required => false) }
12
+ let(:header) { [ id, name, age ] }
13
+ let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
14
+ let(:base_relation) { Relation::Base.new(relation_name, header, body) }
15
+ let(:left) { operand }
16
+ let(:right) { operand }
17
+ let(:union) { left.union(right) }
18
+ let(:object) { described_class.new }
19
+
20
+ context 'when the operands are base relations' do
21
+ let(:operand) { base_relation }
22
+
23
+ it_should_behave_like 'a generated SQL SELECT query'
24
+
25
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")') }
26
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users"))') }
27
+ end
28
+
29
+ context 'when the operands are projections' do
30
+ let(:operand) { base_relation.project([ :id, :name ]) }
31
+
32
+ it_should_behave_like 'a generated SQL SELECT query'
33
+
34
+ its(:to_s) { should eql('(SELECT DISTINCT "id", "name" FROM "users") UNION (SELECT DISTINCT "id", "name" FROM "users")') }
35
+ its(:to_subquery) { should eql('((SELECT DISTINCT "id", "name" FROM "users") UNION (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") UNION (SELECT "id", "name", "age", 1 AS "one" FROM "users")') }
44
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age", 1 AS "one" FROM "users") UNION (SELECT "id", "name", "age", 1 AS "one" FROM "users"))') }
45
+ end
46
+
47
+ context 'when the operands are renames' do
48
+ let(:operand) { base_relation.rename(:id => :user_id) }
49
+
50
+ it_should_behave_like 'a generated SQL SELECT query'
51
+
52
+ its(:to_s) { should eql('(SELECT "id" AS "user_id", "name", "age" FROM "users") UNION (SELECT "id" AS "user_id", "name", "age" FROM "users")') }
53
+ its(:to_subquery) { should eql('((SELECT "id" AS "user_id", "name", "age" FROM "users") UNION (SELECT "id" AS "user_id", "name", "age" FROM "users"))') }
54
+ end
55
+
56
+ context 'when the operands are restrictions' do
57
+ let(:operand) { base_relation.restrict { |r| r.id.eq(1) } }
58
+
59
+ it_should_behave_like 'a generated SQL SELECT query'
60
+
61
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) UNION (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1)') }
62
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) UNION (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1))') }
63
+ end
64
+
65
+ context 'when the operand is a summarization' do
66
+ context 'summarize per table dee' do
67
+ let(:summarize_per) { TABLE_DEE }
68
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
69
+
70
+ it_should_behave_like 'a generated SQL SELECT query'
71
+
72
+ its(:to_s) { should eql('(SELECT COUNT ("id") AS "count" FROM "users") UNION (SELECT COUNT ("id") AS "count" FROM "users")') }
73
+ its(:to_subquery) { should eql('((SELECT COUNT ("id") AS "count" FROM "users") UNION (SELECT COUNT ("id") AS "count" FROM "users"))') }
74
+ end
75
+
76
+ context 'summarize per table dum' do
77
+ let(:summarize_per) { TABLE_DUM }
78
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
79
+
80
+ it_should_behave_like 'a generated SQL SELECT query'
81
+
82
+ its(:to_s) { should eql('(SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) UNION (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE)') }
83
+ its(:to_subquery) { should eql('((SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) UNION (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE))') }
84
+ end
85
+
86
+ context 'summarize by a subset of the operand header' do
87
+ let(:operand) { base_relation.summarize([ :id, :name ]) { |r| r.add(:count, r.age.count) } }
88
+
89
+ it_should_behave_like 'a generated SQL SELECT query'
90
+
91
+ its(:to_s) { should eql('(SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) UNION (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0)') }
92
+ its(:to_subquery) { should eql('((SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) UNION (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0))') }
93
+ end
94
+ end
95
+
96
+ context 'when the operand is ordered' do
97
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] } }
98
+
99
+ it_should_behave_like 'a generated SQL SELECT query'
100
+
101
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age") UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age")') }
102
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age") UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age"))') }
103
+ end
104
+
105
+ context 'when the operand is reversed' do
106
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.reverse }
107
+
108
+ it_should_behave_like 'a generated SQL SELECT query'
109
+
110
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC)') }
111
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC))') }
112
+ end
113
+
114
+ context 'when the operand is limited' do
115
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.take(1) }
116
+
117
+ it_should_behave_like 'a generated SQL SELECT query'
118
+
119
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1) UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1)') }
120
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1) UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1))') }
121
+ end
122
+
123
+ context 'when the operands are offsets' do
124
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.drop(1) }
125
+
126
+ it_should_behave_like 'a generated SQL SELECT query'
127
+
128
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1) UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1)') }
129
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1) UNION (SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1))') }
130
+ end
131
+
132
+ context 'when the operands are differences' do
133
+ let(:operand) { base_relation.difference(base_relation) }
134
+
135
+ it_should_behave_like 'a generated SQL SELECT query'
136
+
137
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) UNION ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users"))') }
138
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) UNION ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")))') }
139
+ end
140
+
141
+ context 'when the operands are intersections' do
142
+ let(:operand) { base_relation.intersect(base_relation) }
143
+
144
+ it_should_behave_like 'a generated SQL SELECT query'
145
+
146
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) UNION ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users"))') }
147
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) UNION ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")))') }
148
+ end
149
+
150
+ context 'when the operands are unions' do
151
+ let(:operand) { base_relation.union(base_relation) }
152
+
153
+ it_should_behave_like 'a generated SQL SELECT query'
154
+
155
+ its(:to_s) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) UNION ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users"))') }
156
+ its(:to_subquery) { should eql('(((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) UNION ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")))') }
157
+ end
158
+
159
+ context 'when the operands are joins' do
160
+ let(:operand) { base_relation.join(base_relation) }
161
+
162
+ it_should_behave_like 'a generated SQL SELECT query'
163
+
164
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right") UNION (SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right")') }
165
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right") UNION (SELECT "id", "name", "age" FROM "users" AS "left" NATURAL JOIN "users" AS "right"))') }
166
+ end
167
+
168
+ context 'when the operands have different base relations' do
169
+ let(:relation_name) { 'users_others' }
170
+ let(:left) { Relation::Base.new('users', header, body) }
171
+ let(:right) { Relation::Base.new('others', header, body) }
172
+
173
+ it_should_behave_like 'a generated SQL SELECT query'
174
+
175
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "others")') }
176
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "others"))') }
177
+ end
178
+
179
+ context 'when the operands have headers sorted in different orders' do
180
+ let(:left) { Relation::Base.new(relation_name, header, body) }
181
+ let(:right) { Relation::Base.new(relation_name, header.reverse, body) }
182
+
183
+ it_should_behave_like 'a generated SQL SELECT query'
184
+
185
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") UNION (SELECT DISTINCT "id", "name", "age" FROM "users")') }
186
+ its(:to_subquery) { should eql('((SELECT "id", "name", "age" FROM "users") UNION (SELECT DISTINCT "id", "name", "age" FROM "users"))') }
187
+ end
188
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation, '#to_s' do
6
+ subject { object.to_s }
7
+
8
+ let(:described_class) { Class.new(SQL::Generator::Relation) }
9
+ let(:id) { Attribute::Integer.new(:id) }
10
+ let(:name) { Attribute::String.new(:name) }
11
+ let(:age) { Attribute::Integer.new(:age, :required => false) }
12
+ let(:header) { [ id, name, age ] }
13
+ let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
14
+ let(:base_relation) { Relation::Base.new('users', header, body) }
15
+ let(:object) { described_class.new }
16
+
17
+ context 'when no object visited' do
18
+ it_should_behave_like 'an idempotent method'
19
+
20
+ it { should respond_to(:to_s) }
21
+
22
+ it { should be_frozen }
23
+
24
+ its(:to_s) { should == '' }
25
+ end
26
+
27
+ context 'when an object is visited' do
28
+ let(:visitable) { mock('Visitable') }
29
+
30
+ before do
31
+ described_class.class_eval do
32
+ def visit_rspec_mocks_mock(mock)
33
+ @name = mock.instance_variable_get(:@name)
34
+ @distinct = 'DISTINCT'
35
+ @scope = Set.new
36
+ end
37
+
38
+ def generate_sql(columns)
39
+ "SELECT #{columns} FROM #{@name}"
40
+ end
41
+ end
42
+
43
+ object.visit(visitable)
44
+ end
45
+
46
+ it_should_behave_like 'a generated SQL expression'
47
+
48
+ its(:to_s) { should eql('SELECT DISTINCT FROM Visitable') }
49
+ end
50
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation, '#to_sql' do
6
+ subject { object.to_sql }
7
+
8
+ let(:described_class) { Class.new(SQL::Generator::Relation) }
9
+ let(:id) { Attribute::Integer.new(:id) }
10
+ let(:name) { Attribute::String.new(:name) }
11
+ let(:age) { Attribute::Integer.new(:age, :required => false) }
12
+ let(:header) { [ id, name, age ] }
13
+ let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
14
+ let(:object) { described_class.new }
15
+
16
+ context 'when no object visited' do
17
+ it_should_behave_like 'an idempotent method'
18
+
19
+ it { should be_kind_of(String) }
20
+
21
+ it { should be_frozen }
22
+
23
+ it { should == '' }
24
+ end
25
+
26
+ context 'when an object is visited' do
27
+ let(:visitable) { mock('Visitable') }
28
+
29
+ before do
30
+ described_class.class_eval do
31
+ def visit_rspec_mocks_mock(mock)
32
+ mock
33
+ end
34
+ end
35
+ end
36
+
37
+ before do
38
+ @original = object.to_sql
39
+ object.visit(visitable)
40
+ end
41
+
42
+ it_should_behave_like 'an idempotent method'
43
+
44
+ it { should be_kind_of(String) }
45
+
46
+ it { should be_frozen }
47
+
48
+ it { should_not equal(@original) }
49
+
50
+ it { should eql(visitable.to_s) }
51
+ end
52
+ end