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,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation, '#to_subquery' do
6
+ subject { object.to_subquery }
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
+ @scope = Set.new
35
+ end
36
+
37
+ def generate_sql(columns)
38
+ "SELECT #{columns} FROM #{@name}"
39
+ end
40
+ end
41
+
42
+ object.visit(visitable)
43
+ end
44
+
45
+ it_should_behave_like 'a generated SQL expression'
46
+
47
+ its(:to_s) { should eql('(SELECT * FROM Visitable)') }
48
+ end
49
+ 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) { Relation::Base.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.sort_by { |r| [ r.id, r.name, r.age ] }.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.sort_by { |r| [ r.id, r.name, r.age ] }.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) { Relation::Base.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.sort_by { |r| [ r.id, r.name, r.age ] }.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.sort_by { |r| [ r.id, r.name, r.age ] }.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,165 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Unary, '#visit_axiom_algebra_extension' do
6
+ subject { object.visit_axiom_algebra_extension(extension) }
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(:extension) { operand.extend { |r| r.add(:one, 1) } }
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 "id", "name", "age", 1 AS "one" FROM "users"') }
24
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" 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", 1 AS "one" FROM "users"') }
33
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name", 1 AS "one" FROM "users")') }
34
+ end
35
+
36
+ context 'when the operand is an extension' do
37
+ let(:operand) { base_relation.extend { |r| r.add(:two, 2) } }
38
+
39
+ it_should_behave_like 'a generated SQL SELECT query'
40
+
41
+ its(:to_s) { should eql('SELECT "id", "name", "age", "two", 1 AS "one" FROM (SELECT *, 2 AS "two" FROM "users") AS "users"') }
42
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM (SELECT *, 2 AS "two" FROM "users") AS "users")') }
43
+ end
44
+
45
+ context 'when the operand is a rename' do
46
+ let(:operand) { base_relation.rename(:id => :user_id) }
47
+
48
+ it_should_behave_like 'a generated SQL SELECT query'
49
+
50
+ its(:to_s) { should eql('SELECT "user_id", "name", "age", 1 AS "one" FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "users"') }
51
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "users")') }
52
+ end
53
+
54
+ context 'when the operand is a restriction' do
55
+ let(:operand) { base_relation.restrict { |r| r.id.eq(1) } }
56
+
57
+ it_should_behave_like 'a generated SQL SELECT query'
58
+
59
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM "users" WHERE "id" = 1') }
60
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM "users" WHERE "id" = 1)') }
61
+ end
62
+
63
+ context 'when the operand is a summarization' do
64
+ context 'summarize per table dee' do
65
+ let(:summarize_per) { TABLE_DEE }
66
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
67
+
68
+ it_should_behave_like 'a generated SQL SELECT query'
69
+
70
+ its(:to_s) { should eql('SELECT "count", 1 AS "one" FROM (SELECT COUNT ("id") AS "count" FROM "users") AS "users"') }
71
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM (SELECT COUNT ("id") AS "count" FROM "users") AS "users")') }
72
+ end
73
+
74
+ context 'summarize per table dum' do
75
+ let(:summarize_per) { TABLE_DUM }
76
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
77
+
78
+ it_should_behave_like 'a generated SQL SELECT query'
79
+
80
+ its(:to_s) { should eql('SELECT "count", 1 AS "one" FROM (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) AS "users"') }
81
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) AS "users")') }
82
+ end
83
+
84
+ context 'summarize by a subset of the operand header' do
85
+ let(:operand) { base_relation.summarize([ :id, :name ]) { |r| r.add(:count, r.age.count) } }
86
+
87
+ it_should_behave_like 'a generated SQL SELECT query'
88
+
89
+ its(:to_s) { should eql('SELECT "id", "name", "count", 1 AS "one" FROM (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) AS "users"') }
90
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) AS "users")') }
91
+ end
92
+ end
93
+
94
+ context 'when the operand is ordered' do
95
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] } }
96
+
97
+ it_should_behave_like 'a generated SQL SELECT query'
98
+
99
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM "users" ORDER BY "id", "name", "age"') }
100
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM "users" ORDER BY "id", "name", "age")') }
101
+ end
102
+
103
+ context 'when the operand is reversed' do
104
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.reverse }
105
+
106
+ it_should_behave_like 'a generated SQL SELECT query'
107
+
108
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC') }
109
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC)') }
110
+ end
111
+
112
+ context 'when the operand is limited' do
113
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.take(1) }
114
+
115
+ it_should_behave_like 'a generated SQL SELECT query'
116
+
117
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM "users" ORDER BY "id", "name", "age" LIMIT 1') }
118
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM "users" ORDER BY "id", "name", "age" LIMIT 1)') }
119
+ end
120
+
121
+ context 'when the operand is an offset' do
122
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.drop(1) }
123
+
124
+ it_should_behave_like 'a generated SQL SELECT query'
125
+
126
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM "users" ORDER BY "id", "name", "age" OFFSET 1') }
127
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM "users" ORDER BY "id", "name", "age" OFFSET 1)') }
128
+ end
129
+
130
+ context 'when the operand is a difference' do
131
+ let(:operand) { base_relation.difference(base_relation) }
132
+
133
+ it_should_behave_like 'a generated SQL SELECT query'
134
+
135
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) AS "users"') }
136
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) AS "users")') }
137
+ end
138
+
139
+ context 'when the operand is an intersection' do
140
+ let(:operand) { base_relation.intersect(base_relation) }
141
+
142
+ it_should_behave_like 'a generated SQL SELECT query'
143
+
144
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) AS "users"') }
145
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) AS "users")') }
146
+ end
147
+
148
+ context 'when the operand is a union' do
149
+ let(:operand) { base_relation.union(base_relation) }
150
+
151
+ it_should_behave_like 'a generated SQL SELECT query'
152
+
153
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) AS "users"') }
154
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) AS "users")') }
155
+ end
156
+
157
+ context 'when the operand is a join' do
158
+ let(:operand) { base_relation.join(base_relation) }
159
+
160
+ it_should_behave_like 'a generated SQL SELECT query'
161
+
162
+ its(:to_s) { should eql('SELECT "id", "name", "age", 1 AS "one" FROM (SELECT * FROM "users" AS "left" NATURAL JOIN "users" AS "right") AS "users"') }
163
+ its(:to_subquery) { should eql('(SELECT *, 1 AS "one" FROM (SELECT * FROM "users" AS "left" NATURAL JOIN "users" AS "right") AS "users")') }
164
+ end
165
+ end
@@ -0,0 +1,193 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SQL::Generator::Relation::Unary, '#visit_axiom_algebra_projection' do
6
+ subject { object.visit_axiom_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) { Relation::Base.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 an extension' do
37
+ let(:operand) { base_relation.extend { |r| r.add(:one, 1) } }
38
+
39
+ context 'when the projection includes the extended column' do
40
+ let(:projection) { operand.project([ :id, :name, :one ]) }
41
+
42
+ it_should_behave_like 'a generated SQL SELECT query'
43
+
44
+ its(:to_s) { pending { should eql('SELECT DISTINCT "id", "name", 1 AS "one" FROM "users"') } }
45
+ its(:to_subquery) { pending { should eql('SELECT DISTINCT "id", "name", 1 AS "one" FROM "users"') } }
46
+ end
47
+
48
+ context 'when the projection does not include the extended column' do
49
+ let(:projection) { operand.project([ :id, :name ]) }
50
+
51
+ it_should_behave_like 'a generated SQL SELECT query'
52
+
53
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT *, 1 AS "one" FROM "users") AS "users"') }
54
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM (SELECT *, 1 AS "one" FROM "users") AS "users")') }
55
+ end
56
+ end
57
+
58
+ context 'when the operand is a rename' do
59
+ let(:operand) { base_relation.rename(:id => :user_id) }
60
+
61
+ context 'when the projection includes the renamed column' do
62
+ let(:projection) { operand.project([ :user_id, :name ]) }
63
+
64
+ it_should_behave_like 'a generated SQL SELECT query'
65
+
66
+ its(:to_s) { pending { should eql('SELECT DISTINCT "id" AS "user_id", "name" FROM "users"') } }
67
+ its(:to_subquery) { pending { should eql('SELECT DISTINCT "id" AS "user_id", "name" FROM "users"') } }
68
+ end
69
+
70
+ context 'when the projection does not include the renamed column' do
71
+ let(:projection) { operand.project([ :name, :age ]) }
72
+
73
+ it_should_behave_like 'a generated SQL SELECT query'
74
+
75
+ its(:to_s) { should eql('SELECT DISTINCT "name", "age" FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "users"') }
76
+ its(:to_subquery) { should eql('(SELECT DISTINCT "name", "age" FROM (SELECT "id" AS "user_id", "name", "age" FROM "users") AS "users")') }
77
+ end
78
+ end
79
+
80
+ context 'when the operand is a restriction' do
81
+ let(:operand) { base_relation.restrict { |r| r.id.eq(1) } }
82
+
83
+ it_should_behave_like 'a generated SQL SELECT query'
84
+
85
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM "users" WHERE "id" = 1') }
86
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM "users" WHERE "id" = 1)') }
87
+ end
88
+
89
+ context 'when the operand is a summarization' do
90
+ context 'summarize per table dee' do
91
+ let(:summarize_per) { TABLE_DEE }
92
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
93
+ let(:projection) { operand.project([ :count ]) }
94
+
95
+ it_should_behave_like 'a generated SQL SELECT query'
96
+
97
+ its(:to_s) { should eql('SELECT DISTINCT "count" FROM (SELECT COUNT ("id") AS "count" FROM "users") AS "users"') }
98
+ its(:to_subquery) { should eql('(SELECT DISTINCT "count" FROM (SELECT COUNT ("id") AS "count" FROM "users") AS "users")') }
99
+ end
100
+
101
+ context 'summarize per table dum' do
102
+ let(:summarize_per) { TABLE_DUM }
103
+ let(:operand) { base_relation.summarize(summarize_per) { |r| r.add(:count, r.id.count) } }
104
+ let(:projection) { operand.project([ :count ]) }
105
+
106
+ it_should_behave_like 'a generated SQL SELECT query'
107
+
108
+ its(:to_s) { should eql('SELECT DISTINCT "count" FROM (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) AS "users"') }
109
+ its(:to_subquery) { should eql('(SELECT DISTINCT "count" FROM (SELECT COUNT ("id") AS "count" FROM "users" HAVING FALSE) AS "users")') }
110
+ end
111
+
112
+ context 'summarize by a subset of the operand header' do
113
+ let(:operand) { base_relation.summarize([ :id, :name ]) { |r| r.add(:count, r.age.count) } }
114
+
115
+ it_should_behave_like 'a generated SQL SELECT query'
116
+
117
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) AS "users"') }
118
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM (SELECT "id", "name", COUNT ("age") AS "count" FROM "users" GROUP BY "id", "name" HAVING COUNT (*) > 0) AS "users")') }
119
+ end
120
+ end
121
+
122
+ context 'when the operand is ordered' do
123
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] } }
124
+
125
+ it_should_behave_like 'a generated SQL SELECT query'
126
+
127
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age") AS "users"') }
128
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age") AS "users")') }
129
+ end
130
+
131
+ context 'when the operand is reversed' do
132
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.reverse }
133
+
134
+ it_should_behave_like 'a generated SQL SELECT query'
135
+
136
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) AS "users"') }
137
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id" DESC, "name" DESC, "age" DESC) AS "users")') }
138
+ end
139
+
140
+ context 'when the operand is limited' do
141
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.take(1) }
142
+
143
+ it_should_behave_like 'a generated SQL SELECT query'
144
+
145
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) AS "users"') }
146
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" LIMIT 1) AS "users")') }
147
+ end
148
+
149
+ context 'when the operand is an offset' do
150
+ let(:operand) { base_relation.sort_by { |r| [ r.id, r.name, r.age ] }.drop(1) }
151
+
152
+ it_should_behave_like 'a generated SQL SELECT query'
153
+
154
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) AS "users"') }
155
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" ORDER BY "id", "name", "age" OFFSET 1) AS "users")') }
156
+ end
157
+
158
+ context 'when the operand is a difference' do
159
+ let(:operand) { base_relation.difference(base_relation) }
160
+
161
+ it_should_behave_like 'a generated SQL SELECT query'
162
+
163
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) AS "users"') }
164
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM ((SELECT "id", "name", "age" FROM "users") EXCEPT (SELECT "id", "name", "age" FROM "users")) AS "users")') }
165
+ end
166
+
167
+ context 'when the operand is an intersection' do
168
+ let(:operand) { base_relation.intersect(base_relation) }
169
+
170
+ it_should_behave_like 'a generated SQL SELECT query'
171
+
172
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) AS "users"') }
173
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM ((SELECT "id", "name", "age" FROM "users") INTERSECT (SELECT "id", "name", "age" FROM "users")) AS "users")') }
174
+ end
175
+
176
+ context 'when the operand is a union' do
177
+ let(:operand) { base_relation.union(base_relation) }
178
+
179
+ it_should_behave_like 'a generated SQL SELECT query'
180
+
181
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) AS "users"') }
182
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM ((SELECT "id", "name", "age" FROM "users") UNION (SELECT "id", "name", "age" FROM "users")) AS "users")') }
183
+ end
184
+
185
+ context 'when the operand is a join' do
186
+ let(:operand) { base_relation.join(base_relation) }
187
+
188
+ it_should_behave_like 'a generated SQL SELECT query'
189
+
190
+ its(:to_s) { should eql('SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" AS "left" NATURAL JOIN "users" AS "right") AS "users"') }
191
+ its(:to_subquery) { should eql('(SELECT DISTINCT "id", "name" FROM (SELECT * FROM "users" AS "left" NATURAL JOIN "users" AS "right") AS "users")') }
192
+ end
193
+ end