FEATURE: add query_array (#10)

FEATURE: add query_array (#10)

Add query_array which allows you to select an array of tuple values.

This differs from query single in that you get one element in the array per row.

diff --git a/README.md b/README.md
index fb8ff95..95ebee2 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,12 @@ p conn.query_single('select 1 union select 2')
 
 p conn.query_hash('select 1 as a, 2 as b union select 3, 4')
 # [{"a" => 1, "b"=> 1},{"a" => 3, "b" => 4}
+ 
+p conn.query_array("select 1 as a, '2' as b union select 3, 'e'")
+# [[1, '2'], [3, 'e']]
+ 
+p conn.query_array("select 1 as a, '2' as b union select 3, 'e'").to_h
+# {1 => '2', 3 => 'e'}
 `‍``
 
 ## The query builder
@@ -63,9 +69,18 @@ end
 The builder allows for `order_by`, `where`, `select`, `set`, `limit`, `join`, `left_join` and `offset`.
 
 ## Is it fast?
-
 Yes, it is very fast. See benchmarks in [the bench directory](https://github.com/discourse/mini_sql/tree/master/bench).
 
+**Comparison mini_sql methods**
+`‍``
+query_array     1351.6 i/s
+      query      963.8 i/s - 1.40x  slower
+ query_hash      787.4 i/s - 1.72x  slower
+
+query_single('select id from topics limit 1000')             2368.9 i/s
+ query_array('select id from topics limit 1000').flatten     1350.1 i/s - 1.75x  slower
+`‍``
+
 As a rule it will outperform similar naive PG code while remaining safe.
 
 `‍``ruby
diff --git a/bench/topic_perf.rb b/bench/topic_perf.rb
index cc7c93b..6f0fae5 100644
--- a/bench/topic_perf.rb
+++ b/bench/topic_perf.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'bundler/inline'
 
 gemfile do
@@ -194,7 +196,7 @@ end
 $swift = Swift::DB::Postgres.new(db: "test_db")
 
 def swift_select_title_id(l=1000)
-  s = ""
+  s = +''
   i = 0
   r = $swift.execute("select id, title from topics order by id limit 1000")
   while i < r.selected_rows
@@ -219,6 +221,84 @@ results = [
 exit(-1) unless results.uniq.length == 1
 
 
+#Benchmark.ips do |r|
+#  r.report('string') do |n|
+#    while n > 0
+#      s = +''
+#      1_000.times { |i| s << i; s << i }
+#      n -= 1
+#    end
+#  end
+#  r.report('array') do |n|
+#    while n > 0
+#      1_000.times { |i| [i, i] }
+#      n -= 1
+#    end
+#  end
+#
+#  r.compare!
+#end
+
+# Comparison:
+#   array:    13041.2 i/s
+#  string:     4254.9 i/s - 3.06x  slower
+
+Benchmark.ips do |r|
+  r.report('query_hash') do |n|
+    while n > 0
+      $mini_sql.query_hash('select id, title from topics order by id limit 1000').each do |hash|
+        [hash['id'], hash['title']]
+      end
+      n -= 1
+    end
+  end
+  r.report('query_array') do |n|
+    while n > 0
+      $mini_sql.query_array('select id, title from topics order by id limit 1000').each do |id, title|
+        [id, title]
+      end
+      n -= 1
+    end
+  end
+  r.report('query') do |n|
+    while n > 0
+      $mini_sql.query('select id, title from topics order by id limit 1000').each do |obj|
+        [obj.id, obj.title]
+      end
+      n -= 1
+    end
+  end
+
+  r.compare!
+end
+
+# Comparison:
+#         query_array:     1351.6 i/s
+#               query:      963.8 i/s - 1.40x  slower
+#          query_hash:      787.4 i/s - 1.72x  slower
+
+
+Benchmark.ips do |r|
+  r.report('query_single') do |n|
+    while n > 0
+      $mini_sql.query_single('select id from topics order by id limit 1000')
+      n -= 1
+    end
+  end
+  r.report('query_array') do |n|
+    while n > 0
+      $mini_sql.query_array('select id from topics order by id limit 1000').flatten
+      n -= 1
+    end
+  end
+
+  r.compare!
+end
+
+# Comparison:
+#        query_single:     2368.9 i/s
+#         query_array:     1350.1 i/s - 1.75x  slower
+
 Benchmark.ips do |r|
   r.report("ar select title id") do |n|
     while n > 0
diff --git a/lib/mini_sql/mysql/connection.rb b/lib/mini_sql/mysql/connection.rb
index e762748..9c528d3 100644
--- a/lib/mini_sql/mysql/connection.rb
+++ b/lib/mini_sql/mysql/connection.rb
@@ -20,6 +20,10 @@ module MiniSql
         result.to_a
       end
 
+      def query_array(sql, *params)
+        run(sql, :array, params).to_a
+      end
+
       def exec(sql, *params)
         run(sql, :array, params)
         raw_connection.affected_rows
diff --git a/lib/mini_sql/postgres/connection.rb b/lib/mini_sql/postgres/connection.rb
index d1a1b2c..de2fb0b 100644
--- a/lib/mini_sql/postgres/connection.rb
+++ b/lib/mini_sql/postgres/connection.rb
@@ -78,6 +78,14 @@ module MiniSql
         result.clear if result
       end
 
+      def query_array(sql, *params)
+        result = run(sql, params)
+        result.type_map = type_map
+        result.values
+       ensure
+        result.clear if result
+      end
+
       def query(sql, *params)
         result = run(sql, params)
         result.type_map = type_map
diff --git a/lib/mini_sql/sqlite/connection.rb b/lib/mini_sql/sqlite/connection.rb
index 2f9c5c7..d021aa2 100644
--- a/lib/mini_sql/sqlite/connection.rb
+++ b/lib/mini_sql/sqlite/connection.rb
@@ -26,6 +26,10 @@ module MiniSql
         r
       end
 
+      def query_array(sql, *params)
+        run(sql, *params)
+      end
+
       def exec(sql, *params)
 
         start = raw_connection.total_changes
diff --git a/test/mini_sql/connection_tests.rb b/test/mini_sql/connection_tests.rb
index 5bb649a..096d0e7 100644
--- a/test/mini_sql/connection_tests.rb
+++ b/test/mini_sql/connection_tests.rb
@@ -76,6 +76,11 @@ module MiniSql::ConnectionTests
     assert_equal([{ "a" => 1, "b" => '2' }, { "a" => 3, "b" => "e" }], r)
   end
 
+  def test_query_array
+    r = @connection.query_array("select 1 as a, '2' as b union select 3, 'e'")
+    assert_equal([[1, '2'], [3, 'e']], r)
+  end
+
   def test_too_many_params_hash
     r = @connection.query_single("select 100", {a: 99})
     assert_equal(r[0], 100)

GitHub sha: ad86f7e6