require 'rubygems' require_gem 'postgres' class Database attr_reader :conn def initialize @conn = PGconn.connect('localhost', 5432, '', '', 'reality_development', 'postgres', '240374') end def query(sql) Query.new(@conn, sql) end def select(opts) return Query.new(@conn, opts[:sql]) if opts[:sql] if opts[:select] fields = opts[:select].is_a?(Array) ? opts[:select].join(',') : opts[:select] else fields = '*' end sql = "SELECT #{fields} FROM #{opts[:table]}" sql << " WHERE #{opts[:where]}" if opts[:where] Query.new(@conn, sql) end def select_count(opts) select(opts.merge(:select => 'COUNT(*)')).each {|r| return r[:count]} end def literal_repr(o) if String === o "'#{o}'" else o.to_s end end def [](name) Filter.new(self, {:table => name}) end end PG_TYPES = { 16 => :to_bool, 20 => :to_i, 21 => :to_i, 22 => :to_i, 23 => :to_i, 700 => :to_f, 701 => :to_f } require 'metaid' class Query attr_reader :result, :fields def initialize(conn, sql) @conn = conn @sql = sql end def execute(*args) @result = @conn.exec(@sql) @fields = @result.fields.map {|s| s.to_sym} @types = (0..(@result.num_fields - 1)).map {|idx| @result.type(idx)} compile_row_fetcher end def compile_row_fetcher parts = (0..(@result.num_fields - 1)).inject([]) do |m, f| translate_fn = PG_TYPES[@types[f]] translator = translate_fn ? ".#{translate_fn}" : "" m << ":#{@fields[f]} => r[#{f}]#{translator}" end l = eval("lambda {|r|{#{parts.join(',')}}}") extend(Module.new {define_method(:fetch_row, &l)}) end def each(&block) execute @result.each do |r| block.call(fetch_row(r)) end self end def row_count @result.num_tuples end end class Filter def initialize(db, opts) @db = db @opts = opts end def compile_conditions(conditions) return nil unless conditions result = conditions.shift while result && (result =~ /\?/) a = @db.literal_repr(conditions.shift) result.sub!(/\?/, a) end result end def filter(conditions) Filter.new(@db, @opts.merge(:where => compile_conditions(conditions))) end alias_method :select, :filter def first(opts = nil) opts ? (opts[:limit] = 1) : (opts = {:limit => 1}) each {|r| return r} end def[](attributes) params = [] rule = attributes.inject('') {|m, kv| params << kv[1]; m << "(#{kv[0]} = ?)"} filter(params.unshift(rule)).first end def all(opts = nil) a = [] each(opts) {|r| a << r} a end def each(opts = nil, &block) @db.select(opts ? @opts.merge(opts) : @opts).each(&block) end def count(opts = nil) @db.select_count(opts ? @opts.merge(opts) : @opts) end def summary(*fields) @db.select(@opts.merge(:select => fields)).each {|r| return r} end def update(values) @db.update(@opts, values) end end class Symbol def max "max(#{to_s}) AS max_#{to_s}" end def min "min(#{to_s}) AS min_#{to_s}" end end ################################################################################ ################################################################################ $db = Database.new $t = $db[:states] #puts $db[:nodes].all.inspect puts $t.summary(:quality.max, :quality.min, :stamp.max, :stamp.min).inspect ################################################################################ # Table creation ################################################################################ #$db.create_table(:states, # :fields => { # id => :int, # :node_id => :int, # :quality => :int, # :stamp => :double, # :value => :text, # :datatype => :int}, # :index => :id #); ################################################################################ # Joins ################################################################################ # # # # # # # # # # require 'benchmark' def t1 $t.each {|n| n[:node_id]} end def t2 $t.filter(nil).each() {|n| n.inspect} end n = 100 Benchmark.bm do |x| x.report {n.times {t1}} x.report {n.times {t2}} end