alf-sql 0.15.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 (121) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +38 -0
  4. data/LICENCE.md +22 -0
  5. data/Manifest.txt +12 -0
  6. data/README.md +52 -0
  7. data/Rakefile +11 -0
  8. data/lib/alf-sql.rb +1 -0
  9. data/lib/alf/algebra/operand.rb +18 -0
  10. data/lib/alf/predicate/nodes.rb +20 -0
  11. data/lib/alf/predicate/nodes/and.rb +11 -0
  12. data/lib/alf/predicate/nodes/contradiction.rb +12 -0
  13. data/lib/alf/predicate/nodes/dyadic_comp.rb +14 -0
  14. data/lib/alf/predicate/nodes/eq.rb +11 -0
  15. data/lib/alf/predicate/nodes/exists.rb +14 -0
  16. data/lib/alf/predicate/nodes/expr.rb +20 -0
  17. data/lib/alf/predicate/nodes/gt.rb +11 -0
  18. data/lib/alf/predicate/nodes/gte.rb +11 -0
  19. data/lib/alf/predicate/nodes/identifier.rb +12 -0
  20. data/lib/alf/predicate/nodes/in.rb +31 -0
  21. data/lib/alf/predicate/nodes/literal.rb +12 -0
  22. data/lib/alf/predicate/nodes/lt.rb +11 -0
  23. data/lib/alf/predicate/nodes/lte.rb +11 -0
  24. data/lib/alf/predicate/nodes/nadic_bool.rb +18 -0
  25. data/lib/alf/predicate/nodes/native.rb +11 -0
  26. data/lib/alf/predicate/nodes/neq.rb +11 -0
  27. data/lib/alf/predicate/nodes/not.rb +14 -0
  28. data/lib/alf/predicate/nodes/or.rb +11 -0
  29. data/lib/alf/predicate/nodes/qualified_identifier.rb +12 -0
  30. data/lib/alf/predicate/nodes/tautology.rb +12 -0
  31. data/lib/alf/sql.rb +15 -0
  32. data/lib/alf/sql/builder.rb +152 -0
  33. data/lib/alf/sql/cog.rb +32 -0
  34. data/lib/alf/sql/compiler.rb +137 -0
  35. data/lib/alf/sql/grammar.rb +44 -0
  36. data/lib/alf/sql/grammar.sexp.yml +92 -0
  37. data/lib/alf/sql/loader.rb +2 -0
  38. data/lib/alf/sql/nodes/column_name.rb +25 -0
  39. data/lib/alf/sql/nodes/cross_join.rb +28 -0
  40. data/lib/alf/sql/nodes/except.rb +14 -0
  41. data/lib/alf/sql/nodes/expr.rb +90 -0
  42. data/lib/alf/sql/nodes/from_clause.rb +24 -0
  43. data/lib/alf/sql/nodes/inner_join.rb +37 -0
  44. data/lib/alf/sql/nodes/intersect.rb +14 -0
  45. data/lib/alf/sql/nodes/limit_clause.rb +19 -0
  46. data/lib/alf/sql/nodes/literal.rb +18 -0
  47. data/lib/alf/sql/nodes/name_intro.rb +23 -0
  48. data/lib/alf/sql/nodes/offset_clause.rb +19 -0
  49. data/lib/alf/sql/nodes/order_by_clause.rb +25 -0
  50. data/lib/alf/sql/nodes/order_by_term.rb +30 -0
  51. data/lib/alf/sql/nodes/qualified_name.rb +32 -0
  52. data/lib/alf/sql/nodes/range_var_name.rb +17 -0
  53. data/lib/alf/sql/nodes/select_exp.rb +101 -0
  54. data/lib/alf/sql/nodes/select_item.rb +37 -0
  55. data/lib/alf/sql/nodes/select_list.rb +31 -0
  56. data/lib/alf/sql/nodes/select_star.rb +15 -0
  57. data/lib/alf/sql/nodes/set_operator.rb +64 -0
  58. data/lib/alf/sql/nodes/set_quantifier.rb +20 -0
  59. data/lib/alf/sql/nodes/subquery_as.rb +28 -0
  60. data/lib/alf/sql/nodes/table_as.rb +31 -0
  61. data/lib/alf/sql/nodes/table_name.rb +17 -0
  62. data/lib/alf/sql/nodes/union.rb +14 -0
  63. data/lib/alf/sql/nodes/where_clause.rb +20 -0
  64. data/lib/alf/sql/nodes/with_exp.rb +50 -0
  65. data/lib/alf/sql/nodes/with_spec.rb +24 -0
  66. data/lib/alf/sql/processor.rb +85 -0
  67. data/lib/alf/sql/processor/all.rb +17 -0
  68. data/lib/alf/sql/processor/clip.rb +39 -0
  69. data/lib/alf/sql/processor/distinct.rb +17 -0
  70. data/lib/alf/sql/processor/flatten.rb +24 -0
  71. data/lib/alf/sql/processor/from_self.rb +29 -0
  72. data/lib/alf/sql/processor/join.rb +80 -0
  73. data/lib/alf/sql/processor/join_support.rb +27 -0
  74. data/lib/alf/sql/processor/limit_offset.rb +30 -0
  75. data/lib/alf/sql/processor/merge.rb +42 -0
  76. data/lib/alf/sql/processor/order_by.rb +32 -0
  77. data/lib/alf/sql/processor/rename.rb +25 -0
  78. data/lib/alf/sql/processor/reorder.rb +21 -0
  79. data/lib/alf/sql/processor/requalify.rb +24 -0
  80. data/lib/alf/sql/processor/semi_join.rb +57 -0
  81. data/lib/alf/sql/processor/star.rb +17 -0
  82. data/lib/alf/sql/processor/where.rb +25 -0
  83. data/lib/alf/sql/version.rb +16 -0
  84. data/spec/algebra/operand/test_to_sql.rb +32 -0
  85. data/spec/builder/test_order_by_clause.rb +44 -0
  86. data/spec/helpers/ast.rb +242 -0
  87. data/spec/helpers/compiler.rb +23 -0
  88. data/spec/nodes/column_name/test_as_name.rb +14 -0
  89. data/spec/nodes/column_name/test_qualifier.rb +14 -0
  90. data/spec/nodes/order_by_clause/test_to_ordering.rb +30 -0
  91. data/spec/nodes/order_by_term/test_as_name.rb +22 -0
  92. data/spec/nodes/order_by_term/test_direction.rb +22 -0
  93. data/spec/nodes/order_by_term/test_qualifier.rb +22 -0
  94. data/spec/nodes/select_exp/test_order_by_clause.rb +22 -0
  95. data/spec/nodes/select_item/test_as_name.rb +22 -0
  96. data/spec/predicate/nodes/exists/test_not.rb +20 -0
  97. data/spec/processor/clip/test_on_select_exp.rb +32 -0
  98. data/spec/processor/clip/test_on_select_list.rb +24 -0
  99. data/spec/processor/distinct/test_on_set_quantified.rb +31 -0
  100. data/spec/processor/from_self/test_on_nonjoin_exp.rb +60 -0
  101. data/spec/processor/from_self/test_on_with_exp.rb +22 -0
  102. data/spec/processor/merge/test_on_select_exp.rb +73 -0
  103. data/spec/processor/merge/test_on_with_exp.rb +56 -0
  104. data/spec/processor/order_by/test_on_select_exp.rb +24 -0
  105. data/spec/processor/rename/test_on_select_item.rb +30 -0
  106. data/spec/processor/rename/test_on_select_list.rb +22 -0
  107. data/spec/processor/reorder/test_on_select_list.rb +22 -0
  108. data/spec/processor/requalify/test_on_select_exp.rb +24 -0
  109. data/spec/processor/star/test_on_select_exp.rb +24 -0
  110. data/spec/processor/test_clip.rb +24 -0
  111. data/spec/processor/test_distinct.rb +24 -0
  112. data/spec/processor/test_on_select_exp.rb +28 -0
  113. data/spec/processor/test_on_set_operator.rb +28 -0
  114. data/spec/processor/test_rename.rb +24 -0
  115. data/spec/shared/compiled_examples.rb +13 -0
  116. data/spec/spec_helper.rb +14 -0
  117. data/spec/test_sql.rb +10 -0
  118. data/tasks/bench.rake +40 -0
  119. data/tasks/gem.rake +8 -0
  120. data/tasks/test.rake +6 -0
  121. metadata +235 -0
@@ -0,0 +1,20 @@
1
+ module Alf
2
+ module Sql
3
+ module WhereClause
4
+ include Expr
5
+
6
+ WHERE = "WHERE".freeze
7
+
8
+ def predicate
9
+ last
10
+ end
11
+
12
+ def to_sql(buffer = "")
13
+ buffer << WHERE << SPACE
14
+ predicate.to_sql(buffer)
15
+ buffer
16
+ end
17
+
18
+ end # module WhereClause
19
+ end # module Sql
20
+ end # module Alf
@@ -0,0 +1,50 @@
1
+ module Alf
2
+ module Sql
3
+ module WithExp
4
+ include Expr
5
+ extend Forwardable
6
+
7
+ WITH = "WITH".freeze
8
+
9
+ def with_exp?
10
+ true
11
+ end
12
+
13
+ def with_spec
14
+ self[1]
15
+ end
16
+
17
+ def select_exp
18
+ last
19
+ end
20
+ def_delegators :select_exp, :select_list,
21
+ :where_clause,
22
+ :predicate,
23
+ :from_clause,
24
+ :table_spec,
25
+ :order_by_clause,
26
+ :limit_clause,
27
+ :offset_clause,
28
+ :desaliaser,
29
+ :to_attr_list,
30
+ :to_ordering,
31
+ :all?,
32
+ :distinct?,
33
+ :set_operator?,
34
+ :limit_or_offset?,
35
+ :join?,
36
+ :should_be_reused?
37
+
38
+ # to_xxx
39
+
40
+ def to_sql(buffer = "")
41
+ buffer << WITH << SPACE
42
+ self[1].to_sql(buffer)
43
+ buffer << SPACE
44
+ self[2].to_sql(buffer, false)
45
+ buffer
46
+ end
47
+
48
+ end # module WithExp
49
+ end # module Sql
50
+ end # module Alf
@@ -0,0 +1,24 @@
1
+ module Alf
2
+ module Sql
3
+ module WithSpec
4
+ include Expr
5
+
6
+ def to_sql(buffer = "")
7
+ each_child do |child,index|
8
+ buffer << COMMA << SPACE unless index==0
9
+ child.to_sql(buffer)
10
+ end
11
+ buffer
12
+ end
13
+
14
+ def to_hash
15
+ hash = {}
16
+ each_child do |child|
17
+ hash[child.table_name.value] = child.subquery
18
+ end
19
+ hash
20
+ end
21
+
22
+ end # module WithSpec
23
+ end # module Sql
24
+ end # module Alf
@@ -0,0 +1,85 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor < Sexpr::Rewriter
4
+ grammar Sql::Grammar
5
+
6
+ UnexpectedError = Class.new(Alf::Error)
7
+
8
+ def initialize(builder)
9
+ @builder = builder
10
+ end
11
+ attr_reader :builder
12
+
13
+ def on_with_exp(sexpr)
14
+ applied = apply(sexpr.select_exp)
15
+ if applied.with_exp?
16
+ merge_with_exps(sexpr, applied, applied.select_exp)
17
+ else
18
+ sexpr.with_update(-1, applied)
19
+ end
20
+ end
21
+
22
+ def on_set_operator(sexpr)
23
+ sexpr.each_with_index.map{|child,index|
24
+ index <= 1 ? child : apply(child)
25
+ }
26
+ end
27
+ alias :on_union :on_set_operator
28
+ alias :on_except :on_set_operator
29
+ alias :on_intersect :on_set_operator
30
+
31
+ def on_select_exp(sexpr)
32
+ sexpr.with_update(2, apply(sexpr[2]))
33
+ end
34
+
35
+ private
36
+
37
+ def merge_with_exps(left, right, main)
38
+ if left.with_exp? and right.with_exp?
39
+ [ :with_exp,
40
+ merge_with_specs(left.with_spec, right.with_spec),
41
+ main ]
42
+ elsif left.with_exp?
43
+ left.with_update(-1, main)
44
+ elsif right.with_exp?
45
+ right.with_update(-1, main)
46
+ else
47
+ main
48
+ end
49
+ end
50
+
51
+ def merge_with_specs(left, right)
52
+ hash = left.to_hash.merge(right.to_hash){|k,v1,v2|
53
+ unless v1 == v2
54
+ msg = "Unexpected different SQL expr: "
55
+ msg << "`#{v1.to_sql}` vs. `#{v2.to_sql}`"
56
+ raise UnexpectedError, msg
57
+ end
58
+ v1
59
+ }
60
+ hash.map{|(k,v)| builder.name_intro(k,v) }.unshift(:with_spec)
61
+ end
62
+
63
+ def tautology
64
+ Predicate::Factory.tautology
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ require_relative 'processor/distinct'
71
+ require_relative 'processor/all'
72
+ require_relative 'processor/clip'
73
+ require_relative 'processor/star'
74
+ require_relative 'processor/rename'
75
+ require_relative 'processor/order_by'
76
+ require_relative 'processor/limit_offset'
77
+ require_relative 'processor/from_self'
78
+ require_relative 'processor/reorder'
79
+ require_relative 'processor/merge'
80
+ require_relative 'processor/where'
81
+ require_relative 'processor/join_support'
82
+ require_relative 'processor/join'
83
+ require_relative 'processor/semi_join'
84
+ require_relative 'processor/flatten'
85
+ require_relative 'processor/requalify'
@@ -0,0 +1,17 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ class All < Processor
5
+
6
+ def on_set_quantified(sexpr)
7
+ sexpr.with_update(1, builder.all)
8
+ end
9
+ alias :on_union :on_set_quantified
10
+ alias :on_except :on_set_quantified
11
+ alias :on_intersect :on_set_quantified
12
+ alias :on_select_exp :on_set_quantified
13
+
14
+ end # class All
15
+ end # class Processor
16
+ end # module Sql
17
+ end # module Alf
@@ -0,0 +1,39 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ class Clip < Processor
5
+
6
+ def initialize(attributes, on_empty = :is_table_dee, builder)
7
+ super(builder)
8
+ @attributes = attributes
9
+ @on_empty = on_empty
10
+ end
11
+ attr_reader :attributes, :on_empty
12
+
13
+ def on_select_exp(sexpr)
14
+ catch(:empty){ return super(sexpr) }
15
+ send("select_#{on_empty}", sexpr)
16
+ end
17
+
18
+ def on_select_list(sexpr)
19
+ result = sexpr.select{|child|
20
+ (child == :select_list) or
21
+ attributes.include?(child.as_name.to_sym)
22
+ }
23
+ (result.size==1) ? throw(:empty) : result
24
+ end
25
+
26
+ private
27
+
28
+ def select_is_table_dee(sexpr)
29
+ builder.select_is_table_dee(select_star(sexpr))
30
+ end
31
+
32
+ def select_star(sexpr)
33
+ All.new(builder).call(Star.new(builder).call(sexpr))
34
+ end
35
+
36
+ end # class Clip
37
+ end # class Processor
38
+ end # module Sql
39
+ end # module Alf
@@ -0,0 +1,17 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ class Distinct < Processor
5
+
6
+ def on_set_quantified(sexpr)
7
+ sexpr.with_update(1, builder.distinct)
8
+ end
9
+ alias :on_union :on_set_quantified
10
+ alias :on_except :on_set_quantified
11
+ alias :on_intersect :on_set_quantified
12
+ alias :on_select_exp :on_set_quantified
13
+
14
+ end # class Distinct
15
+ end # class Processor
16
+ end # module Sql
17
+ end # module Alf
@@ -0,0 +1,24 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ class Flatten < Processor
5
+
6
+ def on_with_exp(sexpr)
7
+ @subqueries = sexpr.with_spec.to_hash
8
+ apply(sexpr.select_exp)
9
+ end
10
+ attr_reader :subqueries
11
+
12
+ alias :on_select_exp :copy_and_apply
13
+ alias :on_missing :copy_and_apply
14
+
15
+ def on_table_as(sexpr)
16
+ return sexpr unless subqueries
17
+ return sexpr unless subquery = subqueries[sexpr.table_name]
18
+ [ :subquery_as, apply(subquery), sexpr.right ]
19
+ end
20
+
21
+ end # class Flatten
22
+ end # class Processor
23
+ end # module Sql
24
+ end # module Alf
@@ -0,0 +1,29 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ class FromSelf < Processor
5
+
6
+ def on_with_exp(sexpr)
7
+ q = builder.next_qualifier!
8
+ name_intro = builder.name_intro(q, sexpr.select_exp)
9
+ [ :with_exp,
10
+ sexpr.with_spec.dup.push(name_intro),
11
+ builder.select_all(sexpr.select_exp, q, q) ]
12
+ end
13
+
14
+ def on_nonjoin_exp(sexpr)
15
+ q = builder.next_qualifier!
16
+ [ :with_exp,
17
+ [:with_spec,
18
+ builder.name_intro(q, sexpr)],
19
+ builder.select_all(sexpr, q, q) ]
20
+ end
21
+ alias :on_union :on_nonjoin_exp
22
+ alias :on_except :on_nonjoin_exp
23
+ alias :on_intersect :on_nonjoin_exp
24
+ alias :on_select_exp :on_nonjoin_exp
25
+
26
+ end # class FromSelf
27
+ end # class Processor
28
+ end # module Sql
29
+ end # module Alf
@@ -0,0 +1,80 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ class Join < Processor
5
+ include JoinSupport
6
+
7
+ def initialize(right, builder)
8
+ super(builder)
9
+ @right = right
10
+ end
11
+ attr_reader :right
12
+
13
+ def call(sexpr)
14
+ if unjoinable?(sexpr)
15
+ call(builder.from_self(sexpr))
16
+ elsif unjoinable?(right)
17
+ Join.new(builder.from_self(right), builder).call(sexpr)
18
+ else
19
+ super(sexpr)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def apply_join_strategy(left, right)
26
+ [ :select_exp,
27
+ join_set_quantifiers(left, right),
28
+ join_select_lists(left, right),
29
+ join_from_clauses(left, right),
30
+ join_where_clauses(left, right),
31
+ join_order_by_clauses(left, right) ].compact
32
+ end
33
+
34
+ def unjoinable?(sexpr)
35
+ sexpr.set_operator? or sexpr.limit_or_offset? or sexpr.join?
36
+ end
37
+
38
+ def join_set_quantifiers(left, right)
39
+ left_q, right_q = left.set_quantifier, right.set_quantifier
40
+ left_q == right_q ? left_q : builder.distinct
41
+ end
42
+
43
+ def join_select_lists(left, right)
44
+ left_list, right_list = left.select_list, right.select_list
45
+ list = left_list.dup
46
+ right_list.each_child do |child, index|
47
+ list << child unless left_list.knows?(child.as_name)
48
+ end
49
+ list
50
+ end
51
+
52
+ def join_from_clauses(left, right)
53
+ joincon = join_predicate(left, right)
54
+ join = if joincon.tautology?
55
+ [:cross_join, left.table_spec, right.table_spec]
56
+ else
57
+ [:inner_join, left.table_spec, right.table_spec, joincon]
58
+ end
59
+ left.from_clause.with_update(-1, join)
60
+ end
61
+
62
+ def join_where_clauses(left, right)
63
+ predicate = [ tautology, left.predicate, right.predicate ].compact
64
+ case predicate.size
65
+ when 1 then nil
66
+ when 2 then [ :where_clause, predicate.last ]
67
+ else [ :where_clause, predicate.reduce(:&) ]
68
+ end
69
+ end
70
+
71
+ def join_order_by_clauses(left, right)
72
+ order_by = [ left.order_by_clause, right.order_by_clause ].compact
73
+ return order_by.first if order_by.size <= 1
74
+ order_by.first + order_by.last.sexpr_body
75
+ end
76
+
77
+ end # class Join
78
+ end # class Processor
79
+ end # module Sql
80
+ end # module Alf
@@ -0,0 +1,27 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ module JoinSupport
5
+
6
+ def on_main_exp(sexpr)
7
+ joined = apply_join_strategy(sexpr.select_exp, right.select_exp)
8
+ merge_with_exps(sexpr, right, joined)
9
+ end
10
+ alias :on_with_exp :on_main_exp
11
+ alias :on_select_exp :on_main_exp
12
+
13
+ private
14
+
15
+ def join_predicate(left, right, commons = left.to_attr_list & right.to_attr_list)
16
+ commons = left.to_attr_list & right.to_attr_list
17
+ left_d, right_d = left.desaliaser, right.desaliaser
18
+ commons.to_a.inject(tautology){|cond, attr|
19
+ left_attr, right_attr = left_d[attr], right_d[attr]
20
+ cond &= Predicate::Factory.eq(left_attr, right_attr)
21
+ }
22
+ end
23
+
24
+ end # class JoinSupport
25
+ end # class Processor
26
+ end # module Sql
27
+ end # module Alf
@@ -0,0 +1,30 @@
1
+ module Alf
2
+ module Sql
3
+ class Processor
4
+ class LimitOffset < Processor
5
+
6
+ def initialize(limit, offset, builder)
7
+ super(builder)
8
+ @limit = limit
9
+ @offset = offset
10
+ end
11
+ attr_reader :limit, :offset
12
+
13
+ def on_set_operator(sexpr)
14
+ apply(builder.from_self(sexpr))
15
+ end
16
+ alias :on_union :on_set_operator
17
+ alias :on_except :on_set_operator
18
+ alias :on_intersect :on_set_operator
19
+
20
+ def on_select_exp(sexpr)
21
+ sexpr = builder.from_self(sexpr) if obc = sexpr.limit_or_offset?
22
+ limit_clause = builder.limit_clause(limit)
23
+ offset_clause = builder.offset_clause(offset)
24
+ sexpr.with_push(limit_clause, offset_clause)
25
+ end
26
+
27
+ end # class LimitOffset
28
+ end # class Processor
29
+ end # module Sql
30
+ end # module Alf