bmg 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bmg/algebra.rb +2 -26
  3. data/lib/bmg/algebra/shortcuts.rb +14 -0
  4. data/lib/bmg/operator/autowrap.rb +32 -2
  5. data/lib/bmg/operator/constants.rb +12 -0
  6. data/lib/bmg/operator/image.rb +10 -0
  7. data/lib/bmg/operator/rename.rb +8 -0
  8. data/lib/bmg/relation/spied.rb +4 -9
  9. data/lib/bmg/sequel.rb +53 -2
  10. data/lib/bmg/sequel/relation.rb +24 -19
  11. data/lib/bmg/sequel/translator.rb +153 -0
  12. data/lib/bmg/sequel/type_inference.rb +42 -0
  13. data/lib/bmg/sql.rb +20 -0
  14. data/lib/bmg/sql/builder.rb +177 -0
  15. data/lib/bmg/sql/dialect.rb +15 -0
  16. data/lib/bmg/sql/ext/predicate.rb +23 -0
  17. data/lib/bmg/sql/ext/predicate/and.rb +9 -0
  18. data/lib/bmg/sql/ext/predicate/contradiction.rb +10 -0
  19. data/lib/bmg/sql/ext/predicate/dyadic_comp.rb +12 -0
  20. data/lib/bmg/sql/ext/predicate/eq.rb +9 -0
  21. data/lib/bmg/sql/ext/predicate/exists.rb +12 -0
  22. data/lib/bmg/sql/ext/predicate/expr.rb +18 -0
  23. data/lib/bmg/sql/ext/predicate/gt.rb +9 -0
  24. data/lib/bmg/sql/ext/predicate/gte.rb +9 -0
  25. data/lib/bmg/sql/ext/predicate/identifier.rb +10 -0
  26. data/lib/bmg/sql/ext/predicate/in.rb +29 -0
  27. data/lib/bmg/sql/ext/predicate/literal.rb +10 -0
  28. data/lib/bmg/sql/ext/predicate/lt.rb +9 -0
  29. data/lib/bmg/sql/ext/predicate/lte.rb +9 -0
  30. data/lib/bmg/sql/ext/predicate/nadic_bool.rb +16 -0
  31. data/lib/bmg/sql/ext/predicate/native.rb +9 -0
  32. data/lib/bmg/sql/ext/predicate/neq.rb +9 -0
  33. data/lib/bmg/sql/ext/predicate/not.rb +12 -0
  34. data/lib/bmg/sql/ext/predicate/or.rb +9 -0
  35. data/lib/bmg/sql/ext/predicate/qualified_identifier.rb +12 -0
  36. data/lib/bmg/sql/ext/predicate/tautology.rb +10 -0
  37. data/lib/bmg/sql/grammar.rb +45 -0
  38. data/lib/bmg/sql/grammar.sexp.yml +96 -0
  39. data/lib/bmg/sql/nodes/column_name.rb +25 -0
  40. data/lib/bmg/sql/nodes/cross_join.rb +28 -0
  41. data/lib/bmg/sql/nodes/except.rb +14 -0
  42. data/lib/bmg/sql/nodes/expr.rb +94 -0
  43. data/lib/bmg/sql/nodes/from_clause.rb +24 -0
  44. data/lib/bmg/sql/nodes/inner_join.rb +37 -0
  45. data/lib/bmg/sql/nodes/intersect.rb +14 -0
  46. data/lib/bmg/sql/nodes/limit_clause.rb +19 -0
  47. data/lib/bmg/sql/nodes/literal.rb +18 -0
  48. data/lib/bmg/sql/nodes/name_intro.rb +23 -0
  49. data/lib/bmg/sql/nodes/native_table_as.rb +31 -0
  50. data/lib/bmg/sql/nodes/offset_clause.rb +19 -0
  51. data/lib/bmg/sql/nodes/order_by_clause.rb +25 -0
  52. data/lib/bmg/sql/nodes/order_by_term.rb +30 -0
  53. data/lib/bmg/sql/nodes/qualified_name.rb +32 -0
  54. data/lib/bmg/sql/nodes/range_var_name.rb +17 -0
  55. data/lib/bmg/sql/nodes/select_exp.rb +109 -0
  56. data/lib/bmg/sql/nodes/select_item.rb +37 -0
  57. data/lib/bmg/sql/nodes/select_list.rb +35 -0
  58. data/lib/bmg/sql/nodes/select_star.rb +22 -0
  59. data/lib/bmg/sql/nodes/set_operator.rb +68 -0
  60. data/lib/bmg/sql/nodes/set_quantifier.rb +20 -0
  61. data/lib/bmg/sql/nodes/subquery_as.rb +28 -0
  62. data/lib/bmg/sql/nodes/table_as.rb +31 -0
  63. data/lib/bmg/sql/nodes/table_name.rb +17 -0
  64. data/lib/bmg/sql/nodes/union.rb +14 -0
  65. data/lib/bmg/sql/nodes/where_clause.rb +20 -0
  66. data/lib/bmg/sql/nodes/with_exp.rb +51 -0
  67. data/lib/bmg/sql/nodes/with_spec.rb +24 -0
  68. data/lib/bmg/sql/processor.rb +85 -0
  69. data/lib/bmg/sql/processor/all.rb +17 -0
  70. data/lib/bmg/sql/processor/clip.rb +57 -0
  71. data/lib/bmg/sql/processor/distinct.rb +17 -0
  72. data/lib/bmg/sql/processor/flatten.rb +24 -0
  73. data/lib/bmg/sql/processor/from_self.rb +29 -0
  74. data/lib/bmg/sql/processor/join.rb +80 -0
  75. data/lib/bmg/sql/processor/join_support.rb +28 -0
  76. data/lib/bmg/sql/processor/limit_offset.rb +30 -0
  77. data/lib/bmg/sql/processor/merge.rb +49 -0
  78. data/lib/bmg/sql/processor/order_by.rb +32 -0
  79. data/lib/bmg/sql/processor/rename.rb +25 -0
  80. data/lib/bmg/sql/processor/reorder.rb +21 -0
  81. data/lib/bmg/sql/processor/requalify.rb +24 -0
  82. data/lib/bmg/sql/processor/semi_join.rb +68 -0
  83. data/lib/bmg/sql/processor/star.rb +17 -0
  84. data/lib/bmg/sql/processor/where.rb +25 -0
  85. data/lib/bmg/sql/relation.rb +141 -0
  86. data/lib/bmg/sql/version.rb +16 -0
  87. data/lib/bmg/support.rb +1 -0
  88. data/lib/bmg/support/keys.rb +59 -0
  89. data/lib/bmg/type.rb +95 -14
  90. data/lib/bmg/version.rb +2 -2
  91. data/tasks/test.rake +9 -2
  92. metadata +97 -5
@@ -0,0 +1,17 @@
1
+ module Bmg
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 Bmg
@@ -0,0 +1,24 @@
1
+ module Bmg
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 Bmg
@@ -0,0 +1,29 @@
1
+ module Bmg
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 Bmg
@@ -0,0 +1,80 @@
1
+ module Bmg
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 Bmg
@@ -0,0 +1,28 @@
1
+ module Bmg
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)
16
+ left_d, right_d = left.desaliaser, right.desaliaser
17
+ commons.to_a.inject(tautology){|cond, attr|
18
+ left_attr, right_attr = left_d[attr], right_d[attr]
19
+ left_p = Predicate::Factory.qualified_identifier(left_attr.qualifier, left_attr.as_name)
20
+ right_p = Predicate::Factory.qualified_identifier(right_attr.qualifier, right_attr.as_name)
21
+ cond &= Predicate::Factory.eq(left_p, right_p)
22
+ }
23
+ end
24
+
25
+ end # class JoinSupport
26
+ end # class Processor
27
+ end # module Sql
28
+ end # module Bmg
@@ -0,0 +1,30 @@
1
+ module Bmg
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 Bmg
@@ -0,0 +1,49 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class Merge < Processor
5
+
6
+ def initialize(kind, all, right, builder)
7
+ super(builder)
8
+ @kind = kind
9
+ @all = all
10
+ @right = right
11
+ end
12
+
13
+ def on_with_exp(sexpr)
14
+ if @right.with_exp?
15
+ reordered = Reorder.new(sexpr.to_attr_list, builder).call(@right)
16
+ main = [ @kind, modifier, sexpr.select_exp, reordered.select_exp ]
17
+ merge_with_exps(sexpr, reordered, main)
18
+ else
19
+ [ :with_exp,
20
+ sexpr.with_spec,
21
+ apply(sexpr.last) ]
22
+ end
23
+ end
24
+
25
+ def on_nonjoin_exp(sexpr)
26
+ reordered = Reorder.new(sexpr.to_attr_list, builder).call(@right)
27
+ if @right.with_exp?
28
+ [ :with_exp,
29
+ reordered.with_spec,
30
+ [ @kind, modifier, sexpr, reordered.select_exp ] ]
31
+ else
32
+ [ @kind, modifier, sexpr, reordered ]
33
+ end
34
+ end
35
+ alias :on_union :on_nonjoin_exp
36
+ alias :on_except :on_nonjoin_exp
37
+ alias :on_intersect :on_nonjoin_exp
38
+ alias :on_select_exp :on_nonjoin_exp
39
+
40
+ private
41
+
42
+ def modifier
43
+ @all ? builder.all : builder.distinct
44
+ end
45
+
46
+ end # class Merge
47
+ end # class Processor
48
+ end # module Sql
49
+ end # module Bmg
@@ -0,0 +1,32 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class OrderBy < Processor
5
+
6
+ def initialize(ordering, builder)
7
+ super(builder)
8
+ @ordering = ordering
9
+ end
10
+ attr_reader :ordering
11
+
12
+ def on_set_operator(sexpr)
13
+ call(builder.from_self(sexpr))
14
+ end
15
+ alias :on_union :on_set_operator
16
+ alias :on_except :on_set_operator
17
+ alias :on_intersect :on_set_operator
18
+
19
+ def on_select_exp(sexpr)
20
+ if obc = sexpr.order_by_clause
21
+ sexpr = builder.from_self(sexpr)
22
+ call(sexpr)
23
+ else
24
+ needed = builder.order_by_clause(ordering, &sexpr.desaliaser)
25
+ sexpr.dup.push(needed)
26
+ end
27
+ end
28
+
29
+ end # class OrderBy
30
+ end # class Processor
31
+ end # module Sql
32
+ end # module Bmg
@@ -0,0 +1,25 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class Rename < Processor
5
+
6
+ def initialize(renaming, builder)
7
+ super(builder)
8
+ @renaming = renaming
9
+ end
10
+
11
+ def on_select_list(sexpr)
12
+ sexpr.each_with_index.map{|child,index|
13
+ index == 0 ? child : apply(child)
14
+ }
15
+ end
16
+
17
+ def on_select_item(sexpr)
18
+ return sexpr unless newname = @renaming[sexpr.as_name.to_sym]
19
+ builder.select_item(sexpr.qualifier, sexpr.would_be_name, newname.to_s)
20
+ end
21
+
22
+ end # class Rename
23
+ end # class Processor
24
+ end # module Sql
25
+ end # module Bmg
@@ -0,0 +1,21 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class Reorder < Processor
5
+
6
+ def initialize(attr_list, builder)
7
+ super(builder)
8
+ @indexes = Hash[attr_list.to_a.map(&:to_s).each_with_index.to_a]
9
+ end
10
+
11
+ def on_select_list(sexpr)
12
+ reordered = sexpr.sexpr_body.sort{|i1,i2|
13
+ @indexes[i1.as_name] <=> @indexes[i2.as_name]
14
+ }
15
+ reordered.unshift(:select_list)
16
+ end
17
+
18
+ end # class Reorder
19
+ end # class Processor
20
+ end # module Sql
21
+ end # module Bmg
@@ -0,0 +1,24 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class Requalify < Processor
5
+
6
+ def initialize(builder)
7
+ super
8
+ @requalify = Hash.new{|h,k|
9
+ h[k] = Grammar.sexpr [:range_var_name, builder.next_qualifier!]
10
+ }
11
+ end
12
+ attr_reader :requalify
13
+
14
+ alias :on_select_exp :copy_and_apply
15
+ alias :on_missing :copy_and_apply
16
+
17
+ def on_range_var_name(sexpr)
18
+ requalify[sexpr]
19
+ end
20
+
21
+ end # class Requalify
22
+ end # class Processor
23
+ end # module Sql
24
+ end # module Bmg
@@ -0,0 +1,68 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class SemiJoin < Processor
5
+ include JoinSupport
6
+
7
+ def initialize(right, on, negate = false, builder)
8
+ super(builder)
9
+ @right = right
10
+ @on = on
11
+ @negate = negate
12
+ end
13
+ attr_reader :right, :on, :negate
14
+
15
+ def call(sexpr)
16
+ if sexpr.set_operator?
17
+ call(builder.from_self(sexpr))
18
+ elsif right.set_operator?
19
+ SemiJoin.new(builder.from_self(right), negate, builder).call(sexpr)
20
+ else
21
+ super(sexpr)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def apply_join_strategy(left, right)
28
+ predicate = build_semijoin_predicate(left, right)
29
+ expand_where_clause(left, negate ? !predicate : predicate)
30
+ end
31
+
32
+ def build_semijoin_predicate(left, right)
33
+ if right.is_table_dee?
34
+ right.where_clause.predicate
35
+ else
36
+ commons = self.on
37
+ subquery = Clip.new(commons, false, :star, builder).call(right)
38
+ subquery = Requalify.new(builder).call(subquery)
39
+ if commons.size == 0
40
+ builder.exists(subquery)
41
+ elsif commons.size == 1
42
+ identifier = left.desaliaser[commons.to_a.first]
43
+ Predicate::Factory.in(identifier, subquery)
44
+ else
45
+ join_pre = join_predicate(left, subquery, commons)
46
+ subquery = expand_where_clause(subquery, join_pre)
47
+ subquery = Star.new(builder).call(subquery)
48
+ builder.exists(subquery)
49
+ end
50
+ end
51
+ end
52
+
53
+ def expand_where_clause(sexpr, predicate)
54
+ Grammar.sexpr \
55
+ [ :select_exp,
56
+ sexpr.set_quantifier,
57
+ sexpr.select_list,
58
+ sexpr.from_clause,
59
+ [ :where_clause, (sexpr.predicate || tautology) & predicate ],
60
+ sexpr.order_by_clause,
61
+ sexpr.limit_clause,
62
+ sexpr.offset_clause ].compact
63
+ end
64
+
65
+ end # class SemiJoin
66
+ end # class Processor
67
+ end # module Sql
68
+ end # module Bmg
@@ -0,0 +1,17 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class Star < Processor
5
+
6
+ def on_select_exp(sexpr)
7
+ if sexpr.from_clause
8
+ sexpr.with_update(:select_list, builder.select_star)
9
+ else
10
+ sexpr
11
+ end
12
+ end
13
+
14
+ end # class Star
15
+ end # class Processor
16
+ end # module Sql
17
+ end # module Bmg