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 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