FEATURE: add support for sqlite

FEATURE: add support for sqlite

diff --git a/Guardfile b/Guardfile
index 4048b17..1483d2b 100644
--- a/Guardfile
+++ b/Guardfile
@@ -2,4 +2,5 @@ guard :minitest do
   watch(%r{^test/(.*)_test\.rb$})
   watch(%r{^lib/(.*/)?([^/]+)\.rb$})     { |m| "test/#{m[1]}#{m[2]}_test.rb" }
   watch(%r{^test/test_helper\.rb$})      { 'test' }
+  watch("test/mini_sql/builder_tests.rb") { ['test/mini_sql/postgres/builder_test.rb', 'test/mini_sql/sqlite/builder_test.rb'] }
 end
diff --git a/README.md b/README.md
index fd15fb0..f9933be 100644
--- a/README.md
+++ b/README.md
@@ -18,11 +18,11 @@ Or install it yourself as:
 
 ## Usage
 
-MiniSql is a very simple, safe and fast SQL executor and mapper for PG.
+MiniSql is a very simple, safe and fast SQL executor and mapper for PG and Sqlite.
 
 `‍``ruby
 pg_conn = PG.connect(db_name: 'my_db')
-conn = MiniSql::Connection.new(pg_conn)
+conn = MiniSql::Connection.get(pg_conn)
 
 puts conn.exec('update table set column = 1 where id in (1,2)')
 # returns 2 if 2 rows changed
@@ -79,7 +79,7 @@ end
 # ideally you want to remember to run r.clear here
 
 # this is faster and safer
-conn = MiniSql::Connection.new(pg_conn)
+conn = MiniSql::Connection.get(pg_conn)
 r = conn.query('select * from table')
 
 r.each do |row|
diff --git a/lib/mini_sql.rb b/lib/mini_sql.rb
index c8e544a..0023f08 100644
--- a/lib/mini_sql.rb
+++ b/lib/mini_sql.rb
@@ -10,5 +10,14 @@ require_relative "mini_sql/builder"
 require_relative "mini_sql/inline_param_encoder"
 
 module MiniSql
-  autoload :Coders, "mini_sql/coders"
+  module Postgres
+    autoload :Coders, "mini_sql/postgres/coders"
+    autoload :Connection, "mini_sql/postgres/connection"
+    autoload :DeserializerCache, "mini_sql/postgres/deserializer_cache"
+  end
+
+  module Sqlite
+    autoload :Connection, "mini_sql/sqlite/connection"
+    autoload :DeserializerCache, "mini_sql/sqlite/deserializer_cache"
+  end
 end
diff --git a/lib/mini_sql/coders.rb b/lib/mini_sql/coders.rb
deleted file mode 100644
index 8c71863..0000000
--- a/lib/mini_sql/coders.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-module MiniSql
-  module Coders
-    class NumericCoder < PG::SimpleDecoder
-      def decode(string, tuple = nil, field = nil)
-        BigDecimal.new(string)
-      end
-    end
-
-    class IPAddrCoder < PG::SimpleDecoder
-      def decode(string, tuple = nil, field = nil)
-        IPAddr.new(string)
-      end
-    end
-
-    class TimestampUtc < PG::SimpleDecoder
-      # exact same implementation as Rails here
-      ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
-
-      def decode(string, tuple = nil, field = nil)
-        if string =~ ISO_DATETIME
-          microsec = ($7.to_r * 1_000_000).to_i
-          Time.utc $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
-        else
-          STDERR.puts "unexpected date time format #{string}"
-          string
-        end
-      end
-    end
-
-  end
-end
diff --git a/lib/mini_sql/connection.rb b/lib/mini_sql/connection.rb
index 65040ce..005ebcb 100644
--- a/lib/mini_sql/connection.rb
+++ b/lib/mini_sql/connection.rb
@@ -2,47 +2,15 @@
 
 module MiniSql
   class Connection
-    attr_reader :raw_connection, :type_map, :param_encoder
 
-    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))
-          map.add_coder(MiniSql::Coders::IPAddrCoder.new(name: "cidr", oid: 650, format: 0))
-          map.add_coder(PG::TextDecoder::String.new(name: "tsvector", oid: 3614, format: 0))
-
-          map.rm_coder(0, 1114)
-          if defined? PG::TextDecoder::TimestampUtc
-            # treat timestamp without zone as utc
-            # new to PG 1.1
-            map.add_coder(PG::TextDecoder::TimestampUtc.new(name: "timestamp", oid: 1114, format: 0))
-          else
-            map.add_coder(MiniSql::Coders::TimestampUtc.new(name: "timestamp", oid: 1114, format: 0))
-          end
-          map
-        end
-    end
-
-    # Initialize a new MiniSql::Connection object
-    #
-    # @param raw_connection [PG::Connection] an active connection to PG
-    # @param deserializer_cache [MiniSql::DeserializerCache] a cache of field names to deserializer, can be nil
-    # @param type_map [PG::TypeMap] a type mapper for all results returned, can be nil
-    def initialize(raw_connection, deserializer_cache: nil, param_encoder: nil)
-      # TODO adapter to support other databases
-      @raw_connection = raw_connection
-      @deserializer_cache = deserializer_cache || Connection.default_deserializer_cache
-      @param_encoder = param_encoder || InlineParamEncoder.new(self)
-    end
-
-    def type_map
-      @type_map ||= self.class.type_map(raw_connection)
+    def self.get(raw_connection, options = {})
+      if (defined? ::PG::Connection) && (PG::Connection === raw_connection)
+        Postgres::Connection.new(raw_connection, options)
+      elsif (defined? ::SQLite3::Database) && (SQLite3::Database === raw_connection)
+        Sqlite::Connection.new(raw_connection, options)
+      else
+        raise ArgumentError, 'unknown connection type!'
+      end
     end
 
     # Returns a flat array containing all results.
@@ -52,53 +20,19 @@ module MiniSql
     # @param params [Array or Hash], params to apply to query
     # @return [Object] a flat array containing all results
     def query_single(sql, *params)
-      result = run(sql, params)
-      result.type_map = type_map
-      if result.nfields == 1
-        result.column_values(0)
-      else
-        tuples = result.ntuples
-        fields = result.nfields
-
-        array = []
-        f = 0
-        row = 0
-
-        while row < tuples
-          while f < fields
-            array << result.getvalue(row, f)
-            f += 1
-          end
-          f = 0
-          row += 1
-        end
-        array
-      end
-    ensure
-      result.clear if result
+      raise NotImplementedError, "must be implemented by child connection"
     end
 
     def query(sql, *params)
-      result = run(sql, params)
-      result.type_map = type_map
-      @deserializer_cache.materialize(result)
-    ensure
-      result.clear if result
+      raise NotImplementedError, "must be implemented by child connection"
     end
 
     def exec(sql, *params)
-      result = run(sql, params)
-      result.cmd_tuples
-    ensure
-      result.clear if result
+      raise NotImplementedError, "must be implemented by child connection"
     end
 
     def query_hash(sql, *params)
-      result = run(sql, params)
-      result.type_map = type_map
-      result.to_a
-    ensure
-      result.clear
+      raise NotImplementedError, "must be implemented by child connection"
     end
 
     def build(sql)
@@ -106,16 +40,7 @@ module MiniSql
     end
 
     def escape_string(str)
-      raw_connection.escape_string(str)
-    end
-
-    private
-
-    def run(sql, params)
-      if params && params.length > 0
-        sql = param_encoder.encode(sql, *params)
-      end
-      raw_connection.async_exec(sql)
+      raise NotImplementedError, "must be implemented by child connection"
     end
 
   end
diff --git a/lib/mini_sql/deserializer_cache.rb b/lib/mini_sql/deserializer_cache.rb
index d86e0d1..6d8a827 100644
--- a/lib/mini_sql/deserializer_cache.rb
+++ b/lib/mini_sql/deserializer_cache.rb
@@ -1,65 +1,8 @@
 module MiniSql
   class DeserializerCache
-
-    DEFAULT_MAX_SIZE = 500
-
-    def initialize(max_size = nil)
-      @cache = {}
-      @max_size = max_size || DEFAULT_MAX_SIZE
-    end
-
+    # method takes a raw result and converts to proper objects
     def materialize(result)
-
-      return [] if result.ntuples == 0
-
-      key = result.fields
-
-      # trivial fast LRU implementation

[... diff too long, it was truncated ...]

GitHub sha: 1d4d687a