Skip to content

Commit

Permalink
FEATURE: add query_array (#10)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ermolaev authored and SamSaffron committed Jan 21, 2020
1 parent 6c30995 commit ad86f7e
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 2 deletions.
17 changes: 16 additions & 1 deletion README.md
Expand Up @@ -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
Expand All @@ -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
Expand Down
82 changes: 81 additions & 1 deletion bench/topic_perf.rb
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'bundler/inline'

gemfile do
Expand Down Expand Up @@ -194,7 +196,7 @@ def mini_sql_title_id_query_single
$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
Expand All @@ -219,6 +221,84 @@ def swift_select_title_id(l=1000)
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
Expand Down
4 changes: 4 additions & 0 deletions lib/mini_sql/mysql/connection.rb
Expand Up @@ -20,6 +20,10 @@ def query_hash(sql, *params)
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
Expand Down
8 changes: 8 additions & 0 deletions lib/mini_sql/postgres/connection.rb
Expand Up @@ -78,6 +78,14 @@ def query_single(sql, *params)
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
Expand Down
4 changes: 4 additions & 0 deletions lib/mini_sql/sqlite/connection.rb
Expand Up @@ -26,6 +26,10 @@ def query_hash(sql, *params)
r
end

def query_array(sql, *params)
run(sql, *params)
end

def exec(sql, *params)

start = raw_connection.total_changes
Expand Down
5 changes: 5 additions & 0 deletions test/mini_sql/connection_tests.rb
Expand Up @@ -76,6 +76,11 @@ def test_query_hash
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)
Expand Down

0 comments on commit ad86f7e

Please sign in to comment.