spiderfw 0.6.19 → 0.6.20
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.
- 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
|