axiom-sql-generator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +37 -0
- data/.rspec +4 -0
- data/.rvmrc +1 -0
- data/.travis.yml +35 -0
- data/CONTRIBUTING.md +11 -0
- data/Gemfile +8 -0
- data/Gemfile.devtools +57 -0
- data/Guardfile +23 -0
- data/LICENSE +20 -0
- data/README.md +70 -0
- data/Rakefile +5 -0
- data/TODO +34 -0
- data/axiom-sql-generator.gemspec +25 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +165 -0
- data/config/yardstick.yml +2 -0
- data/lib/axiom-sql-generator.rb +3 -0
- data/lib/axiom/sql/generator.rb +61 -0
- data/lib/axiom/sql/generator/attribute.rb +25 -0
- data/lib/axiom/sql/generator/core_ext/date.rb +20 -0
- data/lib/axiom/sql/generator/core_ext/date_time.rb +46 -0
- data/lib/axiom/sql/generator/direction.rb +38 -0
- data/lib/axiom/sql/generator/function.rb +55 -0
- data/lib/axiom/sql/generator/function/aggregate.rb +134 -0
- data/lib/axiom/sql/generator/function/connective.rb +53 -0
- data/lib/axiom/sql/generator/function/numeric.rb +135 -0
- data/lib/axiom/sql/generator/function/predicate.rb +266 -0
- data/lib/axiom/sql/generator/function/proposition.rb +38 -0
- data/lib/axiom/sql/generator/function/string.rb +29 -0
- data/lib/axiom/sql/generator/identifier.rb +28 -0
- data/lib/axiom/sql/generator/literal.rb +157 -0
- data/lib/axiom/sql/generator/relation.rb +240 -0
- data/lib/axiom/sql/generator/relation/base.rb +14 -0
- data/lib/axiom/sql/generator/relation/binary.rb +136 -0
- data/lib/axiom/sql/generator/relation/insertion.rb +62 -0
- data/lib/axiom/sql/generator/relation/materialized.rb +60 -0
- data/lib/axiom/sql/generator/relation/set.rb +107 -0
- data/lib/axiom/sql/generator/relation/unary.rb +379 -0
- data/lib/axiom/sql/generator/version.rb +12 -0
- data/lib/axiom/sql/generator/visitor.rb +121 -0
- data/spec/rcov.opts +7 -0
- data/spec/shared/generated_sql_behavior.rb +15 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/config_alias.rb +3 -0
- data/spec/unit/axiom/sql/generator/attribute/visit_axiom_attribute_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/class_methods/parenthesize_spec.rb +18 -0
- data/spec/unit/axiom/sql/generator/direction/visit_axiom_relation_operation_order_ascending_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/direction/visit_axiom_relation_operation_order_descending_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_count_spec.rb +16 -0
- data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_maximum_spec.rb +16 -0
- data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_mean_spec.rb +16 -0
- data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_minimum_spec.rb +16 -0
- data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_standard_deviation_spec.rb +16 -0
- data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_sum_spec.rb +16 -0
- data/spec/unit/axiom/sql/generator/function/aggregate/visit_axiom_aggregate_variance_spec.rb +16 -0
- data/spec/unit/axiom/sql/generator/function/connective/visit_axiom_function_connective_conjunction_spec.rb +20 -0
- data/spec/unit/axiom/sql/generator/function/connective/visit_axiom_function_connective_disjunction_spec.rb +20 -0
- data/spec/unit/axiom/sql/generator/function/connective/visit_axiom_function_connective_negation_spec.rb +20 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_absolute_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_addition_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_division_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_exponentiation_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_modulo_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_multiplication_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_square_root_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_subtraction_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_unary_minus_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/numeric/visit_axiom_function_numeric_unary_plus_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_equality_spec.rb +27 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_exclusion_spec.rb +47 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_greater_than_or_equal_to_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_greater_than_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_inclusion_spec.rb +47 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_inequality_spec.rb +55 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_less_than_or_equal_to_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/predicate/visit_axiom_function_predicate_less_than_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/proposition/visit_axiom_function_proposition_contradiction_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/proposition/visit_axiom_function_proposition_tautology_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/function/string/visit_axiom_function_string_length_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/identifier/visit_identifier_spec.rb +26 -0
- data/spec/unit/axiom/sql/generator/literal/class_methods/dup_frozen_spec.rb +23 -0
- data/spec/unit/axiom/sql/generator/literal/visit_class_spec.rb +31 -0
- data/spec/unit/axiom/sql/generator/literal/visit_date_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/literal/visit_date_time_spec.rb +55 -0
- data/spec/unit/axiom/sql/generator/literal/visit_enumerable_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/literal/visit_false_class_spec.rb +14 -0
- data/spec/unit/axiom/sql/generator/literal/visit_nil_class_spec.rb +14 -0
- data/spec/unit/axiom/sql/generator/literal/visit_numeric_spec.rb +34 -0
- data/spec/unit/axiom/sql/generator/literal/visit_string_spec.rb +26 -0
- data/spec/unit/axiom/sql/generator/literal/visit_time_spec.rb +97 -0
- data/spec/unit/axiom/sql/generator/literal/visit_true_class_spec.rb +14 -0
- data/spec/unit/axiom/sql/generator/relation/binary/base/to_subquery_spec.rb +35 -0
- data/spec/unit/axiom/sql/generator/relation/binary/base/visit_axiom_relation_base_spec.rb +22 -0
- data/spec/unit/axiom/sql/generator/relation/binary/to_s_spec.rb +35 -0
- data/spec/unit/axiom/sql/generator/relation/binary/to_subquery_spec.rb +35 -0
- data/spec/unit/axiom/sql/generator/relation/binary/visit_axiom_algebra_join_spec.rb +179 -0
- data/spec/unit/axiom/sql/generator/relation/binary/visit_axiom_algebra_product_spec.rb +183 -0
- data/spec/unit/axiom/sql/generator/relation/class_methods/visit_spec.rb +71 -0
- data/spec/unit/axiom/sql/generator/relation/insertion/to_subquery_spec.rb +15 -0
- data/spec/unit/axiom/sql/generator/relation/insertion/visit_axiom_relation_operation_insertion_spec.rb +187 -0
- data/spec/unit/axiom/sql/generator/relation/materialized/visit_axiom_relation_materialized_spec.rb +28 -0
- data/spec/unit/axiom/sql/generator/relation/materialized/visited_spec.rb +26 -0
- data/spec/unit/axiom/sql/generator/relation/name_spec.rb +30 -0
- data/spec/unit/axiom/sql/generator/relation/set/class_methods/normalize_operand_headers_spec.rb +35 -0
- data/spec/unit/axiom/sql/generator/relation/set/to_s_spec.rb +55 -0
- data/spec/unit/axiom/sql/generator/relation/set/to_subquery_spec.rb +55 -0
- data/spec/unit/axiom/sql/generator/relation/set/visit_axiom_algebra_difference_spec.rb +191 -0
- data/spec/unit/axiom/sql/generator/relation/set/visit_axiom_algebra_intersection_spec.rb +188 -0
- data/spec/unit/axiom/sql/generator/relation/set/visit_axiom_algebra_union_spec.rb +188 -0
- data/spec/unit/axiom/sql/generator/relation/to_s_spec.rb +50 -0
- data/spec/unit/axiom/sql/generator/relation/to_sql_spec.rb +52 -0
- data/spec/unit/axiom/sql/generator/relation/to_subquery_spec.rb +49 -0
- data/spec/unit/axiom/sql/generator/relation/unary/to_s_spec.rb +55 -0
- data/spec/unit/axiom/sql/generator/relation/unary/to_subquery_spec.rb +75 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_extension_spec.rb +165 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_projection_spec.rb +193 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_rename_spec.rb +178 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_restriction_spec.rb +199 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_algebra_summarization_spec.rb +652 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_base_spec.rb +21 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_limit_spec.rb +165 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_offset_spec.rb +165 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_order_spec.rb +183 -0
- data/spec/unit/axiom/sql/generator/relation/unary/visit_axiom_relation_operation_reverse_spec.rb +165 -0
- data/spec/unit/axiom/sql/generator/relation/visit_spec.rb +54 -0
- data/spec/unit/axiom/sql/generator/relation/visited_spec.rb +35 -0
- data/spec/unit/axiom/sql/generator/visitor/class_methods/handler_for_spec.rb +71 -0
- data/spec/unit/axiom/sql/generator/visitor/visit_spec.rb +12 -0
- data/spec/unit/axiom/sql/generator/visitor/visited_spec.rb +11 -0
- data/spec/unit/date/iso8601_spec.rb +23 -0
- data/spec/unit/date_time/iso8601_spec.rb +112 -0
- 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
|