class DrbListModel

this file is part of manqod manqod is distributed under the CDDL licence the owner of manqod is Dobai-Pataky Balint(dpblnt@gmail.com)

Attributes

archive_key[R]
base[R]
clients[R]
column_of_archive[R]
column_of_background[R]
column_of_foreground[R]
column_of_id[R]
column_of_iter_depth[R]
column_of_sensitive[R]
column_of_tree[R]
column_of_tree_parent[R]
column_of_visible[R]
data[R]
fetch_filter_key[R]
fetch_filter_negate[R]
fetch_filter_value[R]
headers[R]
headertypes[R]
moditem[R]
my_id[R]
querysql[R]
rowcount[R]
tree_key[R]
tree_parent_key[R]

Public Class Methods

new(drbdb,my_id) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 9
def initialize(drbdb,my_id)
        @my_id=my_id.to_i
        @drbdb=drbdb
        @data=nil
        @rowcount=0
        @tree_key=nil
        @tree_parent_key=nil
        @fetch_filter_key=nil
        @fetch_filter_value=nil
        @fetch_filter_negate=false
        @archive_key=nil
        @clients=Hash.new
        @base=nil
        @last_seq=Hash.new
        @buttons=Array.new
        @childs=Array.new
end

Public Instance Methods

alive?() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 665
def alive?
        @drbdb.alive?
end
buttons(user_id) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 650
def buttons(user_id)
      ret=Array.new
      @buttons.each{|button|
              ret.push(button) if button["grp_id"].to_i < 1 || @drbdb.user_in_group?(user_id.to_i,button["grp_id"].to_i)
      }
      ret
end
cache() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 661
def cache
      @drbdb.cache
end
child_iter_ids(iter_id) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 103
def child_iter_ids(iter_id)
        chs=Array.new
        @data.each{|chid,chrow|
                chs.push(chid) if iter_id==chrow[@column_of_tree]
        }
        chs
end
childs() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 658
def childs
      @childs
end
create_skeleton() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 129
        def create_skeleton
                @moditem=@drbdb.admin.qrow("select moditems.*,`queries`.`sql`,tables.name as base_table from moditems left join `queries` on `queries`.`id` = `moditems`.`query_sql_id` left join tables on queries.base = tables.id where moditems.id='#{my_id}'")

                #querysql
                @querysql=@moditem['sql'] if !@moditem["sql"].nil? and @moditem["sql"].length>5
                @querysql=@moditem['querysql'] if @moditem["querysql"].length>5

                #base
                @base=@moditem["base_table"]
                @base=@drbdb.guess_table(querysql,"id") if @base.nil?

                #id/list_key
                @list_key=gtk_attribute("list_key") || "#{@base}.id"

                @key_child=gtk_attribute("child_key")
                @key_parent=gtk_attribute("parent_key")
                @key_child2=gtk_attribute("second_child_key")
                @key_parent2=gtk_attribute("second_parent_key")

                if @tree_key=gtk_attribute("tree_key")
                        @tree_parent_key=gtk_attribute("tree_parent_key") || @list_key
                end
                
                @fetch_filter_key=gtk_attribute("fetch_filter_key")
                @fetch_filter_value=gtk_attribute("fetch_filter_value")
                @fetch_filter_negate=gtk_attribute("fetch_filter_negate") == 'true'
                @background_key=gtk_attribute("background_key")
                @foreground_key=gtk_attribute("foreground_key")
                @archive_key=gtk_attribute("archive_key")

                einfo("base:#{@base}, list_key:#{@list_key}, tree_key:#{@tree_key}, child_key: #{@key_child}, parent_key: #{@key_parent}, child_key2: #{@key_child2}, parent_key2: #{@key_parent2}") unless @drbdb.main_server.starting_up

                @headers=Hash.new
                headers[@list_key]= { "type" => "Bignum", "visible" => false, "data" => @list_key }  #id
                headers["visible_for_filter"]= { "type" => "TrueClass", "visible" => false, "data" => "visible_for_filter" }  #visible_for_filter
                headers["sensitive"]= { "type" => "TrueClass", "visible" => false, "data" => "sensitive" }  #sensitive
                headers[@background_key]= { "type" => "String", "visible" => false, "data" => @background_key }  if @background_key
                headers[@foreground_key]= { "type" => "String", "visible" => false, "data" => @foreground_key }  if @foreground_key
                headers[@tree_key]= { "type" => "Bignum", "visible" => false, "data" => @tree_key }  if @tree_key
                headers[@tree_parent_key]= { "type" => "Bignum", "visible" => false, "data" => @tree_parent_key }  if (!@tree_parent_key.nil?)
                headers["iter_depth"]= { "type" => "Bignum", "visible" => false, "data" => "iter_depth" }  if @tree_key
                headers[@key_child]= { "type" => "Bignum", "visible" => false, "data" => @key_child }  if @key_child
                headers[@key_child2]= { "type" => "Bignum", "visible" => false, "data" => @key_child2 }  if @key_child2
                headers[@fetch_filter_key]= { "type" => "String", "visible" => false, "data" => @fetch_filter_key }  if @fetch_filter_key
                headers[@archive_key]= { "type" => "TrueClass", "visible" => false, "data" => @archive_key }  if @archive_key
                
                hdebug="\nheaders from query:"
                @drbdb.admin.rows("select * from gtkheaders where listing='#{my_id}' order by oid asc").each{|row|
                        headers[row["data"]]= row
                        headers[row["data"]]["visible"] = !(['gtk_invisible_int','gtk_invisible_string'].include?(row['type']))
                        hdebug="#{hdebug}\n #{row['data']} #{row['type']}"
                }

                @headertypes=Array.new
                header_n=0
                headers.each_value{|header| 
                        header["model_col"]=header_n
                        case header['type']
                                when 'gtk_int','Integer','gtk_invisible_int','Bignum','gtk_duration','gtk_timestamp' then headertypes.push(Bignum)
                                when 'gtk_float','gtk_progress' then headertypes.push(Float)
                                when 'gtk_toggle','TrueClass' then  headertypes.push(TrueClass)
#                               when 'gtk_pixbuf' then  headertypes.push(Gdk::Pixbuf)
                                when 'gtk_pixbuf' then headertypes.push(Fixnum)
                        else
                                headertypes.push(String)
                        end
                        header_n+=1
                }

                @data=Hash.new

                @column_of_id=headers[@list_key]["model_col"]
                @column_of_visible=headers["visible_for_filter"]["model_col"]
                @column_of_background=headers[@background_key]["model_col"] unless headers[@background_key].nil?
                @column_of_foreground=headers[@foreground_key]["model_col"] unless headers[@foreground_key].nil?
                @column_of_sensitive=headers["sensitive"]["model_col"]
                @column_of_fetch_filter=if @fetch_filter_key then headers[@fetch_filter_key]["model_col"] else nil end
                @column_of_archive=if @archive_key then headers[@archive_key]["model_col"] else nil end
                hdebug="#{hdebug}\nheadertypes: #{@headertypes.inspect}\nheaders:\n"

                @background_legend=Hash.new
                @foreground_legend=Hash.new

                @headers.each_value{|val| hdebug="#{hdebug} #{val['data']} #{val['model_col']}\n"}
#               print "#{self} #{hdebug}\n"
                @column_of_tree=nil
                @column_of_tree_parent=nil
                if @tree_key
                        @column_of_tree=if tree_header=headers[@tree_key] then tree_header["model_col"]; else nil;end
                        @column_of_tree_parent=if tree_parent_header=headers[@tree_parent_key] then tree_parent_header["model_col"] else nil end
                        @column_of_iter_depth=headers["iter_depth"]["model_col"]
                end


                
                @relations=Array.new
                @drbdb.admin.rows("select * from relations where src_table='#{base}' or dst_table='#{base}'").each{|relation|
                        @relations.push(relation)
                }
                
                cache.set("#{@my_id}moditem",@moditem)
                cache.set("#{@my_id}headers",@headers)
                cache.set("#{@my_id}headertypes",@headertypes)
                cache.set("#{@my_id}attributes",{
                        :querySQL=>@querysql,
                        :base=>@base,
                        :list_key=>@list_key,
                        :child_key=>@key_child,
                        :parent_key=>@key_parent,
                        :child_key2=>@key_child2,
                        :parent_key2=>@key_parent2,
                        :tree_key=>@tree_key,
                        :column_of_id=>@column_of_id,
                        :column_of_visible=>@column_of_visible,
                        :column_of_sensitive=>@column_of_sensitive,
                        :column_of_background=>@column_of_background,
                        :column_of_foreground=>@column_of_foreground,
                        :column_of_archive=>@column_of_archive,
                        :column_of_tree_parent=>@column_of_tree_parent,
                        :column_of_tree=>@column_of_tree,
                        :fetch_filter_key=>@fetch_filter_key,
                        :column_of_fetch_filter=>@column_of_fetch_filter,
                        :fetch_filter_value=>@fetch_filter_value,
                        :fetch_filter_negate=>@fetch_filter_negate,
                        :archive_key=>@archive_key
                        })

                reset_buttons
                reset_childs
                self
        end
data_iter_of_col_num(col_num,col_num_data) click to toggle source

finds iter by col_num, col_num=colnum number to search for, col_num_data=the data we're looking for

# File lib/DrbDB/DrbListModel.rb, line 74
def data_iter_of_col_num(col_num,col_num_data)
        found_iter=nil
        @data.each{|row_id,iter|
                if iter[col_num]==col_num_data
                        found_iter=iter
                        break
                end
        }
        found_iter
end
data_of_column(colname,lid) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 122
def data_of_column(colname,lid)
        ret=nil
        if di=@data[lid] and col=headers[colname]
                ret=di[col["model_col"]]
        end
end
duplicate_iter(iter_to_dup) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 566
        def duplicate_iter(iter_to_dup)
                sql="insert into #{base} set ";
                q=@drbdb.query("select * from #{base} where #{@list_key}='#{iter_to_dup[@column_of_id]}'")
                colon=false
                if row=q.fetch_hash()
                        row.each { |field,data|
                                if "#{base}.#{field}"!=@list_key and data
                                        sql="#{sql}, " if colon
                                        colon=true
                                        sql="#{sql} `#{field}`='#{@drbdb.escape_string(data)}' " 
                                end
                        }
                end
                @drbdb.query(sql)
                rows_changed("last")
#               load_data(nil,"last")
        end
filtered_fetch(ff_value=nil) { |row| ... } click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 448
def filtered_fetch(ff_value=nil)
        t=Thread.new{
        einfo("serving")
        before=Time.now
        served_rows=0
        tree_sorted.each{|row_id,row|
                if @column_of_fetch_filter.nil? or ff_value.nil? or (not @fetch_filter_negate and row[@column_of_fetch_filter].to_s == ff_value.to_s) or (@fetch_filter_negate and row[@column_of_fetch_filter].to_s != ff_value.to_s)
                        yield row
                        served_rows+=1
                end
        }
        yield served_rows
        serve_time=Time.now.to_f-before.to_f
        einfo("served #{served_rows}(of #{@rowcount}) in "+sprintf("%.2fs",serve_time))
        }
end
filtered_fetch2(ff_value=nil,archive=false) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 465
        def filtered_fetch2(ff_value=nil,archive=false)
#               t=Thread.new{
                        before=Time.now.to_f
                        tree=tree_sorted
                        sort_time=Time.now.to_f
                        ret=Array.new
                        tree.each{|row| 
                                ret.push(row) if (@column_of_fetch_filter.nil? || ff_value.nil? || (!@fetch_filter_negate && row[@column_of_fetch_filter].to_s == ff_value.to_s) || (@fetch_filter_negate && row[@column_of_fetch_filter].to_s != ff_value.to_s) ) && (@column_of_archive.nil? || (archive || !row[@column_of_archive]))
                        }
                        end_time=Time.now.to_f
                        einfo("serving #{ret.size}(of #{@rowcount}) (sort:"+sprintf("%.2fs",sort_time-before)+" filter:"+sprintf("%.2fs",end_time-sort_time)+") "+sprintf("%.2fs",end_time-before))
#               }
        ret
        end
getBinding() click to toggle source

def method_missing(sym,*args)

print "\n!#{self} missing method: #{sym}(#{args})\n"

end

# File lib/DrbDB/DrbListModel.rb, line 689
def getBinding
        binding
end
get_id() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 36
def get_id
        @my_id
end
gtk_attribute(gattr) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 42
def gtk_attribute(gattr)
        @drbdb.gtk_attribute(gattr,self)
end
lock_iter(locked_id) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 46
def lock_iter(locked_id)
        edebug("lock for #{locked_id}") unless @clients.size==0
        if @data.has_key?(locked_id) && @data[locked_id][@column_of_sensitive]
                @data[locked_id][@column_of_sensitive]=false
                begin
                        cache.set("#{@my_id}[#{locked_id}]",@data[locked_id])
                rescue => err
                        eerror("Locking: #{err}")
                end
                notify_clients(locked_id)
        end
end
mod_type() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 39
def mod_type
        "list"
end
notify_clients(*args) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 619
def notify_clients(*args)
              to_remove=Array.new
              th=Array.new
                      edebug("notifying #{@clients.size} clients for #{args.inspect}") unless @drbdb.main_server.starting_up || @clients.size==0
              @clients.each_pair{|client_id,client|
                      th << Thread.new{
                      begin
                              if client.alive?
                                      client.update(self.class.name,*args)
                              end
                      rescue =>err
                              eexception(err)
                              to_remove.push(client_id)
                      end
                      }
              }
              th.each{|t| t.join}
              to_remove.each{|dead|
                              @clients.delete(dead)
                      edebug("dead client deleted: #{dead}")
              }
end
remove_dead_clients() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 669
def remove_dead_clients
        alive_clients=0
        clients.delete_if{|client_id,client|
                begin
                        Timeout.timeout(30){
                                client.alive?
                                alive_clients+=1
                                false
                        }
                rescue 
                        ewarn("removing dead subscription: #{client_id.inspect}")
                        true
                end
        }
        alive_clients
end
remove_iters(ids_to_remove,nick=nil) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 584
def remove_iters(ids_to_remove,nick=nil)
        unless base.nil?
                @drbdb.query("delete from #{base} where #{@list_key} in (#{ids_to_remove.join(",")})")
                einfo("removing ids: #{ids_to_remove.inspect}")
                rows_changed(ids_to_remove,nick)
        end
end
reset_buttons() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 270
def reset_buttons
        #loading buttons
        einfo("resetting buttons") unless @drbdb.main_server.starting_up
        @buttons.clear
        @drbdb.admin.rows("select * from buttons where moditemid='#{my_id}' order by oid").each{|button|
                @buttons.push(button)
        }
        notify_clients(nil,"buttons")
end
reset_childs() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 261
def reset_childs
        #loading childs
        einfo("resetting childs") unless @drbdb.main_server.starting_up
        @childs.clear
        @drbdb.admin.rows("select moditems.id,moditems.display,moditems.oid,modules.modname from moditems left join modules on modid = modules.id where parent='#{my_id}' order by oid").each{|moditem|
                @childs.push(moditem)
        }
end
rows_changed(chids,nick=nil,chseq=0) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 480
        def rows_changed(chids,nick=nil,chseq=0)
                ids=if chids.class.name == "Array" then chids.clone else [chids] end
                #prevent circular calling of rows_changed
                chseq=Time.new.to_f-@my_id  if chseq == 0 #FIXME: not unique

#               edebug("rows_changed:@#{chseq}:#{nick}:#{ids.inspect}")
                ids_to_remove=Array.new
                ids.each{|ch_id|
                        if @last_seq[ch_id] == chseq
                                ids_to_remove.push(ch_id)
                        else
                                @last_seq[ch_id]=chseq
                        end
                }
                ids_to_remove.each{|to_rem| ids.delete(to_rem)}

                return unless ids.size > 0

                #precaching related ids, thus if update deletes a row, we still have the required data to walk the map
                pre_ids=Hash.new
                @drbdb.moditems_with_base(base){|other_list|
                        unless other_list == self
                                pre_ids[other_list]=Array.new unless pre_ids.has_key?(other_list)
                                ids.each{|i| pre_ids[other_list].push(i)}
                        end
                }

                @relations.each{|relation|
                        if (relation['src_table'] == base) || (relation['dst_table'] == base && relation['rel_type'] == 'o')
                                #forward, update parent lists for group_by queries
                                #update lists with relation.src_table.src_field == relation.dst_table.dst_field
                                @drbdb.moditems_with_base(relation['dst_table']){|other_list|
                                        pre_ids[other_list]=Array.new unless pre_ids.has_key?(other_list) #might already be there if multiple relations to each other
                                        ids.each{|changed_id|
                                                if other_id=data_of_column(relation['src_table']+"."+relation['src_field'],changed_id)
                                                        pre_ids[other_list].push(other_id.to_i)
                                                end
                                        }
                                }
                                #else
                                #reverse, update client lists, should be used only if this relation_id is used in the client query
                                #update lists with dst_field == src_table.src_field
                        end
                }
                
                rnick=if nick.nil? then nil else nick.clone end
                ids=update(nil,ids,rnick)

                #postcaching again, if update was insert we have the data to walk the map
                @relations.each{|relation|
                        if (relation['src_table'] == base) || (relation['dst_table'] == base && relation['rel_type'] == 'o')
                                #forward, update parent lists for group_by queries
                                #update lists with relation.src_table.src_field == relation.dst_table.dst_field
                                @drbdb.moditems_with_base(relation['dst_table']){|other_list|
                                        pre_ids[other_list]=Array.new unless pre_ids.has_key?(other_list) #might already be there
                                        ids.each{|changed_id|
                                                if other_id=data_of_column(relation['src_table']+"."+relation['src_field'],changed_id)
                                                        pre_ids[other_list].push(other_id.to_i)
                                                end
                                        }
#                                       ecode("postcache #{other_list}:#{pre_ids[other_list].inspect}")
                                }
                                #else
                                #reverse, update client lists, should be used only if this relation_id is used in the client query
                                #update lists with dst_field == src_table.src_field
                        end
                }
#               ecode("notifying other drblists with base:#{@base} for #{ids.inspect}")
                @drbdb.moditems_with_base(base){|other_list|
                        #drblist.rows_changed(ids,nil,chseq) unless drblist == self
                        unless other_list == self
                                pre_ids[other_list]=Array.new unless pre_ids.has_key?(other_list)
                                ids.each{|i| pre_ids[other_list].push(i)}
#                               ecode("postcache #{other_list}:#{pre_ids[other_list].inspect}")
                        end
                }

#               ecode("notifying other drblists related to:#{@base} for #{ids.inspect}")
#               ecode("pre_ids:#{pre_ids.inspect}")
                pre_ids.each{|other_model,other_ids|
                        other_ids.uniq!
#                       ecode("#{other_model}:#{other_ids.inspect}/@#{chseq}")
                        other_model.rows_changed(other_ids,nil,chseq)
                }
        end
set_children_depth(ids,have_seen=Array.new) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 111
def set_children_depth(ids,have_seen=Array.new)
        ids.each{|iid|
                if @data.has_key?(iid) && !have_seen.include?(iid)
                        edebug("setting depth for: #{iid}")
                        have_seen.push(iid)
                        set_iter_depth(@data[iid])
                        set_children_depth(child_iter_ids(iid),have_seen)
                end
        }
end
set_iter_depth(row,depth_check=Array.new) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 85
def set_iter_depth(row,depth_check=Array.new)
        begin
        if @tree_key
                if piter=data_iter_of_col_num(@column_of_tree_parent,row[@column_of_tree])
                        unless depth_check.include?(piter[@column_of_id])
                                depth_check.push(piter[@column_of_id])
                                set_iter_depth(piter,depth_check)
                                else
                                edebug("circular child=>parent relation: #{depth_check.join("->")} -/> #{piter[@column_of_id]}")
                        end
                        row[@column_of_iter_depth]=piter[@column_of_iter_depth]+1
                end
        end
        rescue =>err
                eexception(err)
        end
end
store_history(nick,row_id,operation,header_id=nil,data=nil) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 642
  def store_history(nick,row_id,operation,header_id=nil,data=nil)
#               ecode("storing history: nick:#{nick},op:#{operation}, id:#{row_id}, header:#{header_id}, data:#{data}")
        sql="insert into `history` set `ctime`=now(), `nick`='#{nick}', `base`='#{@base}', `row_id`='#{row_id}', `operation`='#{operation}'"
        sql+=", `header_id`='#{header_id}'" unless header_id.nil?
        sql+=",`data`='#{@drbdb.escape_string(data.to_s)}'" unless data.nil?
        @drbdb.admin.query(sql)
  end
subscribe(client_id,client) click to toggle source

not yet used def change_value_of_iter(iter, column_data, new_value)

sql="update #{base} set `#{column_data}`='#{new_value}' where `id`='#{iter[column_of_id]}' limit 1"
einfo("changing #{column_data} to #{new_value} updatesql: #{sql}","list")
@drbdb.query(prepare_sql(sql,self))
rows_changed(iter[column_of_id].to_i)

end def change_value_of_path(path, column_data, new_value)

change_value_of_iter(@sorter.get_iter(path), column_data, new_value)

end

# File lib/DrbDB/DrbListModel.rb, line 604
def subscribe(client_id,client)
              if @clients.has_key?(client_id)
                      false
              else
              edebug("subscribing: #{client_id}")
              @clients[client_id]=client
              true
      end
end
to_s() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 33
def to_s
        "Drbmodel{#{@drbdb.name}:#{moditem['display']}(#{my_id})}"
end
tree_sorted() click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 438
def tree_sorted
        if @tree_key
                @data.values.sort{|a,b| 
                        a[@column_of_iter_depth]<=>b[@column_of_iter_depth]
                }
                else
                @data.values
        end
end
unlock_iter(unlocked_id) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 59
def unlock_iter(unlocked_id)
        if @data.has_key?(unlocked_id) && !@data[unlocked_id][@column_of_sensitive]
                edebug("unlock for #{unlocked_id}") unless @clients.size==0
                @data[unlocked_id][@column_of_sensitive]=true
                begin
                        cache.set("#{@my_id}[#{unlocked_id}]",@data[unlocked_id])
                rescue => err
                        eerror("Unlocking: #{err}")
                end
                notify_clients(unlocked_id)
                #else the row was locked and removed. we do not unlock it
        end
end
unsubscribe(client_id) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 614
def unsubscribe(client_id)
              edebug("unsubscribing: #{client_id}")
              @clients.delete(client_id)
end
update(notifier,ids=nil,rnick=nil) click to toggle source
# File lib/DrbDB/DrbListModel.rb, line 280
        def update(notifier,ids=nil,rnick=nil)
                before_update=Time.now
                before=Time.now
                if ids
                        if ids=='last' || ids == ["last"]
                                begin
                                                sql=@drbdb.select_last(eval("\"#{querysql}\""),@list_key)
                                                edebug(sql)
                                        rescue =>err
                                                eerror("update last: #{err}\n#{querysql}")
                                                @drbdb.report_mail("update last: #{err}",{'self'=>self.inspect,'error'=>err.inspect,'query'=>querysql.inspect,'backtrace'=>err.backtrace.join("\n")})
                                end
                        else
                                ids=[ids] if ids.class != Array
                                begin
#                                               sql=eval("\"select * from (#{querysql}) as the_query where #{@list_key} in (#{ids.join(',')})\"")
                                                s=@drbdb.add_where("#{querysql}","#{@list_key} in (#{ids.join(',')})")
#                                               edebug("query:#{s}")
                                                sql=eeval("\"#{@drbdb.escape_string(s)}\"")
#                                               edebug("query:#{sql}")
                                        rescue =>err
                                                eerror("update #{ids.inspect}: #{err}\n#{querysql}")
                                                @drbdb.report_mail("update ids:#{ids.inspect}",{'self'=>self.inspect,'error'=>err.inspect,'query'=>querysql.inspect,'backtrace'=>err.backtrace.join("\n")})
                                end
                        end
                        else
                                begin
                                                sql=eeval("\"#{@drbdb.escape_string(querysql)}\"") #'
                                        rescue =>err
                                                @drbdb.report_mail("update full: #{err}",{'self'=>self.inspect,'error'=>err.inspect,'query'=>querysql.inspect,'backtrace'=>err.backtrace.join("\n")})
                                end
                end
                begin
                        qres=@drbdb.rows(sql,true) if sql
                rescue =>err
                        eerror("update: #{err}\n#{sql}")
                        @drbdb.report_mail("update: #{err}",{'self'=>self.inspect,'error'=>err.inspect,'query'=>querysql.inspect,'backtrace'=>err.backtrace.join("\n")})
                end

                sql_time_diff=Time.now.to_f-before.to_f

                if qres then
                        unless ids
                                @data.clear
                                @rowcount=0 
                        end
                        last_ids=nil
                        before=Time.now
                        qres.each{|row|
                                last_ids=[row[@list_key].to_i] if ids == "last" || ids == ["last"]
                                #warning about wrongly implemented queries, which force us to overwrite rows
                                ewarn("overwriting row ##{row[@list_key].to_i}:#{@data[row[@list_key].to_i].inspect}") if @data.has_key?(row[@list_key].to_i) and ids.class.name != "Array"
                                #history
                                row_backup=if !rnick.nil? && @data.has_key?(row[@list_key].to_i) then @data[row[@list_key].to_i].clone else nil end
                                #preserve row insensitivity(lock state)
                                row_insens=@data.has_key?(row[@list_key].to_i) ? @data[row[@list_key].to_i][@column_of_sensitive] : true
                                @data[row[@list_key].to_i]=Hash.new
                                iter=@data[row[@list_key].to_i]
                                headers.each_value{|header|
                                        if row.has_key?(header['data'])
                                                iter[header['model_col']]=
                                                        case header['type']
                                                                when 'gtk_toggle','TrueClass' then row[header['data']]=='true'
                                                                when 'gtk_int','Integer','gtk_invisible_int','Bignum','gtk_duration','gtk_timestamp' then row[header['data']].to_i
                                                                when 'gtk_float','gtk_progress' then row[header['data']].to_f
                                                                when 'gtk_pixbuf' then row[header['data']].to_i #we hold only ids here
                                                                else
                                                                        row[header['data']]
                                                        end
                                                #prepare background color
                                                if @column_of_background == header['model_col'] && iter[@column_of_background].class == String
                                                        cl=iter[@column_of_background].split(":")
                                                        iter[@column_of_background]=cl[0]
                                                        @background_legend[cl[0]]=cl[1]
                                                end
                                                #prepare foreground color
                                                if @column_of_foreground == header['model_col'] && iter[@column_of_foreground].class == String
                                                        cl=iter[@column_of_foreground].split(":") 
                                                        iter[@column_of_foreground]=cl[0]
                                                        @foreground_legend[cl[0]]=cl[1]
                                                end
                                                #history
                                                unless row_backup.nil?
                                                        if header.has_key?("id") && row_backup[header['model_col']] != iter[header['model_col']]
                                                                store_history(rnick,row[@list_key],"u",header['id'],row_backup[header['model_col']])
                                                        end
                                                end
                                        end
                                }
                                #history
                                store_history(rnick,row[@list_key],"i") if row_backup.nil? and !rnick.nil?
                                #defaults
                                iter[@column_of_sensitive]=row_insens
                                iter[@column_of_visible]=false
                                iter[@column_of_iter_depth]=0 if @tree_key
                                @rowcount+=1 unless ids
                        }
                        
                        #store fore and background legends
                        cache.set("#{@my_id}foreground",@foreground_legend)
                        cache.set("#{@my_id}background",@background_legend)
                        
                        if ids and qres.size == 0
                                edebug("removed #{ids.inspect}")
                                #we were called to load_data of a non existing id, which means it was deleted
                                ids.each{|id_to_remove|
                                        @data.delete(id_to_remove.to_i);
                                        einfo("removing from cache: #{id_to_remove.inspect}")
                                        begin
                                                cache.delete("#{@my_id}[#{id_to_remove}]")
                                        rescue Memcached::NotFound
                                        end
                                        @rowcount-=1;
                                        store_history(rnick,id_to_remove,"d") unless rnick.nil?
                                }
                        end
                end
                download_time_diff=Time.now.to_f-before.to_f
                before=Time.now
                if @tree_key
                        #set up the iter depth, for tree building
                        if ids.nil?
                                #full reload
                                @data.each_value{|row| set_iter_depth(row)}
                                else
                                #set iter_depth for all recursive children
                                set_children_depth(ids)
                        end
                end
                tree_prep_time_diff=Time.now.to_f-before.to_f
                before=Time.now
                if @tree_key
                        keys=Array.new
                        tree_sorted.each{|row| keys.push(row[@column_of_id])}
                        else
                        keys=@data.keys
                end

                        if ids.nil?
                                @data.each{|key,row| 
                                        cache.set("#{@my_id}[#{key}]",row)
                                }
                                else
                                ids.each{|iid|
                                        cache.set("#{@my_id}[#{iid}]",@data[iid])
                                }
                        end
                cache.set("#{@my_id}[ids]",keys)
                
                cache_time_diff=Time.now.to_f-before.to_f
                
                ids=last_ids unless last_ids.nil?
                update_time_diff=Time.now.to_f-before_update.to_f
                einfo("loaded #{if ids then ids.inspect else @rowcount end}/#{@data.size} in q:"+sprintf("%.1fs",sql_time_diff)+",d:"+sprintf("%.1fs",download_time_diff)+",t:"+sprintf("%.1fs",tree_prep_time_diff)+",c:"+sprintf("%.1fs",cache_time_diff)+"/"+sprintf("%.1fs",update_time_diff)) unless @drbdb.main_server.starting_up
                notify_clients(ids)
                ids
        end