spiderfw 0.6.19 → 0.6.20
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/VERSION +1 -1
- data/apps/config_editor/_init.rb +1 -2
- data/apps/config_editor/controllers/config_editor_controller.rb +1 -7
- data/apps/core/components/widgets/table/table.rb +5 -3
- data/apps/core/forms/widgets/form/form.rb +3 -1
- data/lib/spiderfw/config/options/spider.rb +1 -2
- data/lib/spiderfw/model/condition.rb +3 -3
- data/lib/spiderfw/model/mappers/db_mapper.rb +67 -21
- data/lib/spiderfw/model/mappers/mapper.rb +5 -2
- data/lib/spiderfw/model/mixins/versioned.rb +3 -0
- data/lib/spiderfw/model/query_funcs.rb +51 -0
- data/lib/spiderfw/model/storage/db/adapters/oracle.rb +27 -2
- data/lib/spiderfw/model/storage/db/connectors/oci8.rb +2 -2
- data/lib/spiderfw/model/storage/db/db_schema.rb +29 -0
- data/lib/spiderfw/model/storage/db/db_storage.rb +43 -18
- metadata +4 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
= 0.6.20
|
2
|
+
== 05 October, 2011
|
3
|
+
* Added support for aggregate functions in DB conditions
|
4
|
+
* Added after_load event to Form widget to allow manipulation of the loaded object
|
5
|
+
* Fixed error Table Widget when paginate attribute is set to false
|
6
|
+
* Improved DB join generation
|
7
|
+
|
1
8
|
= 0.6.19
|
2
9
|
== 21 September, 2011
|
3
10
|
* Fixes: DbMapper join logic, configuration editor, js files compilation, Rack set_body_io (used by SOAP)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.20
|
data/apps/config_editor/_init.rb
CHANGED
@@ -11,5 +11,4 @@ Spider::Template.register_namespace('config_editor', Spider::ConfigEditor)
|
|
11
11
|
|
12
12
|
|
13
13
|
require 'apps/config_editor/controllers/config_editor_controller'
|
14
|
-
require 'apps/config_editor/widgets/edit/edit'
|
15
|
-
require 'apps/config_editor/widgets/edit_bool/edit_bool'
|
14
|
+
require 'apps/config_editor/widgets/edit/edit'
|
@@ -75,13 +75,7 @@ module Spider; module ConfigEditor
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def create_edit_widget(key, option)
|
78
|
-
|
79
|
-
when 'Spider::Bool', 'Spider::DataTypes::Bool'
|
80
|
-
EditBool
|
81
|
-
else
|
82
|
-
Edit
|
83
|
-
end
|
84
|
-
w = wclass.new(@request, @response, @scene)
|
78
|
+
w = Edit.new(@request, @response, @scene)
|
85
79
|
w.attributes[:name] = key
|
86
80
|
w.attributes[:option] = option
|
87
81
|
w.widget_init
|
@@ -95,7 +95,7 @@ module Spider; module Components
|
|
95
95
|
end
|
96
96
|
@rows = prepare_queryset(@queryset ? @queryset : @model.list)
|
97
97
|
@rows.condition.and(self.condition) if self.condition && !self.condition.empty?
|
98
|
-
if
|
98
|
+
if @attributes[:paginate]
|
99
99
|
@rows.limit = @attributes[:row_limit]
|
100
100
|
@rows.offset = @offset
|
101
101
|
@scene.page = @page
|
@@ -111,8 +111,10 @@ module Spider; module Components
|
|
111
111
|
@scene.rows = prepare_rows(@rows)
|
112
112
|
@scene.data = @rows
|
113
113
|
@scene.has_more = @rows.has_more?
|
114
|
-
|
115
|
-
|
114
|
+
if @attributes[:paginate]
|
115
|
+
@scene.pages = (@rows.total_rows.to_f / @attributes[:row_limit]).ceil
|
116
|
+
@scene.paginate_last = [@scene.paginate_first + 9, @scene.pages].min
|
117
|
+
end
|
116
118
|
@scene.columns = @elements.size
|
117
119
|
super
|
118
120
|
end
|
@@ -181,8 +181,7 @@ module Spider
|
|
181
181
|
:do => lambda{ |val| ENV['http_proxy'] = val }
|
182
182
|
|
183
183
|
config_option 'resources.disable_custom', _("Disable resource overriding in home"), :type => Spider::Bool, :default => false
|
184
|
-
|
185
|
-
config_option 'migrations.window', _("Fetch window to use when migrating models"), :type => Fixnum, :default => 100
|
184
|
+
|
186
185
|
|
187
186
|
|
188
187
|
end
|
@@ -250,15 +250,15 @@ module Spider; module Model
|
|
250
250
|
cnt += 1
|
251
251
|
comparison = @comparisons[key] || '='
|
252
252
|
cond = "#{comparison} #{value.inspect}"
|
253
|
-
str += "#{key} #{cond}"
|
253
|
+
str += "#{key.inspect} #{cond}"
|
254
254
|
end
|
255
|
-
str = '(' + str + ')' if str.length > 0
|
256
255
|
#str += ' [raw:'+raw.inspect+']' unless raw.empty?
|
257
256
|
first = true
|
258
257
|
if @subconditions.length > 0
|
259
258
|
str += ' '+@conjunction.to_s+' ' if str.length > 0
|
260
|
-
str += @subconditions.map{ |sub| sub.inspect }.join(' '+@conjunction.to_s+' ')
|
259
|
+
str += @subconditions.map{ |sub| sub.inspect }.reject{ |sub| sub.empty? }.join(' '+@conjunction.to_s+' ')
|
261
260
|
end
|
261
|
+
str = "(#{str})" if cnt + @subconditions.length > 1
|
262
262
|
return str
|
263
263
|
end
|
264
264
|
|
@@ -494,7 +494,13 @@ module Spider; module Model; module Mappers
|
|
494
494
|
cond[:values] = []
|
495
495
|
|
496
496
|
|
497
|
-
# Returns an hash
|
497
|
+
# Returns an hash with true for elements that need an inner join
|
498
|
+
# Note: subsequent joins of a left join have to be left joins,
|
499
|
+
# otherwise the first join will behave as an inner.
|
500
|
+
# Maybe they should be marked and later made behave like an inner with an
|
501
|
+
# appropriate (A.key is null or B.key is not null) condition.
|
502
|
+
# Not sure if this is needed, since first level conditions should filter out
|
503
|
+
# any unwanted results.
|
498
504
|
def get_join_info(model, condition)
|
499
505
|
join_info = {}
|
500
506
|
condition.each_with_comparison do |k, v, comp|
|
@@ -524,21 +530,27 @@ module Spider; module Model; module Mappers
|
|
524
530
|
end
|
525
531
|
end
|
526
532
|
end
|
527
|
-
|
533
|
+
res = {}
|
534
|
+
keys = join_info.keys
|
535
|
+
sub_join_infos = [join_info]
|
528
536
|
condition.subconditions.each do |sub_cond|
|
529
537
|
next if sub_cond.empty?
|
530
538
|
sub_join_info = get_join_info(model, sub_cond)
|
531
|
-
sub_join_info.
|
539
|
+
keys += sub_join_info.keys
|
540
|
+
sub_join_infos << sub_join_info
|
541
|
+
end
|
542
|
+
keys.uniq!
|
543
|
+
sub_join_infos.each do |sub_join_info|
|
544
|
+
keys.each do |k|
|
532
545
|
if condition.conjunction == :or
|
533
|
-
|
534
|
-
|
546
|
+
res[k] = true if sub_join_info[k] && res[k] != false
|
547
|
+
res[k] = false unless sub_join_info[k]
|
535
548
|
else
|
536
|
-
|
549
|
+
res[k] = true if sub_join_info[k]
|
537
550
|
end
|
538
551
|
end
|
539
552
|
end
|
540
|
-
|
541
|
-
join_info
|
553
|
+
res
|
542
554
|
end
|
543
555
|
|
544
556
|
|
@@ -546,12 +558,32 @@ module Spider; module Model; module Mappers
|
|
546
558
|
join_info = options[:join_info]
|
547
559
|
join_info ||= get_join_info(@model, condition)
|
548
560
|
|
561
|
+
def has_aggregates?(condition, in_or=false)
|
562
|
+
condition.each_with_comparison do |k, v, comp|
|
563
|
+
return true if k.is_a?(QueryFuncs::Function) && k.has_aggregates?
|
564
|
+
end
|
565
|
+
in_or = true if condition.conjunction == :or
|
566
|
+
condition.subconditions.each do |sub_cond|
|
567
|
+
return true if in_or && has_aggregates?(sub_cond, in_or)
|
568
|
+
end
|
569
|
+
return false
|
570
|
+
end
|
571
|
+
|
572
|
+
is_having = options[:is_having]
|
573
|
+
is_having = has_aggregates?(condition) if is_having.nil?
|
574
|
+
|
575
|
+
cond[:group_by_fields] ||= [] if is_having
|
549
576
|
|
550
577
|
condition.each_with_comparison do |k, v, comp|
|
551
578
|
if k.is_a?(QueryFuncs::Function)
|
552
579
|
field = prepare_queryfunc(k)
|
553
580
|
cond[:values] << [field, comp, v]
|
554
581
|
joins += field.joins
|
582
|
+
if is_having
|
583
|
+
cond[:group_by_fields] += k.inner_elements.map{ |el_name, owner_func|
|
584
|
+
owner_func.mapper_fields[el_name.to_s]
|
585
|
+
}
|
586
|
+
end
|
555
587
|
next
|
556
588
|
end
|
557
589
|
element = model.elements[k.to_sym]
|
@@ -566,13 +598,14 @@ module Spider; module Model; module Mappers
|
|
566
598
|
if (v && model.mapper.have_references?(element.name) && v.select{ |key, value|
|
567
599
|
!element.model.elements[key] || !element.model.elements[key].primary_key? }.empty?)
|
568
600
|
# 1/n <-> 1 with only primary keys
|
569
|
-
element_cond = {:conj => 'AND', :values => []}
|
601
|
+
element_cond = {:conj => 'AND', :values => [], :is_having => is_having}
|
570
602
|
v.each_with_comparison do |el_k, el_v, el_comp|
|
571
|
-
field = model_schema.
|
603
|
+
field = model_schema.foreign_key_field(element.name, el_k)
|
572
604
|
el_comp ||= '='
|
573
605
|
op = el_comp
|
574
606
|
field_cond = [field, op, map_condition_value(element.model.elements[el_k.to_sym].type, el_v)]
|
575
607
|
element_cond[:values] << field_cond
|
608
|
+
cond[:group_by_fields] << field if is_having
|
576
609
|
end
|
577
610
|
cond[:values] << element_cond
|
578
611
|
else
|
@@ -599,23 +632,30 @@ module Spider; module Model; module Mappers
|
|
599
632
|
|
600
633
|
if v.nil? && comp == '='
|
601
634
|
el_model_schema = model_schema
|
602
|
-
element_cond = {:conj => 'AND', :values => []}
|
603
|
-
|
635
|
+
element_cond = {:conj => 'AND', :values => [], :is_having => is_having}
|
636
|
+
if model.mapper.have_references?(element.name)
|
604
637
|
el_name = element.name
|
605
638
|
el_model = element.model
|
606
|
-
|
639
|
+
elsif element.junction?
|
607
640
|
el_model = element.type
|
608
641
|
el_model_schema = element.model.mapper.schema
|
609
|
-
el_name = element.attributes[:junction_their_element]
|
642
|
+
el_name = element.attributes[:junction_their_element]
|
643
|
+
else
|
644
|
+
el_model = element.type
|
645
|
+
el_model_schema = el_model.mapper.schema
|
646
|
+
el_name = element.reverse
|
610
647
|
end
|
611
648
|
el_model.primary_keys.each do |k|
|
612
|
-
field = el_model_schema.
|
649
|
+
field = el_model_schema.foreign_key_field(el_name, k.name)
|
613
650
|
field_cond = [field, comp, map_condition_value(element.model.elements[k.name].type, nil)]
|
614
651
|
element_cond[:values] << field_cond
|
652
|
+
element_cond[:is_having] = is_having
|
653
|
+
cond[:group_by_fields] << field if is_having
|
615
654
|
end
|
616
655
|
cond[:values] << element_cond
|
617
656
|
elsif v
|
618
|
-
sub_condition, sub_joins = element.mapper.prepare_condition(v, :table => sub_join[:as],
|
657
|
+
sub_condition, sub_joins = element.mapper.prepare_condition(v, :table => sub_join[:as],
|
658
|
+
:joins => joins, :join_info => el_join_info, :is_having => is_having || nil)
|
619
659
|
sub_condition[:table] = sub_join[:as] if sub_join[:as]
|
620
660
|
joins = sub_joins
|
621
661
|
cond[:values] << sub_condition
|
@@ -627,7 +667,8 @@ module Spider; module Model; module Mappers
|
|
627
667
|
end
|
628
668
|
end
|
629
669
|
elsif(model_schema.field(element.name))
|
630
|
-
field = model_schema.
|
670
|
+
field = model_schema.field(element.name)
|
671
|
+
field = FieldInAliasedTable(field, options[:table]) if options[:table]
|
631
672
|
op = comp ? comp : '='
|
632
673
|
if (v.is_a?(Spider::QueryFuncs::Expression))
|
633
674
|
v_joins = prepare_expression(v)
|
@@ -636,13 +677,16 @@ module Spider; module Model; module Mappers
|
|
636
677
|
else
|
637
678
|
cond[:values] << [field, op, map_condition_value(model.elements[k.to_sym].type, v)]
|
638
679
|
end
|
680
|
+
cond[:group_by_fields] << field if is_having
|
639
681
|
end
|
640
|
-
|
682
|
+
|
641
683
|
end
|
684
|
+
cond[:is_having] = is_having
|
685
|
+
|
642
686
|
sub_sqls = []
|
643
687
|
sub_bind_values = []
|
644
688
|
condition.subconditions.each do |sub|
|
645
|
-
sub_res = self.prepare_condition(sub, :joins => joins, :join_info => join_info)
|
689
|
+
sub_res = self.prepare_condition(sub, :joins => joins, :join_info => join_info, :is_having => is_having || nil)
|
646
690
|
cond[:values] << sub_res[0]
|
647
691
|
joins = sub_res[1]
|
648
692
|
remaining_condition += sub_res[2]
|
@@ -766,9 +810,11 @@ module Spider; module Model; module Mappers
|
|
766
810
|
el_joins, el_model, el = get_deep_join(el_name)
|
767
811
|
joins += el_joins
|
768
812
|
owner_func.mapper_fields ||= {}
|
769
|
-
owner_func.mapper_fields[
|
813
|
+
owner_func.mapper_fields[el_name.to_s] = el_model.mapper.schema.field(el.name)
|
770
814
|
end
|
771
|
-
|
815
|
+
f = FieldFunction.new(storage.function(func), schema.table, joins)
|
816
|
+
f.aggregate = true if func.has_aggregates?
|
817
|
+
return f
|
772
818
|
end
|
773
819
|
|
774
820
|
# Takes a Spider::QueryFuncs::Expression, and associates the fields to the corresponding elements
|
@@ -442,7 +442,8 @@ module Spider; module Model
|
|
442
442
|
preprocess_condition(condition)
|
443
443
|
cascade = @model.elements_array.select{ |el| !el.integrated? && el.attributes[:delete_cascade] }
|
444
444
|
assocs = association_elements.select do |el|
|
445
|
-
!
|
445
|
+
!el.junction? && # done later from @model.referenced_by_junctions
|
446
|
+
(!storage.supports?(:delete_cascade) || !schema.cascade?(el.name)) # TODO: implement
|
446
447
|
end
|
447
448
|
curr = @model.where(condition) unless curr
|
448
449
|
before_delete(curr)
|
@@ -950,7 +951,9 @@ module Spider; module Model
|
|
950
951
|
condition.delete(k)
|
951
952
|
integrated_from = element.integrated_from
|
952
953
|
integrated_from_element = element.integrated_from_element
|
953
|
-
condition.
|
954
|
+
sub = condition.get_deep_obj
|
955
|
+
sub.set(integrated_from_element, c, v)
|
956
|
+
condition[integrated_from.name] = integrated_from.model.mapper.preprocess_condition(sub)
|
954
957
|
elsif element.junction? && !v.is_a?(BaseModel) && !v.is_a?(Hash) && !v.nil? # conditions on junction id don't make sense
|
955
958
|
condition.delete(k)
|
956
959
|
condition.set("#{k}.#{element.attributes[:junction_their_element]}", c, v)
|
@@ -112,6 +112,9 @@ module Spider; module Model
|
|
112
112
|
vmod.elements[el.name].attributes.delete(:condition)
|
113
113
|
end
|
114
114
|
if el.multiple? && !el.attributes[:junction] && !is_version_content
|
115
|
+
# Note: this creates a junction
|
116
|
+
# When the object is deleted, the junction will be deleted too since the object
|
117
|
+
# is not versioned
|
115
118
|
elh[:attributes].delete(:reverse)
|
116
119
|
elh[:attributes].delete(:add_reverse)
|
117
120
|
elh[:attributes].delete(:add_multiple_reverse)
|
@@ -78,6 +78,22 @@ module Spider; module QueryFuncs
|
|
78
78
|
return els
|
79
79
|
end
|
80
80
|
|
81
|
+
def aggregate?
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def has_aggregates?
|
86
|
+
return true if aggregate?
|
87
|
+
elements.each do |el|
|
88
|
+
return true if el.is_a?(Function) && el.has_aggregates?
|
89
|
+
end
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
"#{self.func_name.to_s.upcase}(#{elements.map{ |el| el.inspect }.join(', ')})"
|
95
|
+
end
|
96
|
+
|
81
97
|
end
|
82
98
|
|
83
99
|
class ZeroArityFunction < Function
|
@@ -133,6 +149,11 @@ module Spider; module QueryFuncs
|
|
133
149
|
end
|
134
150
|
|
135
151
|
class Subtract < BinaryFunction
|
152
|
+
|
153
|
+
def inspect
|
154
|
+
"#{@el1.inspect} - #{@el2.inspect}"
|
155
|
+
end
|
156
|
+
|
136
157
|
end
|
137
158
|
|
138
159
|
class Concat < NAryFunction
|
@@ -149,5 +170,35 @@ module Spider; module QueryFuncs
|
|
149
170
|
|
150
171
|
end
|
151
172
|
|
173
|
+
class AggregateFunction < UnaryFunction
|
174
|
+
|
175
|
+
def aggregate?
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
class Avg < AggregateFunction
|
183
|
+
end
|
184
|
+
|
185
|
+
class Count < AggregateFunction
|
186
|
+
end
|
187
|
+
|
188
|
+
class First < AggregateFunction
|
189
|
+
end
|
190
|
+
|
191
|
+
class Last < AggregateFunction
|
192
|
+
end
|
193
|
+
|
194
|
+
class Max < AggregateFunction
|
195
|
+
end
|
196
|
+
|
197
|
+
class Min < AggregateFunction
|
198
|
+
end
|
199
|
+
|
200
|
+
class Sum < AggregateFunction
|
201
|
+
end
|
202
|
+
|
152
203
|
|
153
204
|
end; end
|
@@ -140,11 +140,28 @@ module Spider; module Model; module Storage; module Db
|
|
140
140
|
end
|
141
141
|
keys = sql_keys(query)
|
142
142
|
tables_sql, tables_values = sql_tables(query)
|
143
|
+
|
143
144
|
sql = "SELECT #{keys} FROM #{tables_sql} "
|
144
145
|
bind_vars += tables_values
|
146
|
+
|
145
147
|
where, vals = sql_condition(query)
|
146
148
|
bind_vars += vals
|
147
149
|
sql += "WHERE #{where} " if where && !where.empty?
|
150
|
+
|
151
|
+
having, having_vals = sql_condition(query, true)
|
152
|
+
unless having.blank?
|
153
|
+
unless (query[:limit] || query[:query_type] == :count) && !query[:joins].empty?
|
154
|
+
|
155
|
+
group_fields = (
|
156
|
+
query[:keys].select{ |k| !k.is_a?(FieldExpression)
|
157
|
+
} + collect_having_fields(query[:condition])).flatten.uniq
|
158
|
+
group_keys = sql_keys(group_fields)
|
159
|
+
sql += "GROUP BY #{group_keys} "
|
160
|
+
sql += "HAVING #{having} "
|
161
|
+
end
|
162
|
+
bind_vars += having_vals
|
163
|
+
end
|
164
|
+
|
148
165
|
order = sql_order(query, query[:order_replacements])
|
149
166
|
if (query[:limit] || query[:query_type] == :count)
|
150
167
|
limit = nil
|
@@ -158,9 +175,16 @@ module Spider; module Model; module Storage; module Db
|
|
158
175
|
end
|
159
176
|
if (!query[:joins].empty?)
|
160
177
|
data_tables_sql = query[:order_on_different_table] ? tables_sql : query[:tables].join(', ')
|
161
|
-
|
178
|
+
pks = query[:primary_keys].reject{ |pk| pk.is_a?(Db::FieldExpression) }
|
179
|
+
pk_sql = pks.join(', ')
|
162
180
|
distinct_sql = "SELECT DISTINCT #{pk_sql} FROM #{tables_sql}"
|
163
|
-
distinct_sql += " WHERE #{where}"
|
181
|
+
distinct_sql += " WHERE #{where}" unless where.blank?
|
182
|
+
unless having.blank?
|
183
|
+
|
184
|
+
group_keys = sql_keys((pks + collect_having_fields(query[:condition])).flatten.uniq)
|
185
|
+
distinct_sql += " GROUP BY #{group_keys} "
|
186
|
+
distinct_sql += " HAVING #{having}"
|
187
|
+
end
|
164
188
|
data_sql = "SELECT #{keys} FROM #{data_tables_sql} WHERE (#{pk_sql}) IN (#{distinct_sql})"
|
165
189
|
data_sql += " order by #{order}" unless order.blank?
|
166
190
|
else
|
@@ -185,6 +209,7 @@ module Spider; module Model; module Storage; module Db
|
|
185
209
|
|
186
210
|
def sql_condition_value(key, comp, value, bound_vars=true)
|
187
211
|
curr[:bind_cnt] ||= 0
|
212
|
+
key = key.expression if key.is_a?(FieldExpression)
|
188
213
|
if (comp.to_s.downcase == 'ilike')
|
189
214
|
comp = 'like'
|
190
215
|
key = "UPPER(#{key})"
|
@@ -100,9 +100,9 @@ module Spider; module Model; module Storage; module Db; module Connectors
|
|
100
100
|
bind_vars.each_index do |i|
|
101
101
|
var = bind_vars[i]
|
102
102
|
if (var.is_a?(Oracle::OracleNilValue))
|
103
|
-
cursor.bind_param(i+1, nil, var.type, 0)
|
103
|
+
cursor.bind_param(":#{i+1}", nil, var.type, 0)
|
104
104
|
else
|
105
|
-
cursor.bind_param(i+1, var)
|
105
|
+
cursor.bind_param(":#{i+1}", var)
|
106
106
|
end
|
107
107
|
end
|
108
108
|
res = cursor.exec
|
@@ -97,6 +97,7 @@ module Spider; module Model; module Storage; module Db
|
|
97
97
|
|
98
98
|
# Sets the column name for an element.
|
99
99
|
def set_column(element_name, field)
|
100
|
+
field = {:name => field} if field.is_a?(String)
|
100
101
|
field = Field.new(@table, field[:name], field[:type], field[:attributes] || {}) if field.is_a?(Hash)
|
101
102
|
had_column = @columns[element_name]
|
102
103
|
@columns[element_name] = field
|
@@ -105,6 +106,7 @@ module Spider; module Model; module Storage; module Db
|
|
105
106
|
|
106
107
|
# Sets a foreign key to the primary key of an element.
|
107
108
|
def set_foreign_key(element_name, element_key, field)
|
109
|
+
field = {:name => field} if field.is_a?(String)
|
108
110
|
if field.is_a?(Hash)
|
109
111
|
field[:attributes] ||= {}
|
110
112
|
field[:attributes][:expression] ||= field[:expression]
|
@@ -241,6 +243,25 @@ module Spider; module Model; module Storage; module Db
|
|
241
243
|
end
|
242
244
|
|
243
245
|
end
|
246
|
+
|
247
|
+
class FieldInAliasedTable < Field
|
248
|
+
|
249
|
+
def initialize(field, table_alias)
|
250
|
+
@table = field.table
|
251
|
+
@name = field.name
|
252
|
+
@type = field.type
|
253
|
+
@table_alias = table_alias
|
254
|
+
end
|
255
|
+
|
256
|
+
def to_s
|
257
|
+
"#{@table_alias}.#{@name}"
|
258
|
+
end
|
259
|
+
|
260
|
+
def inspect
|
261
|
+
"#<#{self.class.name}:#{self.object_id} @name=\"#{@name}\", @table=#<Spider::Model::Storage::Db::Table:#{@table.object_id} #{@table.name} AS #{@table_alias}> >"
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
244
265
|
|
245
266
|
class FieldExpression < Field
|
246
267
|
attr_reader :expression
|
@@ -264,6 +285,14 @@ module Spider; module Model; module Storage; module Db
|
|
264
285
|
@table = table
|
265
286
|
@joins = joins
|
266
287
|
end
|
288
|
+
|
289
|
+
def aggregate=(val)
|
290
|
+
@aggregate = val
|
291
|
+
end
|
292
|
+
|
293
|
+
def aggregate?
|
294
|
+
!!@aggregate
|
295
|
+
end
|
267
296
|
|
268
297
|
def to_s
|
269
298
|
@expression
|
@@ -165,7 +165,7 @@ module Spider; module Model; module Storage; module Db
|
|
165
165
|
if (func_el.is_a?(Spider::QueryFuncs::Function))
|
166
166
|
function(func_el)
|
167
167
|
else
|
168
|
-
func.mapper_fields[func_el]
|
168
|
+
func.mapper_fields[func_el.to_s]
|
169
169
|
end
|
170
170
|
}
|
171
171
|
case func.func_name
|
@@ -183,6 +183,8 @@ module Spider; module Model; module Storage; module Db
|
|
183
183
|
return "(#{fields[0]} - #{fields[1]})"
|
184
184
|
when :rownum
|
185
185
|
return "ROWNUM()"
|
186
|
+
when :sum, :avg, :count, :first, :last, :max, :min
|
187
|
+
return "#{func.func_name.to_s.upcase}(#{fields[0]})"
|
186
188
|
end
|
187
189
|
raise NotImplementedError, "#{self.class} does not support function #{func.func_name}"
|
188
190
|
end
|
@@ -249,6 +251,16 @@ module Spider; module Model; module Storage; module Db
|
|
249
251
|
where, vals = sql_condition(query)
|
250
252
|
bind_vars += vals
|
251
253
|
sql += "WHERE #{where} " if where && !where.empty?
|
254
|
+
having, having_vals = sql_condition(query, true)
|
255
|
+
unless having.blank?
|
256
|
+
group_fields = (
|
257
|
+
query[:keys].select{ |k| !k.is_a?(FieldExpression)
|
258
|
+
} + collect_having_fields(query[:condition])).flatten.uniq
|
259
|
+
group_keys = sql_keys(group_fields)
|
260
|
+
sql += "GROUP BY #{group_keys} "
|
261
|
+
sql += "HAVING #{having} "
|
262
|
+
bind_vars += having_vals
|
263
|
+
end
|
252
264
|
order = sql_order(query)
|
253
265
|
sql += "ORDER BY #{order} " if order && !order.empty?
|
254
266
|
limit = sql_limit(query)
|
@@ -262,6 +274,7 @@ module Spider; module Model; module Storage; module Db
|
|
262
274
|
|
263
275
|
# Returns the SQL for select keys.
|
264
276
|
def sql_keys(query)
|
277
|
+
query = {:keys => query} unless query.is_a?(Hash)
|
265
278
|
query[:keys].join(',')
|
266
279
|
end
|
267
280
|
|
@@ -300,39 +313,45 @@ module Spider; module Model; module Storage; module Db
|
|
300
313
|
end
|
301
314
|
|
302
315
|
# Returns SQL and bound variables for a condition.
|
303
|
-
def sql_condition(query)
|
316
|
+
def sql_condition(query, having=false)
|
304
317
|
condition = query[:condition]
|
305
318
|
return ['', []] unless (condition && condition[:values])
|
306
319
|
bind_vars = []
|
307
|
-
condition[:values].reject!{ |v| v.is_a?(Hash) && v[:values].empty?
|
308
|
-
|
309
|
-
|
320
|
+
condition[:values].reject!{ |v| (v.is_a?(Hash) && v[:values].empty?)}
|
321
|
+
vals = condition[:values]
|
322
|
+
|
323
|
+
return nil if !having && condition[:is_having]
|
324
|
+
mapped = vals.map do |v|
|
325
|
+
if v.is_a? Hash # subconditions
|
310
326
|
# FIXME: optimize removing recursion
|
311
|
-
|
312
|
-
|
327
|
+
sql, vals = sql_condition({:condition => v}, having)
|
328
|
+
next unless sql
|
313
329
|
bind_vars += vals
|
314
330
|
sql = nil if sql.empty?
|
315
331
|
sql = "(#{sql})" if sql && v[:values].length > 1
|
316
332
|
sql
|
317
|
-
elsif
|
318
|
-
|
319
|
-
|
320
|
-
v[1] = 'between' if (v[2].is_a?(Range))
|
321
|
-
v[2].upcase! if (v[1].to_s.downcase == 'ilike')
|
322
|
-
if (v[1].to_s.downcase == 'between')
|
323
|
-
bind_vars << v[2].first
|
324
|
-
bind_vars << v[2].last
|
333
|
+
elsif !having || condition[:is_having]
|
334
|
+
if v[2].is_a? Spider::QueryFuncs::Expression
|
335
|
+
sql_condition_value(v[0], v[1], v[2].to_s, false)
|
325
336
|
else
|
326
|
-
|
337
|
+
v[1] = 'between' if (v[2].is_a?(Range))
|
338
|
+
v[2].upcase! if (v[1].to_s.downcase == 'ilike')
|
339
|
+
if (v[1].to_s.downcase == 'between')
|
340
|
+
bind_vars << v[2].first
|
341
|
+
bind_vars << v[2].last
|
342
|
+
else
|
343
|
+
bind_vars << v[2] unless v[2].nil?
|
344
|
+
end
|
345
|
+
sql_condition_value(v[0], v[1], v[2])
|
327
346
|
end
|
328
|
-
sql_condition_value(v[0], v[1], v[2])
|
329
347
|
end
|
330
348
|
end
|
331
|
-
return mapped.
|
349
|
+
return mapped.reject{ |p| p.nil? }.join(' '+(condition[:conj] || 'and')+' '), bind_vars
|
332
350
|
end
|
333
351
|
|
334
352
|
# Returns the SQL for a condition comparison.
|
335
353
|
def sql_condition_value(key, comp, value, bound_vars=true)
|
354
|
+
key = key.expression if key.is_a?(FieldExpression)
|
336
355
|
if (comp.to_s.downcase == 'ilike')
|
337
356
|
comp = 'like'
|
338
357
|
key = "UPPER(#{key})"
|
@@ -709,6 +728,12 @@ module Spider; module Model; module Storage; module Db
|
|
709
728
|
end
|
710
729
|
return sql, values
|
711
730
|
end
|
731
|
+
|
732
|
+
def collect_having_fields(condition)
|
733
|
+
c = condition
|
734
|
+
c.is_a?(Hash) ?
|
735
|
+
((c[:group_by_fields] || []) + (c[:values] || []).map{ |v| collect_having_fields(v) }) : []
|
736
|
+
end
|
712
737
|
|
713
738
|
##############################################################
|
714
739
|
# Reflection #
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spiderfw
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 47
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.6.
|
9
|
+
- 20
|
10
|
+
version: 0.6.20
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ivan Pirlik
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-10-05 00:00:00 +02:00
|
19
19
|
default_executable: spider
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|