bmg 0.16.0.pre.rc1 → 0.16.0.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bmg.rb +1 -0
- data/lib/bmg/algebra.rb +17 -1
- data/lib/bmg/algebra/shortcuts.rb +27 -11
- data/lib/bmg/error.rb +4 -0
- data/lib/bmg/operator.rb +6 -44
- data/lib/bmg/operator/autowrap.rb +80 -8
- data/lib/bmg/operator/join.rb +19 -0
- data/lib/bmg/operator/shared/binary.rb +22 -0
- data/lib/bmg/operator/shared/nary.rb +19 -0
- data/lib/bmg/operator/shared/unary.rb +21 -0
- data/lib/bmg/operator/summarize.rb +77 -0
- data/lib/bmg/sequel/translator.rb +16 -1
- data/lib/bmg/sql/builder.rb +8 -0
- data/lib/bmg/sql/grammar.rb +2 -0
- data/lib/bmg/sql/grammar.sexp.yml +11 -0
- data/lib/bmg/sql/nodes/group_by_clause.rb +20 -0
- data/lib/bmg/sql/nodes/select_exp.rb +4 -0
- data/lib/bmg/sql/nodes/summarizer.rb +23 -0
- data/lib/bmg/sql/processor.rb +1 -0
- data/lib/bmg/sql/processor/summarize.rb +48 -0
- data/lib/bmg/sql/processor/where.rb +10 -5
- data/lib/bmg/sql/relation.rb +17 -0
- data/lib/bmg/sql/support/from_clause_orderer.rb +129 -39
- data/lib/bmg/summarizer.rb +132 -0
- data/lib/bmg/summarizer/avg.rb +36 -0
- data/lib/bmg/summarizer/collect.rb +31 -0
- data/lib/bmg/summarizer/concat.rb +42 -0
- data/lib/bmg/summarizer/count.rb +31 -0
- data/lib/bmg/summarizer/max.rb +31 -0
- data/lib/bmg/summarizer/min.rb +31 -0
- data/lib/bmg/summarizer/stddev.rb +26 -0
- data/lib/bmg/summarizer/sum.rb +31 -0
- data/lib/bmg/summarizer/variance.rb +42 -0
- data/lib/bmg/type.rb +11 -0
- data/lib/bmg/version.rb +1 -1
- metadata +34 -3
@@ -42,6 +42,7 @@ module Bmg
|
|
42
42
|
#
|
43
43
|
selection = apply(sexpr.select_list)
|
44
44
|
predicate = apply(sexpr.predicate) if sexpr.predicate
|
45
|
+
grouping = apply(sexpr.group_by_clause) if sexpr.group_by_clause
|
45
46
|
order = apply(sexpr.order_by_clause) if sexpr.order_by_clause
|
46
47
|
limit = apply(sexpr.limit_clause) if sexpr.limit_clause
|
47
48
|
offset = apply(sexpr.offset_clause) if sexpr.offset_clause
|
@@ -49,6 +50,7 @@ module Bmg
|
|
49
50
|
dataset = dataset.select(*selection)
|
50
51
|
dataset = dataset.distinct if sexpr.distinct?
|
51
52
|
dataset = dataset.where(predicate) if predicate
|
53
|
+
dataset = dataset.group(grouping) if grouping
|
52
54
|
dataset = dataset.order_by(*order) if order
|
53
55
|
dataset = dataset.limit(limit, offset == 0 ? nil : offset) if limit or offset
|
54
56
|
dataset
|
@@ -68,13 +70,21 @@ module Bmg
|
|
68
70
|
case kind = sexpr.left.first
|
69
71
|
when :qualified_name
|
70
72
|
left.column == right.value ? left : ::Sequel.as(left, right)
|
71
|
-
when :literal
|
73
|
+
when :literal, :summarizer
|
72
74
|
::Sequel.as(left, right)
|
73
75
|
else
|
74
76
|
raise NotImplementedError, "Unexpected select item `#{kind}`"
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
80
|
+
def on_summarizer(sexpr)
|
81
|
+
if sexpr.summary_expr
|
82
|
+
::Sequel.function(sexpr.summary_func, apply(sexpr.summary_expr))
|
83
|
+
else
|
84
|
+
::Sequel.function(sexpr.summary_func).*
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
78
88
|
def on_qualified_name(sexpr)
|
79
89
|
apply(sexpr.last).qualify(sexpr.qualifier)
|
80
90
|
end
|
@@ -116,6 +126,11 @@ module Bmg
|
|
116
126
|
::Sequel.as(apply(sexpr.subquery), ::Sequel.identifier(sexpr.as_name))
|
117
127
|
end
|
118
128
|
|
129
|
+
def on_group_by_clause(sexpr)
|
130
|
+
return nil unless sexpr.size > 1
|
131
|
+
sexpr.sexpr_body.map{|c| apply(c)}
|
132
|
+
end
|
133
|
+
|
119
134
|
def on_order_by_clause(sexpr)
|
120
135
|
sexpr.sexpr_body.map{|c| apply(c)}
|
121
136
|
end
|
data/lib/bmg/sql/builder.rb
CHANGED
@@ -146,6 +146,14 @@ module Bmg
|
|
146
146
|
Predicate::Grammar.sexpr [ :exists, subquery ]
|
147
147
|
end
|
148
148
|
|
149
|
+
def group_by_clause(attrlist, &desaliaser)
|
150
|
+
attrlist.map{|name|
|
151
|
+
name = name.to_s
|
152
|
+
(desaliaser && desaliaser[name]) || column_name(name)
|
153
|
+
}.unshift(:group_by_clause)
|
154
|
+
end
|
155
|
+
builder :group_by_clause
|
156
|
+
|
149
157
|
def order_by_clause(ordering, &desaliaser)
|
150
158
|
ordering.to_a.map{|(name,direction)|
|
151
159
|
name = name.to_s
|
data/lib/bmg/sql/grammar.rb
CHANGED
@@ -30,6 +30,7 @@ require_relative "nodes/table_as"
|
|
30
30
|
require_relative "nodes/native_table_as"
|
31
31
|
require_relative "nodes/subquery_as"
|
32
32
|
require_relative "nodes/table_name"
|
33
|
+
require_relative "nodes/group_by_clause"
|
33
34
|
require_relative "nodes/order_by_clause"
|
34
35
|
require_relative "nodes/order_by_term"
|
35
36
|
require_relative "nodes/limit_clause"
|
@@ -43,3 +44,4 @@ require_relative "nodes/name_intro"
|
|
43
44
|
require_relative "nodes/where_clause"
|
44
45
|
require_relative "nodes/cross_join"
|
45
46
|
require_relative "nodes/inner_join"
|
47
|
+
require_relative "nodes/summarizer"
|
@@ -35,6 +35,7 @@ rules:
|
|
35
35
|
[ select_list, select_star ],
|
36
36
|
from_clause,
|
37
37
|
where_clause,
|
38
|
+
group_by_clause,
|
38
39
|
order_by_clause,
|
39
40
|
limit_clause,
|
40
41
|
offset_clause ]
|
@@ -50,6 +51,8 @@ rules:
|
|
50
51
|
- [ table_spec ]
|
51
52
|
where_clause:
|
52
53
|
- [ predicate ]
|
54
|
+
group_by_clause:
|
55
|
+
- [ a_name ]
|
53
56
|
order_by_clause:
|
54
57
|
- [ order_by_term+ ]
|
55
58
|
order_by_term:
|
@@ -72,11 +75,19 @@ rules:
|
|
72
75
|
scalar_exp:
|
73
76
|
- qualified_name
|
74
77
|
- column_name
|
78
|
+
- summarizer
|
75
79
|
- literal
|
80
|
+
a_name:
|
81
|
+
- qualified_name
|
82
|
+
- column_name
|
76
83
|
qualified_name:
|
77
84
|
- [ range_var_name, column_name ]
|
78
85
|
column_name:
|
79
86
|
- [ name_rgx ]
|
87
|
+
summarizer:
|
88
|
+
- [ summary_func, qualified_name ]
|
89
|
+
summary_func:
|
90
|
+
- "::Symbol"
|
80
91
|
table_name:
|
81
92
|
- [ name_rgx ]
|
82
93
|
range_var_name:
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Bmg
|
2
|
+
module Sql
|
3
|
+
module GroupByClause
|
4
|
+
include Expr
|
5
|
+
|
6
|
+
GROUP_BY = "GROUP BY".freeze
|
7
|
+
|
8
|
+
def to_sql(buffer, dialect)
|
9
|
+
return buffer if size == 1
|
10
|
+
buffer << GROUP_BY << SPACE
|
11
|
+
each_child do |item,index|
|
12
|
+
buffer << COMMA << SPACE unless index == 0
|
13
|
+
item.to_sql(buffer, dialect)
|
14
|
+
end
|
15
|
+
buffer
|
16
|
+
end
|
17
|
+
|
18
|
+
end # module GroupByClause
|
19
|
+
end # module Sql
|
20
|
+
end # module Bmg
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Bmg
|
2
|
+
module Sql
|
3
|
+
module Summarizer
|
4
|
+
include Expr
|
5
|
+
|
6
|
+
def summary_func
|
7
|
+
self[1]
|
8
|
+
end
|
9
|
+
|
10
|
+
def summary_expr
|
11
|
+
self.last
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_sql(buffer, dialect)
|
15
|
+
buffer << summary_func.upcase << "("
|
16
|
+
summary_expr.to_sql(buffer, dialect)
|
17
|
+
buffer << ")"
|
18
|
+
buffer
|
19
|
+
end
|
20
|
+
|
21
|
+
end # module Summarizer
|
22
|
+
end # module Sql
|
23
|
+
end # module Bmg
|
data/lib/bmg/sql/processor.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Bmg
|
2
|
+
module Sql
|
3
|
+
class Processor
|
4
|
+
class Summarize < Processor
|
5
|
+
|
6
|
+
def initialize(by, summarization, builder)
|
7
|
+
super(builder)
|
8
|
+
@by = by
|
9
|
+
@summarization = summarization
|
10
|
+
end
|
11
|
+
attr_reader :by, :summarization
|
12
|
+
|
13
|
+
def on_set_operator(sexpr)
|
14
|
+
call(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
|
+
if obc = sexpr.group_by_clause
|
22
|
+
sexpr = builder.from_self(sexpr)
|
23
|
+
call(sexpr)
|
24
|
+
else
|
25
|
+
sexpr = sexpr.with_update(:select_list, apply(sexpr.select_list))
|
26
|
+
group_by = builder.group_by_clause(by, &sexpr.desaliaser)
|
27
|
+
sexpr.push(group_by)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_select_list(sexpr)
|
32
|
+
by_list = sexpr.sexpr_body.select{|select_item|
|
33
|
+
by.include?(select_item.last.last.to_sym)
|
34
|
+
}
|
35
|
+
group_list = summarization.map{|attr,summarizer|
|
36
|
+
[:select_item,
|
37
|
+
[ :summarizer,
|
38
|
+
summarizer.to_summarizer_name,
|
39
|
+
sexpr.desaliaser[attr] ],
|
40
|
+
[:column_name, attr.to_s] ]
|
41
|
+
}
|
42
|
+
[:select_list] + by_list + group_list
|
43
|
+
end
|
44
|
+
|
45
|
+
end # class Summarize
|
46
|
+
end # class Processor
|
47
|
+
end # module Sql
|
48
|
+
end # module Bmg
|
@@ -20,12 +20,17 @@ module Bmg
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def on_select_exp(sexpr)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
sexpr.with_update(:where_clause, [ :where_clause, (sexpr_p & pred).sexpr ])
|
23
|
+
if sexpr.group_by_clause
|
24
|
+
sexpr = builder.from_self(sexpr)
|
25
|
+
call(sexpr)
|
27
26
|
else
|
28
|
-
|
27
|
+
pred = @predicate.rename(sexpr.desaliaser(true))
|
28
|
+
if sexpr.where_clause
|
29
|
+
sexpr_p = Predicate.new(sexpr.where_clause.predicate)
|
30
|
+
sexpr.with_update(:where_clause, [ :where_clause, (sexpr_p & pred).sexpr ])
|
31
|
+
else
|
32
|
+
sexpr.with_insert(4, [ :where_clause, pred.sexpr ])
|
33
|
+
end
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
data/lib/bmg/sql/relation.rb
CHANGED
@@ -114,6 +114,23 @@ module Bmg
|
|
114
114
|
_instance(type, builder, expr)
|
115
115
|
end
|
116
116
|
|
117
|
+
def _summarize(type, by, summarization)
|
118
|
+
summarization = Operator::Summarize.compile(summarization)
|
119
|
+
if can_compile_summarization?(summarization)
|
120
|
+
expr = before_use(self.expr)
|
121
|
+
expr = Processor::Summarize.new(by, summarization, builder).call(self.expr)
|
122
|
+
_instance(type, builder, expr)
|
123
|
+
else
|
124
|
+
super
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def can_compile_summarization?(summarization)
|
129
|
+
summarization.values.all?{|s|
|
130
|
+
[:avg, :count, :max, :min, :sum].include?(s.to_summarizer_name)
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
117
134
|
def _union(type, right, options)
|
118
135
|
if right_expr = extract_compatible_sexpr(right)
|
119
136
|
expr = before_use(self.expr)
|
@@ -3,51 +3,140 @@ module Bmg
|
|
3
3
|
module Support
|
4
4
|
class FromClauseOrderer
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# Given a `from_clause` AST as input, e.g.
|
7
|
+
#
|
8
|
+
# [ :from_clause,
|
9
|
+
# [ :cross_join
|
10
|
+
# [ :inner_join,
|
11
|
+
# [ :inner_join,
|
12
|
+
# [ :table_as, "suppliers", "s" ],
|
13
|
+
# [ :table_as, "supplies", "sp" ],
|
14
|
+
# [ :eq, [ :qualified, "s", "sid" ], [ :qualified, "sp", "sid" ] ]
|
15
|
+
# ],
|
16
|
+
# [ :table_as, "parts", "p" ],
|
17
|
+
# [ :eq, [ :qualified, "p", "pid" ], [ :qualified "sp", "pid" ] ]
|
18
|
+
# ],
|
19
|
+
# [ :table_as, "cities", "c" ],
|
20
|
+
# ]
|
21
|
+
# ]
|
22
|
+
#
|
23
|
+
# Generates a relationally equivalent list of (type,table,predicate)
|
24
|
+
# triplets, where:
|
8
25
|
#
|
9
26
|
# - type is :base, :cross_join or :inner_join
|
10
27
|
# - table is table_as, native_table_as or subquery_as
|
11
|
-
# - predicate is a join predicate
|
28
|
+
# - predicate is a join predicate `ti.attri = tj.attrj AND ...`
|
29
|
+
#
|
30
|
+
# So that
|
31
|
+
#
|
32
|
+
# 1) the types are observed in strict increasing order (one :base, zero
|
33
|
+
# or more :cross_join, zero or more :inner_join)
|
34
|
+
#
|
35
|
+
# 2) the list is such that it can be safely written as an expression
|
36
|
+
# of the following SQL syntax:
|
37
|
+
#
|
38
|
+
# t1 # [ :base, t1, nil ]
|
39
|
+
# cross_join t2 # [ :cross_join, t2, nil ]
|
40
|
+
# cross_join t3 # [ :cross_join, t3, nil ]
|
41
|
+
# inner_join t4 ON p4 # [ :inner_join, t4, p4 ]
|
42
|
+
# inner_join t5 ON p5 # [ :inner_join, t5, p5 ]
|
43
|
+
#
|
44
|
+
# that is, the linearization is correct only if each predicate `pi`
|
45
|
+
# only makes reference to tables introduced before it (no forward
|
46
|
+
# reference).
|
12
47
|
#
|
13
|
-
#
|
14
|
-
# (one :base, zero or more :cross_join, zero or more
|
15
|
-
# :inner_join). The list is such that it can be safely
|
16
|
-
# written as an expression of the following SQL form:
|
48
|
+
# For the example above, a solution might be:
|
17
49
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
50
|
+
# [
|
51
|
+
# [ :base, [ :table_as, "suppliers", "s" ], nil ],
|
52
|
+
# [ :cross_join, [ :table_as, "cities", "c" ], nil ],
|
53
|
+
# [ :inner_join, [ :table_as, "supplies", "sp" ],
|
54
|
+
# [ :eq, [ :qualified, "s", "sid" ], [ :qualified, "sp", "sid" ] ] ],
|
55
|
+
# [ :inner_join, [ :table_as, "parts", "p" ],
|
56
|
+
# [ :eq, [ :qualified, "p", "pid" ], [ :qualified "sp", "pid" ] ] ]
|
57
|
+
# ]
|
23
58
|
#
|
24
|
-
# A NotImplementedError may be raised if no linearization can
|
25
|
-
# be found.
|
59
|
+
# A NotImplementedError may be raised if no linearization can be found.
|
26
60
|
#
|
27
61
|
def call(sexpr)
|
62
|
+
# The algorithm works in two phases: we first collect all table
|
63
|
+
# references and JOIN clauses by simple inspection of the AST.
|
28
64
|
tables, joins = collect(sexpr)
|
65
|
+
|
66
|
+
# Then we order the tables and join clauses so as to find the
|
67
|
+
# linearization.
|
29
68
|
order_all(tables, joins)
|
30
69
|
end
|
31
70
|
|
32
|
-
protected
|
71
|
+
protected ## Second phase: linearization per se
|
33
72
|
|
34
|
-
|
73
|
+
# Given a non empty list of tables `ti` and a possibly empty list of
|
74
|
+
# join conditions `ti.attri = tj.attrj`, returns a linearization of
|
75
|
+
# join triplets meeting the following POST conditions:
|
76
|
+
#
|
77
|
+
# 1. A triplet is either `[ :base, ti, nil ]`, `[:cross_join, ti, nil]`
|
78
|
+
# or `[ :inner_join, ti, AND([eq]) ]` where `eq` is of the form
|
79
|
+
# `ti.attri = tj.attrj`
|
80
|
+
#
|
81
|
+
# 2. one and only on `:base` triplet comes first, then `:cross_join`
|
82
|
+
# ones, then `:inner_join` ones.
|
83
|
+
#
|
84
|
+
# 3. an inner clause at position x in the resulting list is such that
|
85
|
+
# its join conditions `eq` only make reference to tables `ti` that
|
86
|
+
# have been introduced before or in x itself (i.e. no forward
|
87
|
+
# reference to tables not introduced yet)
|
88
|
+
#
|
89
|
+
def order_all(tables, joins)
|
90
|
+
# Our first strategy is simple: let sort the tables by moving the ones
|
91
|
+
# not referenced in join clauses at the beginning of the list => they
|
92
|
+
# will yield the base an cross join clauses first.
|
93
|
+
tables = tables.sort{|t1,t2|
|
94
|
+
t1js = joins.select{|j| uses?(j, t1) }.size
|
95
|
+
t2js = joins.select{|j| uses?(j, t2) }.size
|
96
|
+
t1js == 0 ? (t2js == 0 ? 0 : -1) : (t2js == 0 ? 1 : 0)
|
97
|
+
}
|
98
|
+
|
99
|
+
# Then order all recursively in that order of tables, filling a result
|
100
|
+
# array that will be returned
|
101
|
+
_order_all(tables, joins, [])
|
102
|
+
end
|
103
|
+
|
104
|
+
def _order_all(tables, joins, result)
|
35
105
|
if tables.empty? and joins.empty?
|
106
|
+
# end or recusion
|
36
107
|
result
|
37
108
|
elsif tables.empty?
|
109
|
+
# Why will this never happen exactly??
|
38
110
|
raise NotImplementedError, "Orphan joins: `#{joins.inspect}`"
|
39
111
|
else
|
112
|
+
# Greedy strategy: we take the first table in the list and keep the
|
113
|
+
# rest for recursion later
|
40
114
|
table, tables_tail = tables.first, tables[1..-1]
|
115
|
+
|
116
|
+
# Split the remaining joins in two lists: those referencing only
|
117
|
+
# introduced tables, and those making forward references
|
41
118
|
on, joins_tail = split_joins(joins, table, tables_tail)
|
119
|
+
|
120
|
+
# Decide which kind of join it is, according to the result and
|
121
|
+
# the number of join clauses that will be used
|
42
122
|
join_kind = result.empty? ? :base : (on.empty? ? :cross_join : :inner_join)
|
123
|
+
|
124
|
+
# Compute the AND([eq]) predicate on selected join clauses
|
43
125
|
predicate = on.inject(nil){|p,clause|
|
44
126
|
p.nil? ? clause : Predicate::Factory.and(p, clause)
|
45
127
|
}
|
128
|
+
|
129
|
+
# Recurse with that new clause in the result
|
46
130
|
clause = [ join_kind, table, predicate ]
|
47
|
-
|
131
|
+
_order_all(tables_tail, joins_tail, result + [clause])
|
48
132
|
end
|
49
133
|
end
|
50
134
|
|
135
|
+
# Given a list of join `ti.attri = tj.attrj` clauses, a newly introduced
|
136
|
+
# ti `table`, and a set of non-yet-introduced tables `tables_tail`,...
|
137
|
+
#
|
138
|
+
# ... split the joins in two sublists: those making references to table
|
139
|
+
# `ti` and making no reference to non introduced tables, and the others.
|
51
140
|
def split_joins(joins, table, tables_tail)
|
52
141
|
joins.partition{|j|
|
53
142
|
uses?(j, table) && !tables_tail.find{|t|
|
@@ -56,17 +145,34 @@ module Bmg
|
|
56
145
|
}
|
57
146
|
end
|
58
147
|
|
59
|
-
|
148
|
+
# Returns whether the join
|
149
|
+
def uses?(condition, table)
|
150
|
+
name = table.as_name.to_s
|
151
|
+
left_name = var_name(condition[1])
|
152
|
+
right_name = var_name(condition[2])
|
153
|
+
(left_name == name) or (right_name == name)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Given a `ti.attri` expression (AST node), returns `ti`
|
157
|
+
def var_name(qualified)
|
158
|
+
case qualified.first
|
159
|
+
when :qualified_identifier then qualified[1].to_s
|
160
|
+
when :qualified_name then qualified[1][1].to_s
|
161
|
+
else
|
162
|
+
raise NotImplementedError, "Unexpected qualified name `#{qualified.inspect}`"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
protected ## First phase: collection of tables and join clauses
|
60
167
|
|
168
|
+
# Given a `from_clause` AST (see grammar.sexp.yml), returns two
|
169
|
+
# lists:
|
170
|
+
# - one with tables `ti` (`table_as`, `native_table_as` & `subquery_as`)
|
171
|
+
# - another one with all equality conditions of the form `ti.attri = tj.attrj`
|
61
172
|
def collect(sexpr)
|
62
173
|
tables = []
|
63
174
|
joins = []
|
64
175
|
_collect(sexpr, tables, joins)
|
65
|
-
tables.sort!{|t1,t2|
|
66
|
-
t1js = joins.select{|j| uses?(j, t1) }.size
|
67
|
-
t2js = joins.select{|j| uses?(j, t2) }.size
|
68
|
-
t1js == 0 ? (t2js == 0 ? 0 : -1) : (t2js == 0 ? 1 : 0)
|
69
|
-
}
|
70
176
|
[ tables, joins ]
|
71
177
|
end
|
72
178
|
|
@@ -98,22 +204,6 @@ module Bmg
|
|
98
204
|
end
|
99
205
|
end
|
100
206
|
|
101
|
-
def uses?(join, table)
|
102
|
-
name = table.as_name.to_s
|
103
|
-
left_name = var_name(join[1])
|
104
|
-
right_name = var_name(join[2])
|
105
|
-
(left_name == name) or (right_name == name)
|
106
|
-
end
|
107
|
-
|
108
|
-
def var_name(qualified)
|
109
|
-
case qualified.first
|
110
|
-
when :qualified_identifier then qualified[1].to_s
|
111
|
-
when :qualified_name then qualified[1][1].to_s
|
112
|
-
else
|
113
|
-
raise NotImplementedError, "Unexpected qualified name `#{qualified.inspect}`"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
207
|
end # class FromClauseOrderer
|
118
208
|
end # module Support
|
119
209
|
end # module Sql
|