axiom-sql-generator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,62 @@
1
+ # encoding: utf-8
2
+
3
+ module Axiom
4
+ module SQL
5
+ module Generator
6
+ class Relation
7
+
8
+ # Generates an SQL statement for an insertion
9
+ class Insertion < Set
10
+ extend Aliasable
11
+
12
+ inheritable_alias(:to_subquery => :to_s)
13
+
14
+ # Visit an Insertion
15
+ #
16
+ # @param [Relation::Operation::Insertion] insertion
17
+ #
18
+ # @return [self]
19
+ #
20
+ # @api private
21
+ def visit_axiom_relation_operation_insertion(insertion)
22
+ @header = insertion.header
23
+ set_columns(insertion)
24
+ set_operands(insertion)
25
+ set_name
26
+ self
27
+ end
28
+
29
+ private
30
+
31
+ # Generate the SQL using the supplied method
32
+ #
33
+ # @return [#to_s]
34
+ #
35
+ # @api private
36
+ def generate_sql(*)
37
+ "INSERT INTO #{@name} #{column_list} #{@right}"
38
+ end
39
+
40
+ # Generate the list of columns to insert into
41
+ #
42
+ # @return [#to_s]
43
+ #
44
+ # @api private
45
+ def column_list
46
+ Generator.parenthesize!(column_list_for(@columns))
47
+ end
48
+
49
+ # Set the name using the left operands' name
50
+ #
51
+ # @return [undefined]
52
+ #
53
+ # @api private
54
+ def set_name
55
+ @name = @left.name
56
+ end
57
+
58
+ end # class Insertion
59
+ end # class Relation
60
+ end # module Generator
61
+ end # module SQL
62
+ end # module Axiom
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ module Axiom
4
+ module SQL
5
+ module Generator
6
+ class Relation
7
+
8
+ # Generates an SQL statement for materialized relation
9
+ class Materialized < Relation
10
+ include Literal
11
+
12
+ # Visit a Materialized relation
13
+ #
14
+ # @param [Relation::Materialized] materialized
15
+ #
16
+ # @return [self]
17
+ #
18
+ # @api private
19
+ def visit_axiom_relation_materialized(materialized)
20
+ @values = materialized.map do |tuple|
21
+ Generator.parenthesize!(
22
+ tuple.to_ary.map { |value| dispatch(value) }.join(', ')
23
+ )
24
+ end
25
+ self
26
+ end
27
+
28
+ # Test if a visitable object has been visited
29
+ #
30
+ # @example
31
+ # visitor.visited? # true or false
32
+ #
33
+ # @return [Boolean]
34
+ #
35
+ # @api public
36
+ def visited?
37
+ instance_variable_defined?(:@values)
38
+ end
39
+
40
+ private
41
+
42
+ # Generate the SQL for the materialized relation
43
+ #
44
+ # @return [#to_s]
45
+ #
46
+ # @api private
47
+ def generate_sql(*)
48
+ return EMPTY_STRING unless visited?
49
+ if @values.empty?
50
+ 'SELECT 0 LIMIT 0' # no values
51
+ else
52
+ "VALUES #{@values.join(', ')}"
53
+ end
54
+ end
55
+
56
+ end # class Materialized
57
+ end # class Relation
58
+ end # module Generator
59
+ end # module SQL
60
+ end # module Axiom
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+
3
+ module Axiom
4
+ module SQL
5
+ module Generator
6
+ class Relation
7
+
8
+ # Generates an SQL statement for a set relation
9
+ class Set < Binary
10
+
11
+ DIFFERENCE = 'EXCEPT'.freeze
12
+ INTERSECTION = 'INTERSECT'.freeze
13
+ UNION = 'UNION'.freeze
14
+
15
+ # Normalize the headers of the operands
16
+ #
17
+ # This is necessary to make sure the columns are in the correct
18
+ # order when generating SQL.
19
+ #
20
+ # @param [Relation::Operation::Set] relation
21
+ #
22
+ # @return [Relation::Operation::Set]
23
+ #
24
+ # @api private
25
+ def self.normalize_operand_headers(relation)
26
+ left = relation.left
27
+ right = relation.right
28
+ left_header = left.header
29
+ if left_header.to_a != right.header.to_a
30
+ relation.class.new(left, right.project(left_header))
31
+ else
32
+ relation
33
+ end
34
+ end
35
+
36
+ # Visit a Union
37
+ #
38
+ # @param [Algebra::Union] union
39
+ #
40
+ # @return [self]
41
+ #
42
+ # @api private
43
+ def visit_axiom_algebra_union(union)
44
+ set_operation(UNION)
45
+ set_operands(union)
46
+ set_name
47
+ self
48
+ end
49
+
50
+ # Visit an Intersection
51
+ #
52
+ # @param [Algebra::Intersection] intersection
53
+ #
54
+ # @return [self]
55
+ #
56
+ # @api private
57
+ def visit_axiom_algebra_intersection(intersection)
58
+ set_operation(INTERSECTION)
59
+ set_operands(intersection)
60
+ set_name
61
+ self
62
+ end
63
+
64
+ # Visit an Difference
65
+ #
66
+ # @param [Algebra::Difference] difference
67
+ #
68
+ # @return [self]
69
+ #
70
+ # @api private
71
+ def visit_axiom_algebra_difference(difference)
72
+ set_operation(DIFFERENCE)
73
+ set_operands(difference)
74
+ set_name
75
+ self
76
+ end
77
+
78
+ private
79
+
80
+ # Generate the SQL using the supplied method
81
+ #
82
+ # @return [#to_s]
83
+ #
84
+ # @api private
85
+ def generate_sql(*)
86
+ "(#{@left}) #{@operation} (#{@right})"
87
+ end
88
+
89
+ # Set the operands from the relation
90
+ #
91
+ # @param [Relation::Operation::Set] relation
92
+ #
93
+ # @return [undefined]
94
+ #
95
+ # @api private
96
+ def set_operands(relation)
97
+ super self.class.normalize_operand_headers(relation)
98
+ end
99
+
100
+ # Generates an SQL statement for base relation set operands
101
+ class Base < Relation::Base; end
102
+
103
+ end # class Set
104
+ end # class Relation
105
+ end # module Generator
106
+ end # module SQL
107
+ end # module Axiom
@@ -0,0 +1,379 @@
1
+ # encoding: utf-8
2
+
3
+ module Axiom
4
+ module SQL
5
+ module Generator
6
+ class Relation
7
+
8
+ # Generates an SQL statement for a unary relation
9
+ class Unary < Relation
10
+ extend Aliasable
11
+ include Direction,
12
+ Literal,
13
+ Function::Aggregate,
14
+ Function::Connective,
15
+ Function::Predicate,
16
+ Function::Proposition,
17
+ Function::String,
18
+ Function::Numeric
19
+
20
+ inheritable_alias(:visit_axiom_relation_operation_reverse => :visit_axiom_relation_operation_order)
21
+
22
+ DISTINCT = 'DISTINCT '.freeze
23
+ NO_ROWS = ' HAVING FALSE'.freeze
24
+ ANY_ROWS = ' HAVING COUNT (*) > 0'
25
+ COLLAPSIBLE = {
26
+ Algebra::Summarization => Set[ ].freeze,
27
+ Algebra::Projection => Set[ Algebra::Projection, Algebra::Restriction, ].freeze,
28
+ Algebra::Extension => Set[ Algebra::Projection, Algebra::Restriction, Axiom::Relation::Operation::Order, Axiom::Relation::Operation::Reverse, Axiom::Relation::Operation::Offset, Axiom::Relation::Operation::Limit ].freeze,
29
+ Algebra::Rename => Set[ Algebra::Projection, Algebra::Restriction, Axiom::Relation::Operation::Order, Axiom::Relation::Operation::Reverse, Axiom::Relation::Operation::Offset, Axiom::Relation::Operation::Limit ].freeze,
30
+ Algebra::Restriction => Set[ Algebra::Projection, Axiom::Relation::Operation::Order, Axiom::Relation::Operation::Reverse, ].freeze,
31
+ Axiom::Relation::Operation::Order => Set[ Algebra::Projection, Algebra::Extension, Algebra::Rename, Algebra::Restriction, Algebra::Summarization, Axiom::Relation::Operation::Order, Axiom::Relation::Operation::Reverse, ].freeze,
32
+ Axiom::Relation::Operation::Reverse => Set[ Algebra::Projection, Algebra::Extension, Algebra::Rename, Algebra::Restriction, Algebra::Summarization, Axiom::Relation::Operation::Order, Axiom::Relation::Operation::Reverse, ].freeze,
33
+ Axiom::Relation::Operation::Offset => Set[ Algebra::Projection, Algebra::Extension, Algebra::Rename, Algebra::Restriction, Algebra::Summarization, Axiom::Relation::Operation::Order, Axiom::Relation::Operation::Reverse, ].freeze,
34
+ Axiom::Relation::Operation::Limit => Set[ Algebra::Projection, Algebra::Extension, Algebra::Rename, Algebra::Restriction, Algebra::Summarization, Axiom::Relation::Operation::Order, Axiom::Relation::Operation::Reverse, Axiom::Relation::Operation::Offset, ].freeze,
35
+ }.freeze
36
+
37
+ # Initialize a Unary relation SQL generator
38
+ #
39
+ # @return [undefined]
40
+ #
41
+ # @api private
42
+ def initialize
43
+ super
44
+ @scope = ::Set.new
45
+ end
46
+
47
+ # Visit a Base Relation
48
+ #
49
+ # @param [Relation::Base] base_relation
50
+ #
51
+ # @return [self]
52
+ #
53
+ # @api private
54
+ def visit_axiom_relation_base(base_relation)
55
+ @name = base_relation.name
56
+ @from = visit_identifier(@name)
57
+ @header = base_relation.header
58
+ @columns = columns_for(base_relation)
59
+ self
60
+ end
61
+
62
+ # Visit a Projection
63
+ #
64
+ # @param [Algebra::Projection] projection
65
+ #
66
+ # @return [self]
67
+ #
68
+ # @api private
69
+ def visit_axiom_algebra_projection(projection)
70
+ @from = subquery_for(projection)
71
+ @distinct = DISTINCT
72
+ @header = projection.header
73
+ @columns = columns_for(projection)
74
+ scope_query(projection)
75
+ self
76
+ end
77
+
78
+ # Visit an Extension
79
+ #
80
+ # @param [Algebra::Extension] extension
81
+ #
82
+ # @return [self]
83
+ #
84
+ # @api private
85
+ def visit_axiom_algebra_extension(extension)
86
+ @from = subquery_for(extension)
87
+ @header = extension.header
88
+ @columns ||= columns_for(extension.operand)
89
+ add_extensions(extension.extensions)
90
+ scope_query(extension)
91
+ self
92
+ end
93
+
94
+ # Visit a Rename
95
+ #
96
+ # @param [Algebra::Rename] rename
97
+ #
98
+ # @return [self]
99
+ #
100
+ # @api private
101
+ def visit_axiom_algebra_rename(rename)
102
+ @from = subquery_for(rename)
103
+ @header = rename.header
104
+ @columns = columns_for(rename.operand, rename.aliases.to_hash)
105
+ scope_query(rename)
106
+ self
107
+ end
108
+
109
+ # Visit a Restriction
110
+ #
111
+ # @param [Algebra::Restriction] restriction
112
+ #
113
+ # @return [self]
114
+ #
115
+ # @api private
116
+ def visit_axiom_algebra_restriction(restriction)
117
+ @from = subquery_for(restriction)
118
+ @where = " WHERE #{dispatch(restriction.predicate)}"
119
+ @header = restriction.header
120
+ @columns ||= columns_for(restriction)
121
+ scope_query(restriction)
122
+ self
123
+ end
124
+
125
+ # Visit a Summarization
126
+ #
127
+ # @param [Algebra::Summarization] summarization
128
+ #
129
+ # @return [self]
130
+ #
131
+ # @api private
132
+ def visit_axiom_algebra_summarization(summarization)
133
+ summarize_per = summarization.summarize_per
134
+ @from = subquery_for(summarization)
135
+ @header = summarization.header
136
+ @columns = columns_for(summarize_per)
137
+ summarize_per(summarize_per)
138
+ group_by_columns
139
+ add_extensions(summarization.summarizers)
140
+ scope_query(summarization)
141
+ self
142
+ end
143
+
144
+ # Visit an Order
145
+ #
146
+ # @param [Relation::Operation::Order] order
147
+ #
148
+ # @return [self]
149
+ #
150
+ # @api private
151
+ def visit_axiom_relation_operation_order(order)
152
+ @from = subquery_for(order)
153
+ @order = " ORDER BY #{order_for(order.directions)}"
154
+ @header = order.header
155
+ @columns ||= columns_for(order)
156
+ scope_query(order)
157
+ self
158
+ end
159
+
160
+ # Visit a Limit
161
+ #
162
+ # @param [Relation::Operation::Limit] limit
163
+ #
164
+ # @return [self]
165
+ #
166
+ # @api private
167
+ def visit_axiom_relation_operation_limit(limit)
168
+ @from = subquery_for(limit)
169
+ @limit = " LIMIT #{limit.limit}"
170
+ @header = limit.header
171
+ @columns ||= columns_for(limit)
172
+ scope_query(limit)
173
+ self
174
+ end
175
+
176
+ # Visit an Offset
177
+ #
178
+ # @param [Relation::Operation::Offset] offset
179
+ #
180
+ # @return [self]
181
+ #
182
+ # @api private
183
+ def visit_axiom_relation_operation_offset(offset)
184
+ @from = subquery_for(offset)
185
+ @offset = " OFFSET #{offset.offset}"
186
+ @header = offset.header
187
+ @columns ||= columns_for(offset)
188
+ scope_query(offset)
189
+ self
190
+ end
191
+
192
+ private
193
+
194
+ # Generate the SQL using the supplied columns
195
+ #
196
+ # @param [String] columns
197
+ #
198
+ # @return [#to_s]
199
+ #
200
+ # @api private
201
+ def generate_sql(columns)
202
+ [ "SELECT #{columns} FROM #{@from}", @where, @group, @having, @order, @limit, @offset ].join
203
+ end
204
+
205
+ # Return the columns to use in a query
206
+ #
207
+ # @return [#to_s]
208
+ #
209
+ # @api private
210
+ def query_columns
211
+ explicit_columns
212
+ end
213
+
214
+ # Return the columns to use in a subquery
215
+ #
216
+ # @return [#to_s]
217
+ #
218
+ # @api private
219
+ def subquery_columns
220
+ explicit_columns_in_subquery? ? explicit_columns : super
221
+ end
222
+
223
+ # Test if the subquery should use "*" and not specify columns explicitly
224
+ #
225
+ # @return [Boolean]
226
+ #
227
+ # @api private
228
+ def explicit_columns_in_subquery?
229
+ @scope.include?(Algebra::Projection) ||
230
+ @scope.include?(Algebra::Rename) ||
231
+ @scope.include?(Algebra::Summarization)
232
+ end
233
+
234
+ # Return a list of columns for ordering
235
+ #
236
+ # @param [DirectionSet] directions
237
+ #
238
+ # @return [#to_s]
239
+ #
240
+ # @api private
241
+ def order_for(directions)
242
+ directions.map { |direction| dispatch(direction) }.join(SEPARATOR)
243
+ end
244
+
245
+ # Summarize the operand over the provided relation
246
+ #
247
+ # @param [Relation] relation
248
+ #
249
+ # @return [undefined]
250
+ #
251
+ # @api private
252
+ def summarize_per(relation)
253
+ return if relation.eql?(TABLE_DEE)
254
+
255
+ if relation.eql?(TABLE_DUM) then summarize_per_table_dum
256
+ elsif (generator = Binary.visit(relation)).name.eql?(name) then summarize_per_subset
257
+ else
258
+ summarize_per_relation(generator)
259
+ end
260
+ end
261
+
262
+ # Summarize the operand using table dee
263
+ #
264
+ # @return [undefined]
265
+ #
266
+ # @api private
267
+ def summarize_per_table_dum
268
+ @having = NO_ROWS
269
+ end
270
+
271
+ # Summarize the operand using a subset
272
+ #
273
+ # @return [undefined]
274
+ #
275
+ # @api private
276
+ def summarize_per_subset
277
+ @having = ANY_ROWS
278
+ end
279
+
280
+ # Summarize the operand using another relation
281
+ #
282
+ # @return [undefined]
283
+ #
284
+ # @api private
285
+ def summarize_per_relation(generator)
286
+ @from = "#{generator.to_subquery} AS #{visit_identifier(generator.name)} NATURAL LEFT JOIN #{@from}"
287
+ end
288
+
289
+ # Group by the columns
290
+ #
291
+ # @return [undefined]
292
+ #
293
+ # @api private
294
+ def group_by_columns
295
+ @group = " GROUP BY #{column_list_for(@columns)}" if @columns.any?
296
+ end
297
+
298
+ # Return an expression that can be used for the FROM
299
+ #
300
+ # @param [Relation] relation
301
+ #
302
+ # @return [#to_s]
303
+ #
304
+ # @api private
305
+ def subquery_for(relation)
306
+ operand = relation.operand
307
+ subquery = dispatch(operand)
308
+ if collapse_subquery_for?(relation)
309
+ @from
310
+ else
311
+ aliased_subquery(subquery)
312
+ end
313
+ end
314
+
315
+ # Add the operand to the current scope
316
+ #
317
+ # @param [Relation] operand
318
+ #
319
+ # @return [undefined]
320
+ #
321
+ # @api private
322
+ def scope_query(operand)
323
+ @scope << operand.class
324
+ end
325
+
326
+ # Test if the relation should be collapsed
327
+ #
328
+ # @param [Relation] relation
329
+ #
330
+ # @return [#to_s]
331
+ #
332
+ # @api private
333
+ def collapse_subquery_for?(relation)
334
+ @scope.subset?(COLLAPSIBLE.fetch(relation.class))
335
+ end
336
+
337
+ # Returns an aliased subquery
338
+ #
339
+ # @param [#to_s] subquery
340
+ #
341
+ # @return [#to_s]
342
+ #
343
+ # @api private
344
+ def aliased_subquery(subquery)
345
+ "#{subquery.to_subquery} AS #{visit_identifier(subquery.name)}"
346
+ ensure
347
+ reset_query_state
348
+ end
349
+
350
+ # Visit a Binary Relation
351
+ #
352
+ # @param [Relation::Operation::Binary] binary
353
+ #
354
+ # @return [Relation::Binary]
355
+ #
356
+ # @api private
357
+ def visit_axiom_relation_operation_binary(binary)
358
+ generator = self.class.visit(binary)
359
+ @name = generator.name
360
+ @from = aliased_subquery(generator)
361
+ generator
362
+ end
363
+
364
+ # Reset the query state
365
+ #
366
+ # @return [undefined]
367
+ #
368
+ # @api private
369
+ def reset_query_state
370
+ @scope.clear
371
+ @extensions.clear
372
+ @distinct = @columns = @where = @order = @limit = @offset = @group = @having = nil
373
+ end
374
+
375
+ end # class Unary
376
+ end # class Relation
377
+ end # module Generator
378
+ end # module SQL
379
+ end # module Axiom