module MiniSql class Connection def self.default_deserializer_cache @deserializer_cache ||= DeserializerCache.new end def self.type_map(conn) @type_map ||= begin map = PG::BasicTypeMapForResults.new(conn) map.add_coder(MiniSql::Coders::NumericCoder.new(name: "numeric", oid: 1700, format: 0)) map.add_coder(MiniSql::Coders::IPAddrCoder.new(name: "inet", oid: 869, format: 0)) end end def initialize(conn, deserializer_cache = nil, type_map = nil) # TODO adapter to support other databases @conn = conn @deserializer_cache = deserializer_cache || Connection.default_deserializer_cache @type_map = type_map || Connection.type_map(conn) end def query_single(sql, params=nil) result = run(sql, params) result.type_map = @type_map result.column_values(0) ensure result.clear if result end def query(sql, params=nil) result = run(sql, params) result.type_map = @type_map @deserializer_cache.materialize(result) ensure result.clear if result end def exec(sql, params=nil) result = run(sql, params) result.cmd_tuples ensure result.clear if result end def build(sql) Builder.new(self, sql) end private def run(sql, params) if params @conn.async_exec(*process_params(sql, params)) else @conn.async_exec(sql) end end def process_params(sql, params) sql = sql.dup param_array = nil if Hash === params param_array = [] params.each do |k, v| sql.gsub!(":#{k.to_s}", "$#{param_array.length + 1}") param_array << v end elsif Array === params i = 0 sql.gsub!("?") do i += 1 case params[i-1] when Integer then "$#{i}::bigint" when Float then "$#{i}::float8" when String then "$#{i}::text" else "$#{i}::unknown" end end param_array = params end [sql, param_array] end end end