veritas-sql-generator 0.0.3

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 (103) hide show
  1. data/Gemfile +33 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +27 -0
  4. data/Rakefile +25 -0
  5. data/TODO +17 -0
  6. data/config/flay.yml +3 -0
  7. data/config/flog.yml +2 -0
  8. data/config/roodi.yml +16 -0
  9. data/config/site.reek +124 -0
  10. data/config/yardstick.yml +2 -0
  11. data/lib/veritas-sql-generator.rb +3 -0
  12. data/lib/veritas/base_relation.rb +36 -0
  13. data/lib/veritas/sql/generator.rb +35 -0
  14. data/lib/veritas/sql/generator/attribute.rb +25 -0
  15. data/lib/veritas/sql/generator/direction.rb +36 -0
  16. data/lib/veritas/sql/generator/identifier.rb +27 -0
  17. data/lib/veritas/sql/generator/literal.rb +160 -0
  18. data/lib/veritas/sql/generator/logic.rb +349 -0
  19. data/lib/veritas/sql/generator/relation.rb +111 -0
  20. data/lib/veritas/sql/generator/relation/base.rb +14 -0
  21. data/lib/veritas/sql/generator/relation/binary.rb +184 -0
  22. data/lib/veritas/sql/generator/relation/set.rb +99 -0
  23. data/lib/veritas/sql/generator/relation/unary.rb +326 -0
  24. data/lib/veritas/sql/generator/version.rb +9 -0
  25. data/lib/veritas/sql/generator/visitor.rb +121 -0
  26. data/spec/rcov.opts +6 -0
  27. data/spec/shared/command_method_behavior.rb +7 -0
  28. data/spec/shared/generated_sql_behavior.rb +15 -0
  29. data/spec/shared/idempotent_method_behavior.rb +7 -0
  30. data/spec/spec.opts +3 -0
  31. data/spec/spec_helper.rb +15 -0
  32. data/spec/unit/veritas/base_relation/name_spec.rb +45 -0
  33. data/spec/unit/veritas/sql/generator/attribute/visit_veritas_attribute_spec.rb +15 -0
  34. data/spec/unit/veritas/sql/generator/direction/visit_veritas_relation_operation_order_ascending_spec.rb +15 -0
  35. data/spec/unit/veritas/sql/generator/direction/visit_veritas_relation_operation_order_descending_spec.rb +15 -0
  36. data/spec/unit/veritas/sql/generator/identifier/visit_identifier_spec.rb +26 -0
  37. data/spec/unit/veritas/sql/generator/literal/class_methods/dup_frozen_spec.rb +23 -0
  38. data/spec/unit/veritas/sql/generator/literal/visit_class_spec.rb +31 -0
  39. data/spec/unit/veritas/sql/generator/literal/visit_date_spec.rb +15 -0
  40. data/spec/unit/veritas/sql/generator/literal/visit_date_time_spec.rb +61 -0
  41. data/spec/unit/veritas/sql/generator/literal/visit_enumerable_spec.rb +15 -0
  42. data/spec/unit/veritas/sql/generator/literal/visit_false_class_spec.rb +14 -0
  43. data/spec/unit/veritas/sql/generator/literal/visit_nil_class_spec.rb +14 -0
  44. data/spec/unit/veritas/sql/generator/literal/visit_numeric_spec.rb +34 -0
  45. data/spec/unit/veritas/sql/generator/literal/visit_string_spec.rb +26 -0
  46. data/spec/unit/veritas/sql/generator/literal/visit_time_spec.rb +97 -0
  47. data/spec/unit/veritas/sql/generator/literal/visit_true_class_spec.rb +14 -0
  48. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_connective_conjunction_spec.rb +16 -0
  49. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_connective_disjunction_spec.rb +16 -0
  50. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_connective_negation_spec.rb +16 -0
  51. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_equality_spec.rb +27 -0
  52. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_exclusion_spec.rb +43 -0
  53. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_greater_than_or_equal_to_spec.rb +15 -0
  54. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_greater_than_spec.rb +15 -0
  55. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_inclusion_spec.rb +43 -0
  56. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_inequality_spec.rb +55 -0
  57. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_less_than_or_equal_to_spec.rb +15 -0
  58. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_predicate_less_than_spec.rb +15 -0
  59. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_proposition_contradiction_spec.rb +15 -0
  60. data/spec/unit/veritas/sql/generator/logic/visit_veritas_logic_proposition_tautology_spec.rb +15 -0
  61. data/spec/unit/veritas/sql/generator/relation/binary/base/to_subquery_spec.rb +35 -0
  62. data/spec/unit/veritas/sql/generator/relation/binary/base/visit_veritas_base_relation_spec.rb +22 -0
  63. data/spec/unit/veritas/sql/generator/relation/binary/class_methods/subquery_spec.rb +42 -0
  64. data/spec/unit/veritas/sql/generator/relation/binary/to_s_spec.rb +35 -0
  65. data/spec/unit/veritas/sql/generator/relation/binary/to_subquery_spec.rb +35 -0
  66. data/spec/unit/veritas/sql/generator/relation/binary/visit_veritas_algebra_join_spec.rb +138 -0
  67. data/spec/unit/veritas/sql/generator/relation/binary/visit_veritas_algebra_product_spec.rb +139 -0
  68. data/spec/unit/veritas/sql/generator/relation/class_methods/subquery_spec.rb +33 -0
  69. data/spec/unit/veritas/sql/generator/relation/class_methods/visit_spec.rb +61 -0
  70. data/spec/unit/veritas/sql/generator/relation/name_spec.rb +30 -0
  71. data/spec/unit/veritas/sql/generator/relation/set/to_s_spec.rb +55 -0
  72. data/spec/unit/veritas/sql/generator/relation/set/to_subquery_spec.rb +55 -0
  73. data/spec/unit/veritas/sql/generator/relation/set/visit_veritas_algebra_difference_spec.rb +138 -0
  74. data/spec/unit/veritas/sql/generator/relation/set/visit_veritas_algebra_intersection_spec.rb +138 -0
  75. data/spec/unit/veritas/sql/generator/relation/set/visit_veritas_algebra_union_spec.rb +138 -0
  76. data/spec/unit/veritas/sql/generator/relation/to_sql_spec.rb +52 -0
  77. data/spec/unit/veritas/sql/generator/relation/unary/to_s_spec.rb +55 -0
  78. data/spec/unit/veritas/sql/generator/relation/unary/to_subquery_spec.rb +75 -0
  79. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_projection_spec.rb +138 -0
  80. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_rename_spec.rb +136 -0
  81. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_algebra_restriction_spec.rb +157 -0
  82. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_base_relation_spec.rb +21 -0
  83. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_limit_spec.rb +125 -0
  84. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_offset_spec.rb +125 -0
  85. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_order_spec.rb +136 -0
  86. data/spec/unit/veritas/sql/generator/relation/unary/visit_veritas_relation_operation_reverse_spec.rb +125 -0
  87. data/spec/unit/veritas/sql/generator/relation/visit_spec.rb +54 -0
  88. data/spec/unit/veritas/sql/generator/relation/visited_spec.rb +35 -0
  89. data/spec/unit/veritas/sql/generator/visitor/class_methods/handler_for_spec.rb +71 -0
  90. data/spec/unit/veritas/sql/generator/visitor/visit_spec.rb +12 -0
  91. data/spec/unit/veritas/sql/generator/visitor/visited_spec.rb +11 -0
  92. data/tasks/quality/ci.rake +2 -0
  93. data/tasks/quality/flay.rake +41 -0
  94. data/tasks/quality/flog.rake +45 -0
  95. data/tasks/quality/heckle.rake +203 -0
  96. data/tasks/quality/metric_fu.rake +26 -0
  97. data/tasks/quality/reek.rake +9 -0
  98. data/tasks/quality/roodi.rake +15 -0
  99. data/tasks/quality/yardstick.rake +23 -0
  100. data/tasks/spec.rake +38 -0
  101. data/tasks/yard.rake +9 -0
  102. data/veritas-sql-generator.gemspec +222 -0
  103. metadata +285 -0
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Set, '#visit_veritas_algebra_union' do
6
+ subject { object.visit_veritas_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) { BaseRelation.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 * FROM "users") UNION (SELECT * 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 operands are renames' do
39
+ let(:operand) { base_relation.rename(:id => :user_id) }
40
+
41
+ it_should_behave_like 'a generated SQL SELECT query'
42
+
43
+ its(:to_s) { should eql('(SELECT "id" AS "user_id", "name", "age" FROM "users") UNION (SELECT "id" AS "user_id", "name", "age" FROM "users")') }
44
+ its(:to_subquery) { should eql('(SELECT "id" AS "user_id", "name", "age" FROM "users") UNION (SELECT "id" AS "user_id", "name", "age" FROM "users")') }
45
+ end
46
+
47
+ context 'when the operands are restrictions' do
48
+ let(:operand) { base_relation.restrict { |r| r[:id].eq(1) } }
49
+
50
+ it_should_behave_like 'a generated SQL SELECT query'
51
+
52
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" WHERE "id" = 1) UNION (SELECT "id", "name", "age" FROM "users" WHERE "id" = 1)') }
53
+ its(:to_subquery) { should eql('(SELECT * FROM "users" WHERE "id" = 1) UNION (SELECT * FROM "users" WHERE "id" = 1)') }
54
+ end
55
+
56
+ context 'when the operand is ordered' do
57
+ let(:operand) { base_relation.order }
58
+
59
+ it_should_behave_like 'a generated SQL SELECT query'
60
+
61
+ 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")') }
62
+ its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id", "name", "age") UNION (SELECT * FROM "users" ORDER BY "id", "name", "age")') }
63
+ end
64
+
65
+ context 'when the operand is reversed' do
66
+ let(:operand) { base_relation.order.reverse }
67
+
68
+ it_should_behave_like 'a generated SQL SELECT query'
69
+
70
+ 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)') }
71
+ its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) UNION (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC)') }
72
+ end
73
+
74
+ context 'when the operand is limited' do
75
+ let(:operand) { base_relation.order.take(1) }
76
+
77
+ it_should_behave_like 'a generated SQL SELECT query'
78
+
79
+ 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)') }
80
+ its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) UNION (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1)') }
81
+ end
82
+
83
+ context 'when the operands are offsets' do
84
+ let(:operand) { base_relation.order.drop(1) }
85
+
86
+ it_should_behave_like 'a generated SQL SELECT query'
87
+
88
+ 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)') }
89
+ its(:to_subquery) { should eql('(SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) UNION (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1)') }
90
+ end
91
+
92
+ context 'when the operands are differences' do
93
+ let(:operand) { base_relation.difference(base_relation) }
94
+
95
+ it_should_behave_like 'a generated SQL SELECT query'
96
+
97
+ 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"))') }
98
+ its(:to_subquery) { should eql('((SELECT * FROM "users") EXCEPT (SELECT * FROM "users")) UNION ((SELECT * FROM "users") EXCEPT (SELECT * FROM "users"))') }
99
+ end
100
+
101
+ context 'when the operands are intersections' do
102
+ let(:operand) { base_relation.intersect(base_relation) }
103
+
104
+ it_should_behave_like 'a generated SQL SELECT query'
105
+
106
+ 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"))') }
107
+ its(:to_subquery) { should eql('((SELECT * FROM "users") INTERSECT (SELECT * FROM "users")) UNION ((SELECT * FROM "users") INTERSECT (SELECT * FROM "users"))') }
108
+ end
109
+
110
+ context 'when the operands are unions' do
111
+ let(:operand) { base_relation.union(base_relation) }
112
+
113
+ it_should_behave_like 'a generated SQL SELECT query'
114
+
115
+ 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"))') }
116
+ its(:to_subquery) { should eql('((SELECT * FROM "users") UNION (SELECT * FROM "users")) UNION ((SELECT * FROM "users") UNION (SELECT * FROM "users"))') }
117
+ end
118
+
119
+ context 'when the operands are joins' do
120
+ let(:operand) { base_relation.join(base_relation) }
121
+
122
+ it_should_behave_like 'a generated SQL SELECT query'
123
+
124
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users" NATURAL JOIN "users") UNION (SELECT "id", "name", "age" FROM "users" NATURAL JOIN "users")') }
125
+ its(:to_subquery) { should eql('(SELECT * FROM "users" NATURAL JOIN "users") UNION (SELECT * FROM "users" NATURAL JOIN "users")') }
126
+ end
127
+
128
+ 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) }
132
+
133
+ it_should_behave_like 'a generated SQL SELECT query'
134
+
135
+ its(:to_s) { should eql('(SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "others")') }
136
+ its(:to_subquery) { should eql('(SELECT * FROM "users") UNION (SELECT * FROM "others")') }
137
+ end
138
+ 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_spec_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
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Unary, '#to_s' do
6
+ subject { object.to_s }
7
+
8
+ let(:id) { Attribute::Integer.new(:id) }
9
+ let(:name) { Attribute::String.new(:name) }
10
+ let(:age) { Attribute::Integer.new(:age, :required => false) }
11
+ let(:header) { [ id, name, age ] }
12
+ let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
13
+ let(:base_relation) { BaseRelation.new('users', header, body) }
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 respond_to(:to_s) }
20
+
21
+ it { should be_frozen }
22
+
23
+ its(:to_s) { should == '' }
24
+ end
25
+
26
+ context 'when a restriction is visited' do
27
+ before do
28
+ object.visit(base_relation.restrict { |r| r[:id].eq(1) })
29
+ end
30
+
31
+ it_should_behave_like 'a generated SQL expression'
32
+
33
+ its(:to_s) { should eql('SELECT "id", "name", "age" FROM "users" WHERE "id" = 1') }
34
+ end
35
+
36
+ context 'when a limit is visited' do
37
+ before do
38
+ object.visit(base_relation.order.take(1))
39
+ end
40
+
41
+ it_should_behave_like 'a generated SQL expression'
42
+
43
+ its(:to_s) { should eql('SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" LIMIT 1') }
44
+ end
45
+
46
+ context 'when an offset is visited' do
47
+ before do
48
+ object.visit(base_relation.order.drop(1))
49
+ end
50
+
51
+ it_should_behave_like 'a generated SQL expression'
52
+
53
+ its(:to_s) { should eql('SELECT "id", "name", "age" FROM "users" ORDER BY "id", "name", "age" OFFSET 1') }
54
+ end
55
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Unary, '#to_subquery' do
6
+ subject { object.to_subquery }
7
+
8
+ let(:id) { Attribute::Integer.new(:id) }
9
+ let(:name) { Attribute::String.new(:name) }
10
+ let(:age) { Attribute::Integer.new(:age, :required => false) }
11
+ let(:header) { [ id, name, age ] }
12
+ let(:body) { [ [ 1, 'Dan Kubb', 35 ] ].each }
13
+ let(:base_relation) { BaseRelation.new('users', header, body) }
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 respond_to(:to_s) }
20
+
21
+ it { should be_frozen }
22
+
23
+ its(:to_s) { should == '' }
24
+ end
25
+
26
+ context 'when a projection is visited' do
27
+ before do
28
+ object.visit(base_relation.project([ :id, :name ]))
29
+ end
30
+
31
+ it_should_behave_like 'a generated SQL expression'
32
+
33
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM "users"') }
34
+ end
35
+
36
+ context 'when a rename is visited' do
37
+ before do
38
+ object.visit(base_relation.rename(:id => :user_id))
39
+ end
40
+
41
+ it_should_behave_like 'a generated SQL expression'
42
+
43
+ its(:to_s) { should eql('SELECT "id" AS "user_id", "name", "age" FROM "users"') }
44
+ end
45
+
46
+ context 'when a restriction is visited' do
47
+ before do
48
+ object.visit(base_relation.restrict { |r| r[:id].eq(1) })
49
+ end
50
+
51
+ it_should_behave_like 'a generated SQL expression'
52
+
53
+ its(:to_s) { should eql('SELECT * FROM "users" WHERE "id" = 1') }
54
+ end
55
+
56
+ context 'when a limit is visited' do
57
+ before do
58
+ object.visit(base_relation.order.take(1))
59
+ end
60
+
61
+ it_should_behave_like 'a generated SQL expression'
62
+
63
+ its(:to_s) { should eql('SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1') }
64
+ end
65
+
66
+ context 'when an offset is visited' do
67
+ before do
68
+ object.visit(base_relation.order.drop(1))
69
+ end
70
+
71
+ it_should_behave_like 'a generated SQL expression'
72
+
73
+ its(:to_s) { should eql('SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1') }
74
+ end
75
+ end
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Unary, '#visit_veritas_algebra_projection' do
6
+ subject { object.visit_veritas_algebra_projection(projection) }
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) { BaseRelation.new(relation_name, header, body) }
15
+ let(:projection) { operand.project([ :id, :name ]) }
16
+ let(:object) { described_class.new }
17
+
18
+ context 'when the operand is a base relation' do
19
+ let(:operand) { base_relation }
20
+
21
+ it_should_behave_like 'a generated SQL SELECT query'
22
+
23
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM "users"') }
24
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM "users"') }
25
+ end
26
+
27
+ context 'when the operand is a projection' do
28
+ let(:operand) { base_relation.project([ :id, :name ]) }
29
+
30
+ it_should_behave_like 'a generated SQL SELECT query'
31
+
32
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM "users"') }
33
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM "users"') }
34
+ end
35
+
36
+ context 'when the operand is a rename' do
37
+ context 'when the projection includes the renamed column' do
38
+ let(:operand) { base_relation.rename(:id => :user_id) }
39
+ let(:projection) { operand.project([ :user_id, :name ]) }
40
+
41
+ it_should_behave_like 'a generated SQL SELECT query'
42
+
43
+ its(:to_s) { pending { should eql('SELECT DISTINCT "id" AS "user_id", "name" FROM "users"') } }
44
+ its(:to_subquery) { pending { should eql('SELECT DISTINCT "id" AS "user_id", "name" FROM "users"') } }
45
+ end
46
+
47
+ context 'when the projection does not include the renamed column' do
48
+ let(:operand) { base_relation.rename(:id => :user_id) }
49
+ let(:projection) { operand.project([ :name, :age ]) }
50
+
51
+ it_should_behave_like 'a generated SQL SELECT query'
52
+
53
+ its(:to_s) { should eql('SELECT DISTINCT "name", "age" FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "users"') }
54
+ its(:to_subquery) { should eql('SELECT DISTINCT "name", "age" FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "users"') }
55
+ end
56
+ end
57
+
58
+ context 'when the operand is a restriction' do
59
+ let(:operand) { base_relation.restrict { |r| r[:id].eq(1) } }
60
+
61
+ it_should_behave_like 'a generated SQL SELECT query'
62
+
63
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM "users" WHERE "id" = 1') }
64
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM "users" WHERE "id" = 1') }
65
+ end
66
+
67
+ context 'when the operand is ordered' do
68
+ let(:operand) { base_relation.order }
69
+
70
+ it_should_behave_like 'a generated SQL SELECT query'
71
+
72
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age") AS "users"') }
73
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age") AS "users"') }
74
+ end
75
+
76
+ context 'when the operand is reversed' do
77
+ let(:operand) { base_relation.order.reverse }
78
+
79
+ it_should_behave_like 'a generated SQL SELECT query'
80
+
81
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) AS "users"') }
82
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) AS "users"') }
83
+ end
84
+
85
+ context 'when the operand is limited' do
86
+ let(:operand) { base_relation.order.take(1) }
87
+
88
+ it_should_behave_like 'a generated SQL SELECT query'
89
+
90
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) AS "users"') }
91
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) AS "users"') }
92
+ end
93
+
94
+ context 'when the operand is an offset' do
95
+ let(:operand) { base_relation.order.drop(1) }
96
+
97
+ it_should_behave_like 'a generated SQL SELECT query'
98
+
99
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) AS "users"') }
100
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) AS "users"') }
101
+ end
102
+
103
+ context 'when the operand is a difference' do
104
+ let(:operand) { base_relation.difference(base_relation) }
105
+
106
+ it_should_behave_like 'a generated SQL SELECT query'
107
+
108
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT * FROM "users") EXCEPT (SELECT * FROM "users")) AS "users"') }
109
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT * FROM "users") EXCEPT (SELECT * FROM "users")) AS "users"') }
110
+ end
111
+
112
+ context 'when the operand is an intersection' do
113
+ let(:operand) { base_relation.intersect(base_relation) }
114
+
115
+ it_should_behave_like 'a generated SQL SELECT query'
116
+
117
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT * FROM "users") INTERSECT (SELECT * FROM "users")) AS "users"') }
118
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT * FROM "users") INTERSECT (SELECT * FROM "users")) AS "users"') }
119
+ end
120
+
121
+ context 'when the operand is a union' do
122
+ let(:operand) { base_relation.union(base_relation) }
123
+
124
+ it_should_behave_like 'a generated SQL SELECT query'
125
+
126
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT * FROM "users") UNION (SELECT * FROM "users")) AS "users"') }
127
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT * FROM "users") UNION (SELECT * FROM "users")) AS "users"') }
128
+ end
129
+
130
+ context 'when the operand is a join' do
131
+ let(:operand) { base_relation.join(base_relation) }
132
+
133
+ it_should_behave_like 'a generated SQL SELECT query'
134
+
135
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" NATURAL JOIN "users") AS "users"') }
136
+ its(:to_subquery) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" NATURAL JOIN "users") AS "users"') }
137
+ end
138
+ end