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 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.19
1
+ 0.6.20
@@ -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
- wclass = case option[:params][:type].name
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 (@attributes[:paginate])
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
- @scene.pages = (@rows.total_rows.to_f / @attributes[:row_limit]).ceil
115
- @scene.paginate_last = [@scene.paginate_first + 9, @scene.pages].min
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
@@ -315,7 +315,9 @@ module Spider; module Forms
315
315
  end
316
316
 
317
317
  def load
318
- instantiate_obj if (@pk)
318
+ obj = instantiate_obj if (@pk)
319
+ trigger(:after_load, obj)
320
+ obj
319
321
  end
320
322
 
321
323
  def save(action=nil)
@@ -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 of elements that need an "inner" join
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
- sub = {}
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.each_key do |k|
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
- sub[k] = true if sub_join_info[k] && sub[k] != false
534
- sub[k] = false unless sub_join_info
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
- sub[k] = true if sub_join_info[k]
549
+ res[k] = true if sub_join_info[k]
537
550
  end
538
551
  end
539
552
  end
540
- join_info.merge!(sub)
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.qualified_foreign_key_field(element.name, el_k)
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
- if model.mapper.have_references?(element.name)
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
- else
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.qualified_foreign_key_field(el_name, k.name)
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], :joins => joins, :join_info => el_join_info)
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.qualified_field(element.name, options[:table])
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[el.name] = el_model.mapper.schema.field(el.name)
813
+ owner_func.mapper_fields[el_name.to_s] = el_model.mapper.schema.field(el.name)
770
814
  end
771
- return FieldFunction.new(storage.function(func), schema.table, joins)
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
- !storage.supports?(:delete_cascade) || !schema.cascade?(el.name) # TODO: implement
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.set("#{integrated_from.name}.#{integrated_from_element}", c, v)
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
- pk_sql = query[:primary_keys].reject{ |pk| pk.is_a?(Db::FieldExpression) }.join(', ')
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}" if where && !where.empty?
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
- mapped = condition[:values].map do |v|
309
- if (v.is_a? Hash) # subconditions
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
- sql, vals = sql_condition({:condition => v})
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 (v[2].is_a? Spider::QueryFuncs::Expression)
318
- sql_condition_value(v[0], v[1], v[2].to_s, false)
319
- else
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
- bind_vars << v[2] unless v[2].nil?
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.select{ |p| p != nil}.join(' '+(condition[:conj] || 'and')+' '), bind_vars
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: 33
4
+ hash: 47
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 19
10
- version: 0.6.19
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-09-21 00:00:00 +02:00
18
+ date: 2011-10-05 00:00:00 +02:00
19
19
  default_executable: spider
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency