lib/spiderfw/model/storage/db/db_storage.rb in spiderfw-0.6.19 vs lib/spiderfw/model/storage/db/db_storage.rb in spiderfw-0.6.20

- old
+ new

@@ -163,11 +163,11 @@ def function(func) fields = func.elements.map{ |func_el| if (func_el.is_a?(Spider::QueryFuncs::Function)) function(func_el) else - func.mapper_fields[func_el] + func.mapper_fields[func_el.to_s] end } case func.func_name when :length return "LENGTH(#{fields.join(', ')})" @@ -181,10 +181,12 @@ return "SUBSTR(#{fields.join(', ')}, #{arguments})" when :subtract return "(#{fields[0]} - #{fields[1]})" when :rownum return "ROWNUM()" + when :sum, :avg, :count, :first, :last, :max, :min + return "#{func.func_name.to_s.upcase}(#{fields[0]})" end raise NotImplementedError, "#{self.class} does not support function #{func.func_name}" end ################################################################## @@ -247,10 +249,20 @@ sql = "SELECT #{sql_keys(query)} FROM #{tables_sql} " bind_vars += tables_values where, vals = sql_condition(query) bind_vars += vals sql += "WHERE #{where} " if where && !where.empty? + having, having_vals = sql_condition(query, true) + unless having.blank? + group_fields = ( + query[:keys].select{ |k| !k.is_a?(FieldExpression) + } + collect_having_fields(query[:condition])).flatten.uniq + group_keys = sql_keys(group_fields) + sql += "GROUP BY #{group_keys} " + sql += "HAVING #{having} " + bind_vars += having_vals + end order = sql_order(query) sql += "ORDER BY #{order} " if order && !order.empty? limit = sql_limit(query) sql += limit if limit return sql, bind_vars @@ -260,10 +272,11 @@ curr[:total_rows] end # Returns the SQL for select keys. def sql_keys(query) + query = {:keys => query} unless query.is_a?(Hash) query[:keys].join(',') end # Returns an array containing the 'FROM' part of an SQL query (including joins), # and the bound variables, if any. @@ -298,43 +311,49 @@ end return str, values end # Returns SQL and bound variables for a condition. - def sql_condition(query) + def sql_condition(query, having=false) condition = query[:condition] return ['', []] unless (condition && condition[:values]) bind_vars = [] - condition[:values].reject!{ |v| v.is_a?(Hash) && v[:values].empty? } - mapped = condition[:values].map do |v| - if (v.is_a? Hash) # subconditions + condition[:values].reject!{ |v| (v.is_a?(Hash) && v[:values].empty?)} + vals = condition[:values] + + return nil if !having && condition[:is_having] + mapped = vals.map do |v| + if v.is_a? Hash # subconditions # FIXME: optimize removing recursion - - sql, vals = sql_condition({:condition => v}) + sql, vals = sql_condition({:condition => v}, having) + next unless sql bind_vars += vals sql = nil if sql.empty? sql = "(#{sql})" if sql && v[:values].length > 1 sql - elsif (v[2].is_a? Spider::QueryFuncs::Expression) - sql_condition_value(v[0], v[1], v[2].to_s, false) - else - v[1] = 'between' if (v[2].is_a?(Range)) - v[2].upcase! if (v[1].to_s.downcase == 'ilike') - if (v[1].to_s.downcase == 'between') - bind_vars << v[2].first - bind_vars << v[2].last + elsif !having || condition[:is_having] + if v[2].is_a? Spider::QueryFuncs::Expression + sql_condition_value(v[0], v[1], v[2].to_s, false) else - bind_vars << v[2] unless v[2].nil? + v[1] = 'between' if (v[2].is_a?(Range)) + v[2].upcase! if (v[1].to_s.downcase == 'ilike') + if (v[1].to_s.downcase == 'between') + bind_vars << v[2].first + bind_vars << v[2].last + else + bind_vars << v[2] unless v[2].nil? + end + sql_condition_value(v[0], v[1], v[2]) end - sql_condition_value(v[0], v[1], v[2]) end end - return mapped.select{ |p| p != nil}.join(' '+(condition[:conj] || 'and')+' '), bind_vars + return mapped.reject{ |p| p.nil? }.join(' '+(condition[:conj] || 'and')+' '), bind_vars end # Returns the SQL for a condition comparison. def sql_condition_value(key, comp, value, bound_vars=true) + key = key.expression if key.is_a?(FieldExpression) if (comp.to_s.downcase == 'ilike') comp = 'like' key = "UPPER(#{key})" end if (value.nil?) @@ -706,9 +725,15 @@ condition_sql, condition_values = sql_condition(max) sql += " WHERE #{condition_sql}" values += condition_values end return sql, values + end + + def collect_having_fields(condition) + c = condition + c.is_a?(Hash) ? + ((c[:group_by_fields] || []) + (c[:values] || []).map{ |v| collect_having_fields(v) }) : [] end ############################################################## # Reflection # ##############################################################