mysql2-nested_hash_bind 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.env.example +5 -0
- data/.rspec +3 -0
- data/.rubocop.yml +46 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +96 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +106 -0
- data/benchmark/tmp/.keep +0 -0
- data/benchmark/xquery_bench.rb +148 -0
- data/benchmark/xquery_stackprof.rb +80 -0
- data/lib/mysql2/nested_hash_bind/query_extension.rb +139 -0
- data/lib/mysql2/nested_hash_bind/version.rb +7 -0
- data/lib/mysql2/nested_hash_bind.rb +14 -0
- data/lib/mysql2-nested_hash_bind.rb +3 -0
- data/mysql2-nested_hash_bind.gemspec +52 -0
- data/sig/mysql2/nested_hash_bind.rbs +6 -0
- metadata +248 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6e539ad716d3ade8fb1bcd312f188b5266704d090d330e3dc28e6aaf4629adad
|
4
|
+
data.tar.gz: 8f53a6de9feaaca5e105d96aa7e2b9c8cb5ff4656d7a4a2d675e6b66e7671be5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 37f1014bae85abb91d12613f7cea23f2b0a574db4306d3fd5dc7979d80f30731d9d54e590ea8ba155561ad44dbd308dbb8e5519ef01b56c26b43ef78622a5fa1
|
7
|
+
data.tar.gz: a8dd965a418ac76c7021771875a69a56a17505bcd53073c696b5bb849669d71dd0e2edfb4df61cfc823de6e23cb97638683acf50619fca0b70119b0b54ddbc0f
|
data/.env.example
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-performance
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
NewCops: enable
|
6
|
+
SuggestExtensions: false
|
7
|
+
TargetRubyVersion: 3.0
|
8
|
+
|
9
|
+
Layout/LineLength:
|
10
|
+
Max: 120
|
11
|
+
|
12
|
+
Metrics/BlockLength:
|
13
|
+
Exclude:
|
14
|
+
- "spec/**/*"
|
15
|
+
- "*.gemspec"
|
16
|
+
|
17
|
+
Naming/FileName:
|
18
|
+
Exclude:
|
19
|
+
- lib/mysql2-nested_hash_bind.rb
|
20
|
+
|
21
|
+
Style/SymbolArray:
|
22
|
+
Exclude:
|
23
|
+
# Exclude for rspec-its syntax
|
24
|
+
- spec/**/*_spec.rb
|
25
|
+
|
26
|
+
Style/StringLiterals:
|
27
|
+
Enabled: true
|
28
|
+
EnforcedStyle: double_quotes
|
29
|
+
|
30
|
+
Style/StringLiteralsInInterpolation:
|
31
|
+
Enabled: true
|
32
|
+
EnforcedStyle: double_quotes
|
33
|
+
|
34
|
+
Style/TrailingCommaInArguments:
|
35
|
+
EnforcedStyleForMultiline: comma
|
36
|
+
|
37
|
+
Style/TrailingCommaInArrayLiteral:
|
38
|
+
EnforcedStyleForMultiline: comma
|
39
|
+
|
40
|
+
Style/TrailingCommaInHashLiteral:
|
41
|
+
EnforcedStyleForMultiline: comma
|
42
|
+
|
43
|
+
Style/WordArray:
|
44
|
+
Exclude:
|
45
|
+
# Exclude for rspec-its syntax
|
46
|
+
- spec/**/*_spec.rb
|
data/.yardopts
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 sue445
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# mysql2-nested_hash_bind
|
2
|
+
[mysql2](https://github.com/brianmario/mysql2) and [mysql2-cs-bind](https://github.com/tagomoris/mysql2-cs-bind) extension to bind response to nested `Hash`.
|
3
|
+
|
4
|
+
This is inspired by https://github.com/jmoiron/sqlx
|
5
|
+
|
6
|
+
[](https://github.com/sue445/mysql2-nested_hash_bind/actions/workflows/test.yml)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'mysql2-nested_hash_bind'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle install
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install mysql2-nested_hash_bind
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
Write `require "mysql2-nested_hash_bind"` and `using Mysql2::NestedHashBind::QueryExtension` in your code
|
26
|
+
|
27
|
+
## Example
|
28
|
+
```ruby
|
29
|
+
require "mysql2-nested_hash_bind"
|
30
|
+
|
31
|
+
using Mysql2::NestedHashBind::QueryExtension
|
32
|
+
|
33
|
+
db = Mysql2::Client.new(
|
34
|
+
host: ENV.fetch("MYSQL_HOST", "127.0.0.1"),
|
35
|
+
port: ENV.fetch("MYSQL_PORT", "3306"),
|
36
|
+
username: ENV.fetch("MYSQL_USERNAME"),
|
37
|
+
database: ENV.fetch("MYSQL_DATABASE"),
|
38
|
+
password: ENV.fetch("MYSQL_PASSWORD", ""),
|
39
|
+
charset: "utf8mb4",
|
40
|
+
database_timezone: :local,
|
41
|
+
cast_booleans: true,
|
42
|
+
symbolize_keys: true,
|
43
|
+
reconnect: true,
|
44
|
+
)
|
45
|
+
|
46
|
+
rows = db.query(<<~SQL)
|
47
|
+
SELECT
|
48
|
+
`posts`.`id`,
|
49
|
+
`posts`.`user_id`,
|
50
|
+
`posts`.`body`,
|
51
|
+
`users`.`account_name` AS `users.account_name`,
|
52
|
+
`users`.`authority` AS `users.authority`,
|
53
|
+
`users`.`del_flg` AS `users.del_flg`
|
54
|
+
FROM `posts`
|
55
|
+
INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
|
56
|
+
SQL
|
57
|
+
|
58
|
+
rows.first
|
59
|
+
#=> {:id=>1, :user_id=>445, :body=>"test", :users=>{:account_name=>"sue445", :authority=>false, :del_flg=>false}}
|
60
|
+
```
|
61
|
+
|
62
|
+
If you do not write `using Mysql2::NestedHashBind::QueryExtension`, it will look like this. (This is the original behavior of `Mysql2::Client#query` and `Mysql2::Client#xquery`)
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
rows.first
|
66
|
+
#=> {:id=>1, :user_id=>445, :body=>"test", :"users.account_name"=>"sue445", :"users.authority"=>false, :"users.del_flg"=>false}
|
67
|
+
```
|
68
|
+
|
69
|
+
## Note
|
70
|
+
* If exists columns containing dots, `Mysql2::Client#query` and `Mysql2::Client#xquery` returns `Array<Hash>`
|
71
|
+
* If no exists columns containing dots, `Mysql2::Client#query` and `Mysql2::Client#xquery` returns [Mysql2::Result](https://www.rubydoc.info/gems/mysql2/Mysql2/Result) (This is the original behavior of `Mysql2::Client#query` and `Mysql2::Client#xquery`)
|
72
|
+
|
73
|
+
## Development
|
74
|
+
At first, create test database.
|
75
|
+
|
76
|
+
e.g.
|
77
|
+
|
78
|
+
```sql
|
79
|
+
CREATE DATABASE mysql2_test;
|
80
|
+
```
|
81
|
+
|
82
|
+
```bash
|
83
|
+
cp .env.example .env
|
84
|
+
vi .env
|
85
|
+
```
|
86
|
+
|
87
|
+
## Benchmark
|
88
|
+
See [benchmark/](benchmark/)
|
89
|
+
|
90
|
+
## Contributing
|
91
|
+
|
92
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sue445/mysql2-nested_hash_bind.
|
93
|
+
|
94
|
+
## License
|
95
|
+
|
96
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/benchmark/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Benchmark report
|
2
|
+
## Setup
|
3
|
+
```bash
|
4
|
+
cp ../.env.example .env
|
5
|
+
vi .env
|
6
|
+
```
|
7
|
+
|
8
|
+
## [xquery_bench.rb](xquery_bench.rb)
|
9
|
+
```bash
|
10
|
+
$ ruby -v
|
11
|
+
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin21]
|
12
|
+
|
13
|
+
$ bundle exec ruby xquery_bench.rb
|
14
|
+
============== Benchmark with LIMIT 10, without to_a ==============
|
15
|
+
Warming up --------------------------------------
|
16
|
+
Mysql2::Client#xquery
|
17
|
+
490.000 i/100ms
|
18
|
+
Mysql2::Client#xquery(sql_with_dot) using Mysql2::NestedHashBind::QueryExtension
|
19
|
+
362.000 i/100ms
|
20
|
+
Mysql2::Client#xquery(sql_without_dot) using Mysql2::NestedHashBind::QueryExtension
|
21
|
+
483.000 i/100ms
|
22
|
+
Calculating -------------------------------------
|
23
|
+
Mysql2::Client#xquery
|
24
|
+
5.107k (± 3.1%) i/s - 25.970k in 5.090386s
|
25
|
+
Mysql2::Client#xquery(sql_with_dot) using Mysql2::NestedHashBind::QueryExtension
|
26
|
+
3.649k (± 3.3%) i/s - 18.462k in 5.064792s
|
27
|
+
Mysql2::Client#xquery(sql_without_dot) using Mysql2::NestedHashBind::QueryExtension
|
28
|
+
4.804k (± 4.3%) i/s - 24.150k in 5.037482s
|
29
|
+
|
30
|
+
Comparison:
|
31
|
+
Mysql2::Client#xquery: 5106.6 i/s
|
32
|
+
Mysql2::Client#xquery(sql_without_dot) using Mysql2::NestedHashBind::QueryExtension: 4803.5 i/s - same-ish: difference falls within error
|
33
|
+
Mysql2::Client#xquery(sql_with_dot) using Mysql2::NestedHashBind::QueryExtension: 3649.1 i/s - 1.40x (± 0.00) slower
|
34
|
+
|
35
|
+
============== Benchmark with LIMIT 10, with to_a ==============
|
36
|
+
Warming up --------------------------------------
|
37
|
+
Mysql2::Client#xquery.to_a
|
38
|
+
446.000 i/100ms
|
39
|
+
Mysql2::Client#xquery(sql_with_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
40
|
+
359.000 i/100ms
|
41
|
+
Mysql2::Client#xquery(sql_without_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
42
|
+
451.000 i/100ms
|
43
|
+
Calculating -------------------------------------
|
44
|
+
Mysql2::Client#xquery.to_a
|
45
|
+
4.440k (± 4.7%) i/s - 22.300k in 5.035494s
|
46
|
+
Mysql2::Client#xquery(sql_with_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
47
|
+
3.587k (± 7.1%) i/s - 17.950k in 5.034172s
|
48
|
+
Mysql2::Client#xquery(sql_without_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
49
|
+
4.330k (± 4.3%) i/s - 21.648k in 5.009239s
|
50
|
+
|
51
|
+
Comparison:
|
52
|
+
Mysql2::Client#xquery.to_a: 4439.7 i/s
|
53
|
+
Mysql2::Client#xquery(sql_without_dot).to_a using Mysql2::NestedHashBind::QueryExtension: 4330.2 i/s - same-ish: difference falls within error
|
54
|
+
Mysql2::Client#xquery(sql_with_dot).to_a using Mysql2::NestedHashBind::QueryExtension: 3587.1 i/s - 1.24x (± 0.00) slower
|
55
|
+
|
56
|
+
============== Benchmark with LIMIT 100, without to_a ==============
|
57
|
+
Warming up --------------------------------------
|
58
|
+
Mysql2::Client#xquery
|
59
|
+
223.000 i/100ms
|
60
|
+
Mysql2::Client#xquery(sql_with_dot) using Mysql2::NestedHashBind::QueryExtension
|
61
|
+
94.000 i/100ms
|
62
|
+
Mysql2::Client#xquery(sql_without_dot) using Mysql2::NestedHashBind::QueryExtension
|
63
|
+
224.000 i/100ms
|
64
|
+
Calculating -------------------------------------
|
65
|
+
Mysql2::Client#xquery
|
66
|
+
2.368k (± 4.8%) i/s - 11.819k in 5.002685s
|
67
|
+
Mysql2::Client#xquery(sql_with_dot) using Mysql2::NestedHashBind::QueryExtension
|
68
|
+
942.892 (± 4.9%) i/s - 4.794k in 5.096560s
|
69
|
+
Mysql2::Client#xquery(sql_without_dot) using Mysql2::NestedHashBind::QueryExtension
|
70
|
+
2.183k (± 9.7%) i/s - 10.976k in 5.069075s
|
71
|
+
|
72
|
+
Comparison:
|
73
|
+
Mysql2::Client#xquery: 2368.0 i/s
|
74
|
+
Mysql2::Client#xquery(sql_without_dot) using Mysql2::NestedHashBind::QueryExtension: 2183.3 i/s - same-ish: difference falls within error
|
75
|
+
Mysql2::Client#xquery(sql_with_dot) using Mysql2::NestedHashBind::QueryExtension: 942.9 i/s - 2.51x (± 0.00) slower
|
76
|
+
|
77
|
+
============== Benchmark with LIMIT 100, with to_a ==============
|
78
|
+
Warming up --------------------------------------
|
79
|
+
Mysql2::Client#xquery.to_a
|
80
|
+
157.000 i/100ms
|
81
|
+
Mysql2::Client#xquery(sql_with_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
82
|
+
93.000 i/100ms
|
83
|
+
Mysql2::Client#xquery(sql_without_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
84
|
+
141.000 i/100ms
|
85
|
+
Calculating -------------------------------------
|
86
|
+
Mysql2::Client#xquery.to_a
|
87
|
+
1.478k (± 2.8%) i/s - 7.536k in 5.102039s
|
88
|
+
Mysql2::Client#xquery(sql_with_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
89
|
+
947.848 (± 4.3%) i/s - 4.743k in 5.013320s
|
90
|
+
Mysql2::Client#xquery(sql_without_dot).to_a using Mysql2::NestedHashBind::QueryExtension
|
91
|
+
1.449k (± 3.6%) i/s - 7.332k in 5.066700s
|
92
|
+
|
93
|
+
Comparison:
|
94
|
+
Mysql2::Client#xquery.to_a: 1478.3 i/s
|
95
|
+
Mysql2::Client#xquery(sql_without_dot).to_a using Mysql2::NestedHashBind::QueryExtension: 1449.1 i/s - same-ish: difference falls within error
|
96
|
+
Mysql2::Client#xquery(sql_with_dot).to_a using Mysql2::NestedHashBind::QueryExtension: 947.8 i/s - 1.56x (± 0.00) slower
|
97
|
+
```
|
98
|
+
|
99
|
+
## [xquery_stackprof.rb](xquery_stackprof.rb)
|
100
|
+
### Usage
|
101
|
+
```bash
|
102
|
+
bundle exec ruby xquery_stackprof.rb
|
103
|
+
bundle exec stackprof-webnav -d tmp/
|
104
|
+
```
|
105
|
+
|
106
|
+
open http://localhost:9292/
|
data/benchmark/tmp/.keep
ADDED
File without changes
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# rubocop:disable all
|
2
|
+
|
3
|
+
require "mysql2-nested_hash_bind"
|
4
|
+
require "benchmark/ips"
|
5
|
+
require "dotenv"
|
6
|
+
require "securerandom"
|
7
|
+
|
8
|
+
Dotenv.load(File.join(__dir__, "..", ".env"))
|
9
|
+
|
10
|
+
require_relative "../spec/support/database_helper"
|
11
|
+
|
12
|
+
class DatabaseClient
|
13
|
+
SELECT_SQL_WITH_DOT_FORMAT = <<~SQL
|
14
|
+
SELECT
|
15
|
+
`posts`.`id`,
|
16
|
+
`posts`.`user_id`,
|
17
|
+
`posts`.`body`,
|
18
|
+
`users`.`account_name` AS `users.account_name`,
|
19
|
+
`users`.`authority` AS `users.authority`,
|
20
|
+
`users`.`del_flg` AS `users.del_flg`
|
21
|
+
FROM `posts`
|
22
|
+
INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
|
23
|
+
LIMIT %d
|
24
|
+
SQL
|
25
|
+
|
26
|
+
SELECT_SQL_WITHOUT_DOT_FORMAT = <<~SQL
|
27
|
+
SELECT
|
28
|
+
`posts`.`id`,
|
29
|
+
`posts`.`user_id`,
|
30
|
+
`posts`.`body`,
|
31
|
+
`users`.`account_name` AS `users_account_name`,
|
32
|
+
`users`.`authority` AS `users_authority`,
|
33
|
+
`users`.`del_flg` AS `users_del_flg`
|
34
|
+
FROM `posts`
|
35
|
+
INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
|
36
|
+
LIMIT %d
|
37
|
+
SQL
|
38
|
+
|
39
|
+
attr_reader :db
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@db = DatabaseHelper.client
|
43
|
+
end
|
44
|
+
|
45
|
+
def setup
|
46
|
+
db.query(<<~SQL)
|
47
|
+
CREATE TABLE `users` (
|
48
|
+
`id` int NOT NULL AUTO_INCREMENT,
|
49
|
+
`account_name` varchar(64) NOT NULL,
|
50
|
+
`authority` tinyint(1) NOT NULL DEFAULT '0',
|
51
|
+
`del_flg` tinyint(1) NOT NULL DEFAULT '0',
|
52
|
+
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
53
|
+
PRIMARY KEY (`id`),
|
54
|
+
UNIQUE KEY `account_name` (`account_name`)
|
55
|
+
)
|
56
|
+
SQL
|
57
|
+
|
58
|
+
db.query(<<~SQL)
|
59
|
+
CREATE TABLE `posts` (
|
60
|
+
`id` int NOT NULL AUTO_INCREMENT,
|
61
|
+
`user_id` int NOT NULL,
|
62
|
+
`body` text NOT NULL,
|
63
|
+
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
64
|
+
PRIMARY KEY (`id`)
|
65
|
+
)
|
66
|
+
SQL
|
67
|
+
|
68
|
+
(1000...1100).each do |id|
|
69
|
+
db.xquery(<<~SQL, id, SecureRandom.uuid)
|
70
|
+
INSERT INTO `users` (`id`, `account_name`) VALUES(?, ?)
|
71
|
+
SQL
|
72
|
+
|
73
|
+
db.xquery(<<~SQL, id, id)
|
74
|
+
INSERT INTO `posts` (`id`, `user_id`, `body`) VALUES(?, ?, 'test')
|
75
|
+
SQL
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def teardown
|
80
|
+
db.query("DROP TABLE IF EXISTS `posts`")
|
81
|
+
db.query("DROP TABLE IF EXISTS `users`")
|
82
|
+
end
|
83
|
+
|
84
|
+
def select_with_dot(limit)
|
85
|
+
db.xquery(SELECT_SQL_WITH_DOT_FORMAT % limit)
|
86
|
+
end
|
87
|
+
|
88
|
+
def select_without_dot(limit)
|
89
|
+
db.xquery(SELECT_SQL_WITHOUT_DOT_FORMAT % limit)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class DatabaseClientWithQueryExtension < DatabaseClient
|
94
|
+
using Mysql2::NestedHashBind::QueryExtension
|
95
|
+
|
96
|
+
def select_with_dot(limit)
|
97
|
+
db.xquery(SELECT_SQL_WITH_DOT_FORMAT % limit)
|
98
|
+
end
|
99
|
+
|
100
|
+
def select_without_dot(limit)
|
101
|
+
db.xquery(SELECT_SQL_WITHOUT_DOT_FORMAT % limit)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
client = DatabaseClient.new
|
106
|
+
patched_client = DatabaseClientWithQueryExtension.new
|
107
|
+
|
108
|
+
begin
|
109
|
+
client.setup
|
110
|
+
|
111
|
+
[10, 100].each do |limit|
|
112
|
+
puts "============== Benchmark with LIMIT #{limit}, without to_a =============="
|
113
|
+
Benchmark.ips do |x|
|
114
|
+
x.report("Mysql2::Client#xquery") do
|
115
|
+
client.select_with_dot(limit)
|
116
|
+
end
|
117
|
+
|
118
|
+
x.report("Mysql2::Client#xquery(sql_with_dot) using Mysql2::NestedHashBind::QueryExtension") do
|
119
|
+
patched_client.select_with_dot(limit)
|
120
|
+
end
|
121
|
+
|
122
|
+
x.report("Mysql2::Client#xquery(sql_without_dot) using Mysql2::NestedHashBind::QueryExtension") do
|
123
|
+
patched_client.select_without_dot(limit)
|
124
|
+
end
|
125
|
+
|
126
|
+
x.compare!
|
127
|
+
end
|
128
|
+
|
129
|
+
puts "============== Benchmark with LIMIT #{limit}, with to_a =============="
|
130
|
+
Benchmark.ips do |x|
|
131
|
+
x.report("Mysql2::Client#xquery.to_a") do
|
132
|
+
client.select_with_dot(limit).to_a
|
133
|
+
end
|
134
|
+
|
135
|
+
x.report("Mysql2::Client#xquery(sql_with_dot).to_a using Mysql2::NestedHashBind::QueryExtension") do
|
136
|
+
patched_client.select_with_dot(limit).to_a
|
137
|
+
end
|
138
|
+
|
139
|
+
x.report("Mysql2::Client#xquery(sql_without_dot).to_a using Mysql2::NestedHashBind::QueryExtension") do
|
140
|
+
patched_client.select_without_dot(limit).to_a
|
141
|
+
end
|
142
|
+
|
143
|
+
x.compare!
|
144
|
+
end
|
145
|
+
end
|
146
|
+
ensure
|
147
|
+
client.teardown
|
148
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# rubocop:disable all
|
2
|
+
|
3
|
+
require "mysql2-nested_hash_bind"
|
4
|
+
require "dotenv"
|
5
|
+
require "securerandom"
|
6
|
+
require "stackprof"
|
7
|
+
|
8
|
+
Dotenv.load(File.join(__dir__, "..", ".env"))
|
9
|
+
|
10
|
+
using Mysql2::NestedHashBind::QueryExtension
|
11
|
+
|
12
|
+
require_relative "../spec/support/database_helper"
|
13
|
+
|
14
|
+
@db = DatabaseHelper.client
|
15
|
+
|
16
|
+
def setup
|
17
|
+
@db.query(<<~SQL)
|
18
|
+
CREATE TABLE `users` (
|
19
|
+
`id` int NOT NULL AUTO_INCREMENT,
|
20
|
+
`account_name` varchar(64) NOT NULL,
|
21
|
+
`authority` tinyint(1) NOT NULL DEFAULT '0',
|
22
|
+
`del_flg` tinyint(1) NOT NULL DEFAULT '0',
|
23
|
+
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
24
|
+
PRIMARY KEY (`id`),
|
25
|
+
UNIQUE KEY `account_name` (`account_name`)
|
26
|
+
)
|
27
|
+
SQL
|
28
|
+
|
29
|
+
@db.query(<<~SQL)
|
30
|
+
CREATE TABLE `posts` (
|
31
|
+
`id` int NOT NULL AUTO_INCREMENT,
|
32
|
+
`user_id` int NOT NULL,
|
33
|
+
`body` text NOT NULL,
|
34
|
+
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
35
|
+
PRIMARY KEY (`id`)
|
36
|
+
)
|
37
|
+
SQL
|
38
|
+
|
39
|
+
(1000...1100).each do |id|
|
40
|
+
@db.xquery(<<~SQL, id, SecureRandom.uuid)
|
41
|
+
INSERT INTO `users` (`id`, `account_name`) VALUES(?, ?)
|
42
|
+
SQL
|
43
|
+
|
44
|
+
@db.xquery(<<~SQL, id, id)
|
45
|
+
INSERT INTO `posts` (`id`, `user_id`, `body`) VALUES(?, ?, 'test')
|
46
|
+
SQL
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def perform
|
51
|
+
1000.times do
|
52
|
+
@db.xquery(<<~SQL)
|
53
|
+
SELECT
|
54
|
+
`posts`.`id`,
|
55
|
+
`posts`.`user_id`,
|
56
|
+
`posts`.`body`,
|
57
|
+
`users`.`account_name` AS `users.account_name`,
|
58
|
+
`users`.`authority` AS `users.authority`,
|
59
|
+
`users`.`del_flg` AS `users.del_flg`
|
60
|
+
FROM `posts`
|
61
|
+
INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
|
62
|
+
SQL
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def teardown
|
67
|
+
@db.query("DROP TABLE IF EXISTS `posts`")
|
68
|
+
@db.query("DROP TABLE IF EXISTS `users`")
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
setup
|
73
|
+
|
74
|
+
StackProf.run(mode: :cpu, raw: true, out: "#{__dir__}/tmp/stackprof_#{Time.now.strftime("%Y%m%d_%H%M%S")}.dump") do
|
75
|
+
perform
|
76
|
+
end
|
77
|
+
|
78
|
+
ensure
|
79
|
+
teardown
|
80
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mysql2
|
4
|
+
module NestedHashBind
|
5
|
+
# Apply patches to `Mysql2::Client#query` and `Mysql2::Client#xquery`
|
6
|
+
module QueryExtension
|
7
|
+
# @!method query(sql, **options)
|
8
|
+
#
|
9
|
+
# @param sql [String]
|
10
|
+
# @param options [Hash]
|
11
|
+
#
|
12
|
+
# @return [Array<Hash>] Exists columns containing dots
|
13
|
+
# @return [Mysql2::Result] No columns containing dots (This is the original behavior of `Mysql2::Client#query`)
|
14
|
+
# @return [nil] No response was returned. (e.g. `ROLLBACK`)
|
15
|
+
#
|
16
|
+
# @see https://www.rubydoc.info/gems/mysql2/Mysql2/Client#query-instance_method
|
17
|
+
|
18
|
+
# @!method xquery(sql, *args, **options)
|
19
|
+
#
|
20
|
+
# @param sql [String]
|
21
|
+
# @param args [Array]
|
22
|
+
# @param options [Hash]
|
23
|
+
#
|
24
|
+
# @return [Array<Hash>] Exists columns containing dots
|
25
|
+
# @return [Mysql2::Result] No columns containing dots (This is the original behavior of `Mysql2::Client#xquery`)
|
26
|
+
# @return [nil] No response was returned. (e.g. `ROLLBACK`)
|
27
|
+
#
|
28
|
+
# @see https://rubydoc.info/gems/mysql2-cs-bind/Mysql2/Client#xquery-instance_method
|
29
|
+
|
30
|
+
refine(Mysql2::Client) do
|
31
|
+
# @param sql [String]
|
32
|
+
# @param options [Hash]
|
33
|
+
#
|
34
|
+
# @return [Array<Hash>] Exists columns containing dots
|
35
|
+
# @return [Mysql2::Result] No columns containing dots (This is the original behavior of `Mysql2::Client#query`)
|
36
|
+
# @return [nil] No response was returned. (e.g. `ROLLBACK`)
|
37
|
+
def query_with_bind(sql, **options)
|
38
|
+
rows = query_without_bind(sql, **options)
|
39
|
+
|
40
|
+
__transform_rows(rows)
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :query_without_bind, :query
|
44
|
+
alias_method :query, :query_with_bind
|
45
|
+
|
46
|
+
# @param sql [String]
|
47
|
+
# @param args [Array]
|
48
|
+
# @param options [Hash]
|
49
|
+
#
|
50
|
+
# @return [Array<Hash>] Exists columns containing dots
|
51
|
+
# @return [Mysql2::Result] No columns containing dots (This is the original behavior of `Mysql2::Client#xquery`)
|
52
|
+
# @return [nil] No response was returned. (e.g. `ROLLBACK`)
|
53
|
+
def xquery_with_bind(sql, *args, **options)
|
54
|
+
rows = xquery_without_bind(sql, *args, **options)
|
55
|
+
|
56
|
+
__transform_rows(rows)
|
57
|
+
end
|
58
|
+
|
59
|
+
alias_method :xquery_without_bind, :xquery
|
60
|
+
alias_method :xquery, :xquery_with_bind
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# @param [Mysql2::Result,nil] rows
|
65
|
+
#
|
66
|
+
# @return [Array<Hash>] Exists columns containing dots
|
67
|
+
# @return [Mysql2::Result] No columns containing dots
|
68
|
+
# (This is the original behavior of `Mysql2::Client#query` and `Mysql2::Client#xquery`)
|
69
|
+
# @return [nil] No response was returned. (e.g. `ROLLBACK`)
|
70
|
+
def __transform_rows(rows)
|
71
|
+
column_names = rows&.first&.keys
|
72
|
+
|
73
|
+
# No columns containing dots
|
74
|
+
return rows unless column_names
|
75
|
+
|
76
|
+
return rows unless column_names.any? { |column_name| __include_column_name_dot?(column_name) }
|
77
|
+
|
78
|
+
# NOTE: Caching result of `column_name.split`
|
79
|
+
columns_cache = __split_columns(column_names)
|
80
|
+
|
81
|
+
rows.map { |row| __transform_row(row: row, columns_cache: columns_cache) }
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param column_names [Array<String,Symbol>]
|
85
|
+
# @return [Hash{String,Symbol=>Array<String,Symbol>}]
|
86
|
+
def __split_columns(column_names)
|
87
|
+
column_names.each_with_object({}) do |column_name, hash|
|
88
|
+
str_key = column_name.respond_to?(:name) ? column_name.name : column_name
|
89
|
+
parent_key, child_key = *str_key.split(".", 2)
|
90
|
+
|
91
|
+
next unless child_key
|
92
|
+
|
93
|
+
if query_options[:symbolize_keys]
|
94
|
+
parent_key = parent_key.to_sym
|
95
|
+
child_key = child_key.to_sym
|
96
|
+
end
|
97
|
+
|
98
|
+
hash[column_name] = { parent_key: parent_key, child_key: child_key }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param column_name [String,Symbol]
|
103
|
+
# @return [Boolean]
|
104
|
+
def __include_column_name_dot?(column_name)
|
105
|
+
# NOTE: Call Symbol#name if possible
|
106
|
+
return column_name.name.include?(".") if column_name.respond_to?(:name)
|
107
|
+
|
108
|
+
column_name.include?(".")
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param row [Hash]
|
112
|
+
# @param columns_cache [Hash{String,Symbol=>Array<String,Symbol>}]
|
113
|
+
#
|
114
|
+
# @return [Hash]
|
115
|
+
def __transform_row(row:, columns_cache:)
|
116
|
+
row.each_with_object({}) do |(k, v), new_row|
|
117
|
+
if columns_cache[k]
|
118
|
+
__update_for_nested_hash(row: new_row, key: k, value: v, columns_cache: columns_cache)
|
119
|
+
else
|
120
|
+
new_row[k] = v
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# @param row [Hash]
|
126
|
+
# @param key [String,Symbol]
|
127
|
+
# @param value [Object]
|
128
|
+
# @param columns_cache [Hash{String,Symbol=>Array<String,Symbol>}]
|
129
|
+
def __update_for_nested_hash(row:, key:, value:, columns_cache:)
|
130
|
+
parent_key = columns_cache[key][:parent_key]
|
131
|
+
child_key = columns_cache[key][:child_key]
|
132
|
+
|
133
|
+
row[parent_key] ||= {}
|
134
|
+
row[parent_key][child_key] = value
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mysql2"
|
4
|
+
require "mysql2-cs-bind"
|
5
|
+
|
6
|
+
require_relative "nested_hash_bind/version"
|
7
|
+
require_relative "nested_hash_bind/query_extension"
|
8
|
+
|
9
|
+
module Mysql2
|
10
|
+
module NestedHashBind
|
11
|
+
class Error < StandardError; end
|
12
|
+
# Your code goes here...
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/mysql2/nested_hash_bind/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "mysql2-nested_hash_bind"
|
7
|
+
spec.version = Mysql2::NestedHashBind::VERSION
|
8
|
+
spec.authors = ["sue445"]
|
9
|
+
spec.email = ["sue445@sue445.net"]
|
10
|
+
|
11
|
+
spec.summary = "mysql2 and mysql2-cs-bind extension to bind response to nested Hash"
|
12
|
+
spec.description = "mysql2 and mysql2-cs-bind extension to bind response to nested Hash"
|
13
|
+
spec.homepage = "https://github.com/sue445/mysql2-nested_hash_bind"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 3.0.0"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
20
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_dependency "mysql2"
|
34
|
+
spec.add_dependency "mysql2-cs-bind"
|
35
|
+
|
36
|
+
spec.add_development_dependency "dotenv"
|
37
|
+
spec.add_development_dependency "rake"
|
38
|
+
spec.add_development_dependency "rspec"
|
39
|
+
spec.add_development_dependency "rspec-its"
|
40
|
+
spec.add_development_dependency "rubocop"
|
41
|
+
spec.add_development_dependency "rubocop_auto_corrector"
|
42
|
+
spec.add_development_dependency "rubocop-performance"
|
43
|
+
spec.add_development_dependency "yard"
|
44
|
+
|
45
|
+
# benchmark
|
46
|
+
spec.add_development_dependency "benchmark-ips"
|
47
|
+
spec.add_development_dependency "stackprof"
|
48
|
+
spec.add_development_dependency "stackprof-webnav"
|
49
|
+
|
50
|
+
# For more information and examples about making a new gem, check out our
|
51
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mysql2-nested_hash_bind
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- sue445
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mysql2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mysql2-cs-bind
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dotenv
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-its
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop_auto_corrector
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-performance
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: yard
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: benchmark-ips
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: stackprof
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: stackprof-webnav
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
description: mysql2 and mysql2-cs-bind extension to bind response to nested Hash
|
196
|
+
email:
|
197
|
+
- sue445@sue445.net
|
198
|
+
executables: []
|
199
|
+
extensions: []
|
200
|
+
extra_rdoc_files: []
|
201
|
+
files:
|
202
|
+
- ".env.example"
|
203
|
+
- ".rspec"
|
204
|
+
- ".rubocop.yml"
|
205
|
+
- ".yardopts"
|
206
|
+
- CHANGELOG.md
|
207
|
+
- Gemfile
|
208
|
+
- LICENSE.txt
|
209
|
+
- README.md
|
210
|
+
- Rakefile
|
211
|
+
- benchmark/README.md
|
212
|
+
- benchmark/tmp/.keep
|
213
|
+
- benchmark/xquery_bench.rb
|
214
|
+
- benchmark/xquery_stackprof.rb
|
215
|
+
- lib/mysql2-nested_hash_bind.rb
|
216
|
+
- lib/mysql2/nested_hash_bind.rb
|
217
|
+
- lib/mysql2/nested_hash_bind/query_extension.rb
|
218
|
+
- lib/mysql2/nested_hash_bind/version.rb
|
219
|
+
- mysql2-nested_hash_bind.gemspec
|
220
|
+
- sig/mysql2/nested_hash_bind.rbs
|
221
|
+
homepage: https://github.com/sue445/mysql2-nested_hash_bind
|
222
|
+
licenses:
|
223
|
+
- MIT
|
224
|
+
metadata:
|
225
|
+
homepage_uri: https://github.com/sue445/mysql2-nested_hash_bind
|
226
|
+
source_code_uri: https://github.com/sue445/mysql2-nested_hash_bind
|
227
|
+
changelog_uri: https://github.com/sue445/mysql2-nested_hash_bind/blob/main/CHANGELOG.md
|
228
|
+
rubygems_mfa_required: 'true'
|
229
|
+
post_install_message:
|
230
|
+
rdoc_options: []
|
231
|
+
require_paths:
|
232
|
+
- lib
|
233
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
234
|
+
requirements:
|
235
|
+
- - ">="
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: 3.0.0
|
238
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
239
|
+
requirements:
|
240
|
+
- - ">="
|
241
|
+
- !ruby/object:Gem::Version
|
242
|
+
version: '0'
|
243
|
+
requirements: []
|
244
|
+
rubygems_version: 3.3.7
|
245
|
+
signing_key:
|
246
|
+
specification_version: 4
|
247
|
+
summary: mysql2 and mysql2-cs-bind extension to bind response to nested Hash
|
248
|
+
test_files: []
|