db2_query 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +122 -67
- data/Rakefile +3 -2
- data/lib/db2_query.rb +10 -13
- data/lib/db2_query/base.rb +3 -5
- data/lib/db2_query/config.rb +20 -17
- data/lib/db2_query/connection.rb +87 -123
- data/lib/db2_query/core.rb +73 -28
- data/lib/db2_query/error.rb +1 -1
- data/lib/db2_query/formatter.rb +2 -2
- data/lib/db2_query/logger.rb +42 -0
- data/lib/db2_query/railtie.rb +2 -7
- data/lib/db2_query/result.rb +11 -3
- data/lib/db2_query/tasks/database.rake +17 -33
- data/lib/db2_query/tasks/initializer.rake +7 -8
- data/lib/db2_query/version.rb +2 -2
- metadata +52 -41
- data/lib/db2_query/bind.rb +0 -6
- data/lib/db2_query/connection_handling.rb +0 -112
- data/lib/db2_query/database_statements.rb +0 -89
- data/lib/db2_query/odbc_connector.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09ecde3a8404d9aa56b416cbd3aa89efb21519166dea225cf8e3d7a54a2b4ed1'
|
4
|
+
data.tar.gz: c72fe3cc5e681d99d3dd5a6ec85863cb15b4b95e72b92e42dcb9d20fac910fbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20370e437967f91a7b273b21339c22ad4f868310812be5ff40676eb0ae28044def876276044aa1a0f237fec1b47e3acf6b7591e2bf1d76d53b7876ca688528ae
|
7
|
+
data.tar.gz: aa62b2ff0df74fb300963ab83c36f066b6e032d26de149ade42ed7cd79daac5579aedeae1bafd264747466a0719520b76ef8cef49e7e1e4c64a9cc84eb9f46db
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/db2_query.svg)](https://badge.fury.io/rb/db2_query)
|
4
4
|
|
5
|
-
A Rails
|
5
|
+
A Rails 5 & Rails 6 plugin for handling Db2 SQL database `SIUD` statement (`SELECT`, `INPUT`, `UPDATE`, `DELETE`) by using ODBC connection.
|
6
|
+
|
7
|
+
Note: Tested at Rails 5.2.6 and Rails 6.1.4
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
Add this line to your application's Gemfile:
|
@@ -26,39 +28,27 @@ Execute init task at the app root
|
|
26
28
|
```bash
|
27
29
|
$ rake db2query:init
|
28
30
|
```
|
29
|
-
|
30
|
-
- `config/
|
31
|
+
Db2Query will generate two required files:
|
32
|
+
- `config/db2query.yml`
|
31
33
|
- `config/initializers/db2query.rb`
|
32
34
|
|
33
|
-
Edit
|
34
|
-
|
35
|
-
Note:
|
36
|
-
|
37
|
-
To upgrade from the previous version to v.0.2.1, please delete all the files generated by init task and do init task again (don't forget to backup your db2query_database.yml).
|
38
|
-
|
39
|
-
In v.0.2.0, we have to `require 'db2_query'` at initializer manually.
|
35
|
+
Edit those files according to the requirement.
|
40
36
|
|
41
37
|
### Database Configuration
|
42
|
-
At `
|
43
|
-
1. DSN connection config
|
44
|
-
2. Connection String config
|
38
|
+
At version 0.3.0, `db2query.yml` only use DSN connection config:
|
45
39
|
```yml
|
46
|
-
development:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
secondary: # DSN Example
|
59
|
-
dsn: iseries
|
60
|
-
uid: <%= ENV["ISERIES_UID"] %>
|
61
|
-
pwd: <%= ENV["ISERIES_PWD"] %>
|
40
|
+
development:
|
41
|
+
dsn: ARUNIT
|
42
|
+
pool: 5
|
43
|
+
timeout: 5
|
44
|
+
test:
|
45
|
+
dsn: ARUNIT
|
46
|
+
pool: 5
|
47
|
+
timeout: 5
|
48
|
+
production:
|
49
|
+
dsn: ARUNIT
|
50
|
+
pool: 5
|
51
|
+
timeout: 5
|
62
52
|
```
|
63
53
|
|
64
54
|
Ensure that `unixodbc` have been installed and test your connection first by using `isql` commands.
|
@@ -67,7 +57,7 @@ Example:
|
|
67
57
|
|
68
58
|
Secondary database connection test
|
69
59
|
```bash
|
70
|
-
$ isql -v
|
60
|
+
$ isql -v ARUNIT
|
71
61
|
+---------------------------------------+
|
72
62
|
| Connected! |
|
73
63
|
| |
|
@@ -80,71 +70,134 @@ SQL>
|
|
80
70
|
```
|
81
71
|
|
82
72
|
## Usage
|
83
|
-
Note: Version 0.1.0 use `Db2Query` namespace.
|
73
|
+
Note: Version 0.1.0 use `Db2Query` namespace. Version 0.2 use `DB2Query`. At version 0.3.0 we use `Db2Query` , revert back to the previous namespace.
|
84
74
|
|
85
75
|
### Basic Usage
|
86
|
-
Create query class that inherit from `DB2Query::Base` in `app/queries` folder
|
76
|
+
Create query class that inherit from `DB2Query::Base` in `app/queries` folder.
|
77
|
+
|
78
|
+
Note: `$`symbol is used as column name prefix.
|
79
|
+
|
80
|
+
### 1. `query` method
|
81
|
+
The `query` method must have 2 inputs:
|
82
|
+
1. Method name
|
83
|
+
2. Body (can be an SQL statement or lamda).
|
84
|
+
|
85
|
+
The lambda is used to facilitate us in using `built-in methods` as shown at two query methods below:
|
86
|
+
Example 1.
|
87
87
|
```ruby
|
88
|
-
class User <
|
88
|
+
class User < Db2Query::Base
|
89
89
|
query :find_by, <<-SQL
|
90
|
-
SELECT * FROM LIBTEST.USERS WHERE id = ?
|
90
|
+
SELECT * FROM LIBTEST.USERS WHERE $id = ?
|
91
91
|
SQL
|
92
92
|
end
|
93
|
-
|
94
|
-
|
93
|
+
```
|
94
|
+
```bash
|
95
|
+
irb(main):004:0> User.find_by 10000
|
96
|
+
SQL (3.2ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
|
97
|
+
=> #<Db2Query::Result [#<Record id: 10000, first_name: "Wilma", last_name: "Lindgren", email: "cleveland_kilback@breitenberg.com">]>
|
98
|
+
```
|
99
|
+
Example 2.
|
100
|
+
```ruby
|
101
|
+
class User < Db2query::Base
|
95
102
|
query :id_greater_than, -> id {
|
96
|
-
exec_query({}, "SELECT * FROM LIBTEST.USERS WHERE id > ?", [id])
|
97
|
-
}
|
98
|
-
|
99
|
-
query :insert_record, -> *args {
|
100
|
-
execute(
|
101
|
-
"INSERT INTO users (id, first_name, last_name, email) VALUES (?, ?, ?, ?)", args
|
102
|
-
)
|
103
|
+
exec_query({}, "SELECT * FROM LIBTEST.USERS WHERE $id > ?", [id])
|
103
104
|
}
|
104
105
|
end
|
105
106
|
```
|
107
|
+
```bash
|
108
|
+
irb(main):003:0> User.id_greater_than 10000
|
109
|
+
SQL (3.2ms) SELECT * FROM LIBTEST.USERS WHERE id > ? [["id", 1000]]
|
110
|
+
=> #<Db2Query::Result [#<Record id: 10000, first_name: "Wilma", last_name: "Lindgren", email: "cleveland_kilback@breitenberg.com">...">]>
|
111
|
+
```
|
112
|
+
Example 3.
|
113
|
+
```ruby
|
114
|
+
class User < Db2query::Base
|
115
|
+
query :insert_record, -> args {
|
116
|
+
exec_query({},
|
117
|
+
"INSERT INTO users ($id, $first_name, $last_name, $email) VALUES (?, ?, ?, ?)", args
|
118
|
+
)
|
119
|
+
}
|
120
|
+
end
|
121
|
+
```
|
122
|
+
```bash
|
123
|
+
```
|
106
124
|
|
107
|
-
|
108
|
-
|
109
|
-
2. Body (can be an SQL statement or lamda).
|
110
|
-
|
111
|
-
The lambda is used to facilitate us in using `built-in methods` as shown at two query methods above.
|
112
|
-
|
113
|
-
Or use a normal sql method (don't forget the `_sql` suffix)
|
125
|
+
### 2. Plain/normal method
|
126
|
+
At a plain/normal sql method we add `_sql` suffix. For example `find_by_sql`
|
114
127
|
```ruby
|
115
|
-
class User <
|
128
|
+
class User < Db2Query::Base
|
116
129
|
def find_by_sql
|
117
|
-
"SELECT * FROM LIBTEST.USERS WHERE id = ?"
|
130
|
+
"SELECT * FROM LIBTEST.USERS WHERE $id = ?"
|
118
131
|
end
|
119
132
|
end
|
120
133
|
```
|
121
|
-
|
134
|
+
Then we can call it by using `find_by` class method.
|
122
135
|
```bash
|
123
|
-
User.find_by 10000
|
136
|
+
irb(main):001:0> User.find_by 10000
|
124
137
|
SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [[nil, 10000]]
|
125
138
|
=> #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
|
126
139
|
```
|
127
|
-
Or
|
140
|
+
Or with hash arguments input
|
141
|
+
```ruby
|
142
|
+
class User < Db2Query::Base
|
143
|
+
def by_name_and_email_sql
|
144
|
+
"SELECT * FROM LIBTEST.USERS WHERE $first_name = ? AND $email = ?"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
128
148
|
```bash
|
129
|
-
User.
|
130
|
-
SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["
|
149
|
+
irb(main):001:0> User.by_name_and_email first_name: "Strange", email: "strange@marvel.universe.com"
|
150
|
+
SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["email", strange@marvel.universe.com]]
|
131
151
|
=> #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
|
152
|
+
```
|
153
|
+
### SQL extention (`@extention`)
|
154
|
+
For a reusable sql, we can extend it by using a combination of `extention` and `sql_with_extention` methods, with an `@extention` pointer at SQL statement.
|
155
|
+
```ruby
|
156
|
+
class User < Db2Query::Base
|
157
|
+
# reusable SQL
|
158
|
+
_SQL = -> extention {
|
159
|
+
sql_with_extention("SELECT * FROM LIBTEST.USERS WHERE @extention", extention)
|
160
|
+
}
|
161
|
+
# implementation
|
162
|
+
query :by_email, _SQL.("$email = ?")
|
163
|
+
end
|
164
|
+
```
|
165
|
+
```bash
|
166
|
+
irb(main):004:0> User.by_email "strange@marvel.universe.com"
|
167
|
+
SQL (3.2ms) SELECT * FROM LIBTEST.USERS WHERE email = ? [["email", "strange@marvel.universe.com"]]
|
168
|
+
=> #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
|
169
|
+
```
|
170
|
+
### List input (`@list`)
|
171
|
+
For an array consist list of inputs, we can use `fetch_list` method and `@list` pointer at the SQL statement.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
class User < Db2Query
|
175
|
+
query :by_ids, -> ids {
|
176
|
+
fetch_list("SELECT * FROM LIBTEST.USERS WHERE ID IN (@list)", ids)
|
177
|
+
}
|
178
|
+
end
|
179
|
+
```
|
180
|
+
```bash
|
181
|
+
irb(main):007:0> User.by_ids [10000,10001,10002]
|
182
|
+
SQL (2.8ms) SELECT * FROM LIBTEST.USERS WHERE ID IN ('10000', '10001', '10002')
|
183
|
+
=> #<Db2Query::Result [#<Record id: 10000, name: "Carol", last_name: "Danvers", email: "captain.marvel@marvel.universe.com">, #<Record id: 10001, first_name: "Natasha", last_name: "Romanova", email: "black.widow@marvel.universe">, #<Record id: 10002, first_name: "Wanda", last_name: "Maximoff", email: "scarlet.witch@marvel.universe.com">]>
|
184
|
+
|
132
185
|
```
|
133
186
|
|
134
187
|
### Formatter
|
135
|
-
In order to get different result column format, a query result can be reformatted by
|
188
|
+
In order to get different result column format, a query result can be reformatted by adding a formatter class that inherits `DB2Query::AbstractFormatter` then register at `config\initializers\db2query.rb`
|
136
189
|
```ruby
|
137
190
|
require "db2_query/formatter"
|
138
191
|
|
139
192
|
# create a formatter class
|
140
|
-
class FirstNameFormatter <
|
193
|
+
class FirstNameFormatter < Db2Query::AbstractFormatter
|
141
194
|
def format(value)
|
142
195
|
"Dr." + value
|
143
196
|
end
|
144
197
|
end
|
145
198
|
|
146
199
|
# register the formatter class
|
147
|
-
|
200
|
+
Db2Query::Formatter.registration do |format|
|
148
201
|
format.register(:first_name_formatter, FirstNameFormatter)
|
149
202
|
end
|
150
203
|
```
|
@@ -161,13 +214,15 @@ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
|
|
161
214
|
=> #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Dr.Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
|
162
215
|
```
|
163
216
|
|
164
|
-
|
165
|
-
|
217
|
+
For complete examples please see the basic examples [here](https://github.com/yohaneslumentut/db2_query/blob/master/test/dummy/app/queries/test_query.rb).
|
218
|
+
|
219
|
+
### Available Result Object methods
|
220
|
+
`Db2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
|
166
221
|
1. `records` to convert query result into array of Record objects.
|
167
222
|
2. `to_h` to convert query result into hash with symbolized keys.
|
168
223
|
|
169
224
|
### Built-in methods
|
170
|
-
These built-in methods are delegated to `
|
225
|
+
These built-in methods are delegated to `Db2Query::Connection` methods
|
171
226
|
1. `query_rows(sql)`
|
172
227
|
2. `query_value(sql)`
|
173
228
|
3. `query_values(sql)`
|
@@ -182,8 +237,8 @@ Create an abstract class that inherit from `ActiveRecord::Base`
|
|
182
237
|
class Db2Record < ActiveRecord::Base
|
183
238
|
self.abstract_class = true
|
184
239
|
|
185
|
-
def self.query(
|
186
|
-
|
240
|
+
def self.query(formatter, sql, args = [])
|
241
|
+
Db2Query::Base.connection.exec_query(formatter, sql, args).to_a.map(&:deep_symbolize_keys)
|
187
242
|
end
|
188
243
|
end
|
189
244
|
```
|
@@ -193,7 +248,7 @@ Utilize the goodness of rails model `scope`
|
|
193
248
|
class User < Db2Record
|
194
249
|
scope :by_name, -> *args {
|
195
250
|
query(
|
196
|
-
"SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ?",
|
251
|
+
{}, "SELECT * FROM LIBTEST.USERS WHERE $first_name = ? AND $last_name = ?", args
|
197
252
|
)
|
198
253
|
}
|
199
254
|
end
|
@@ -220,4 +275,4 @@ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE age > 500
|
|
220
275
|
```
|
221
276
|
|
222
277
|
## License
|
223
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
278
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "bundler/setup"
|
3
4
|
require "bundler/gem_tasks"
|
4
5
|
require "rake/testtask"
|
5
6
|
|
6
7
|
Rake::TestTask.new(:test) do |t|
|
7
8
|
t.libs << "test"
|
8
|
-
t.
|
9
|
-
t.
|
9
|
+
t.pattern = "test/**/*_test.rb"
|
10
|
+
t.verbose = false
|
10
11
|
end
|
11
12
|
|
12
13
|
task default: :test
|
data/lib/db2_query.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
|
-
# frozen_string_literal:true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "yaml"
|
4
|
-
require "erb"
|
5
3
|
require "active_record"
|
6
4
|
require "active_support"
|
7
|
-
require "
|
8
|
-
require "
|
9
|
-
require "
|
5
|
+
require "active_support/concurrency/load_interlock_aware_monitor"
|
6
|
+
require "connection_pool"
|
7
|
+
require "odbc_utf8"
|
10
8
|
|
11
|
-
module
|
9
|
+
module Db2Query
|
12
10
|
extend ActiveSupport::Autoload
|
13
11
|
|
14
12
|
autoload :Version
|
15
|
-
autoload :
|
16
|
-
autoload :Bind
|
17
|
-
autoload :Core
|
18
|
-
autoload :DatabaseStatements
|
13
|
+
autoload :Config
|
19
14
|
autoload :Connection
|
20
|
-
autoload :
|
21
|
-
autoload :Formatter
|
15
|
+
autoload :Core
|
22
16
|
autoload :Result
|
17
|
+
autoload :Logger
|
18
|
+
autoload :Base
|
19
|
+
autoload :Error
|
23
20
|
|
24
21
|
require "db2_query/railtie" if defined?(Rails)
|
25
22
|
end
|
data/lib/db2_query/base.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
3
|
+
module Db2Query
|
4
4
|
class Base
|
5
|
-
include
|
6
|
-
include
|
7
|
-
extend ActiveRecord::ConnectionHandling
|
8
|
-
extend DB2Query::ConnectionHandling
|
5
|
+
include Config
|
6
|
+
include Core
|
9
7
|
end
|
10
8
|
end
|
data/lib/db2_query/config.rb
CHANGED
@@ -1,26 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
module Db2Query
|
4
|
+
module Config
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
DEFAULT_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence }
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
"#{Rails.root}/config/db2query_database.yml"
|
12
|
-
else
|
13
|
-
ENV["DQ_CONFIG_PATH"]
|
14
|
-
end
|
8
|
+
included do
|
9
|
+
@@configurations = nil
|
15
10
|
end
|
16
11
|
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
class_methods do
|
13
|
+
def configurations
|
14
|
+
@@configurations
|
15
|
+
end
|
16
|
+
alias config configurations
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
def load_database_configurations(path = nil)
|
19
|
+
file_path = path || "#{Rails.root}/config/db2query.yml"
|
20
|
+
if File.exist?(file_path)
|
21
|
+
config_file = IO.read(file_path)
|
22
|
+
@@configurations = YAML.load(config_file)[DEFAULT_ENV.call].transform_keys(&:to_sym)
|
23
|
+
else
|
24
|
+
raise Db2Query::Error, "Could not load db2query database configuration. No such file - #{file_path}"
|
25
|
+
end
|
26
|
+
end
|
24
27
|
end
|
25
28
|
end
|
26
29
|
end
|
data/lib/db2_query/connection.rb
CHANGED
@@ -1,164 +1,128 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
ADAPTER_NAME = "DB2Query"
|
3
|
+
require_relative "error"
|
4
|
+
require "connection_pool"
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
define_callbacks :checkout, :checkin
|
6
|
+
module Db2Query
|
7
|
+
class Bind < Struct.new(:name, :value, :index)
|
8
|
+
end
|
11
9
|
|
12
|
-
|
10
|
+
class Connection < ConnectionPool
|
11
|
+
attr_reader :config
|
13
12
|
|
14
|
-
|
15
|
-
attr_reader :owner, :connector, :lock
|
16
|
-
alias :in_use? :owner
|
13
|
+
include Logger
|
17
14
|
|
18
|
-
def initialize(
|
19
|
-
@connector = DB2Query::ODBCConnector.new(type, config)
|
20
|
-
@instrumenter = ActiveSupport::Notifications.instrumenter
|
15
|
+
def initialize(config, &block)
|
21
16
|
@config = config
|
22
|
-
|
17
|
+
super(pool_config, &block)
|
18
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
23
19
|
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
24
|
-
connect
|
25
20
|
end
|
26
21
|
|
27
|
-
|
28
|
-
self.class::ADAPTER_NAME
|
29
|
-
end
|
30
|
-
|
31
|
-
def current_transaction
|
32
|
-
end
|
33
|
-
|
34
|
-
def begin_transaction(options = {})
|
35
|
-
end
|
36
|
-
|
37
|
-
def transaction_open?
|
38
|
-
end
|
22
|
+
alias pool with
|
39
23
|
|
40
|
-
def
|
41
|
-
|
24
|
+
def pool_config
|
25
|
+
{ size: config[:pool], timeout: config[:timeout] }
|
42
26
|
end
|
43
27
|
|
44
|
-
def
|
45
|
-
pool
|
28
|
+
def query(sql)
|
29
|
+
pool do |odbc_conn|
|
30
|
+
stmt = odbc_conn.run(sql)
|
31
|
+
stmt.to_a
|
32
|
+
ensure
|
33
|
+
stmt.drop unless stmt.nil?
|
34
|
+
end
|
46
35
|
end
|
47
36
|
|
48
|
-
def
|
49
|
-
|
37
|
+
def query_rows(sql)
|
38
|
+
query(sql)
|
50
39
|
end
|
51
40
|
|
52
|
-
def
|
53
|
-
|
41
|
+
def query_value(sql)
|
42
|
+
single_value_from_rows(query(sql))
|
54
43
|
end
|
55
44
|
|
56
|
-
def
|
57
|
-
|
58
|
-
connect
|
45
|
+
def query_values(sql)
|
46
|
+
query(sql).map(&:first)
|
59
47
|
end
|
60
|
-
alias reset! reconnect!
|
61
48
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
@connection.disconnect
|
49
|
+
def execute(sql, args = [])
|
50
|
+
pool do |odbc_conn|
|
51
|
+
odbc_conn.do(sql, *args)
|
66
52
|
end
|
67
53
|
end
|
68
54
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
55
|
+
def exec_query(formatters, sql, args = [])
|
56
|
+
binds, args = extract_binds_from_sql(sql, args)
|
57
|
+
sql = db2_spec_sql(sql)
|
58
|
+
log(sql, "SQL", binds, args) do
|
59
|
+
run_query(formatters, sql, args, binds)
|
60
|
+
end
|
74
61
|
end
|
75
62
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
63
|
+
def run_query(formatters, sql, args = [], binds)
|
64
|
+
pool do |odbc_conn|
|
65
|
+
begin
|
66
|
+
if args.empty?
|
67
|
+
stmt = odbc_conn.run(sql)
|
68
|
+
else
|
69
|
+
stmt = odbc_conn.run(sql, *args)
|
70
|
+
end
|
71
|
+
columns = stmt.columns.values.map { |col| col.name.downcase }
|
72
|
+
rows = stmt.to_a
|
73
|
+
ensure
|
74
|
+
stmt.drop unless stmt.nil?
|
84
75
|
end
|
85
|
-
|
76
|
+
Db2Query::Result.new(columns, rows, formatters)
|
86
77
|
end
|
87
|
-
|
88
|
-
@owner = Thread.current
|
89
|
-
end
|
90
|
-
|
91
|
-
def verify!
|
92
|
-
reconnect! unless active?
|
93
78
|
end
|
94
79
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
e, message: message, sql: sql, binds: binds
|
100
|
-
)
|
101
|
-
exception.set_backtrace e.backtrace
|
102
|
-
exception
|
103
|
-
end
|
104
|
-
|
105
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
|
106
|
-
@instrumenter.instrument(
|
107
|
-
"sql.active_record",
|
108
|
-
sql: sql,
|
109
|
-
name: name,
|
110
|
-
binds: binds,
|
111
|
-
type_casted_binds: type_casted_binds,
|
112
|
-
statement_name: statement_name,
|
113
|
-
connection_id: object_id,
|
114
|
-
connection: self) do
|
115
|
-
@lock.synchronize do
|
116
|
-
yield
|
117
|
-
end
|
118
|
-
rescue => e
|
119
|
-
raise translate_exception_class(e, sql, binds)
|
80
|
+
private
|
81
|
+
def single_value_from_rows(rows)
|
82
|
+
row = rows.first
|
83
|
+
row && row.first
|
120
84
|
end
|
121
|
-
end
|
122
85
|
|
123
|
-
|
124
|
-
|
125
|
-
when RuntimeError
|
126
|
-
exception
|
127
|
-
else
|
128
|
-
DB2Query::StatementInvalid.new(message, sql: sql, binds: binds)
|
86
|
+
def iud_sql?(sql)
|
87
|
+
sql.match?(/insert into|update|delete/i)
|
129
88
|
end
|
130
|
-
end
|
131
89
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
raise DB2Query::Error, "Cannot expire connection, " \
|
136
|
-
"it is owned by a different thread: #{@owner}. " \
|
137
|
-
"Current thread: #{Thread.current}."
|
138
|
-
end
|
90
|
+
def iud_ref_table(sql)
|
91
|
+
sql.match?(/delete/i) ? "OLD TABLE" : "NEW TABLE"
|
92
|
+
end
|
139
93
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
94
|
+
def db2_spec_sql(sql)
|
95
|
+
if iud_sql?(sql)
|
96
|
+
"SELECT * FROM #{iud_ref_table(sql)} (#{sql})"
|
97
|
+
else
|
98
|
+
sql
|
99
|
+
end.tr("$", "")
|
144
100
|
end
|
145
|
-
end
|
146
101
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
102
|
+
def extract_binds_from_sql(sql, args)
|
103
|
+
keys = sql.scan(/\$\S+/).map { |key| key.gsub!(/[$=]/, "") }
|
104
|
+
sql = sql.tr("$", "")
|
105
|
+
args = args[0].is_a?(Hash) ? args[0] : args
|
106
|
+
given, expected = args.length, sql.scan(/\?/i).length
|
151
107
|
|
152
|
-
|
108
|
+
if given != expected
|
109
|
+
raise Db2Query::Error, "wrong number of arguments (given #{given}, expected #{expected})"
|
153
110
|
end
|
154
|
-
else
|
155
|
-
raise DB2Query::Error, "Cannot steal connection, it is not currently leased."
|
156
|
-
end
|
157
|
-
end
|
158
111
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
112
|
+
if args.is_a?(Hash)
|
113
|
+
binds = *args.map do |key, value|
|
114
|
+
if args[key.to_sym].nil?
|
115
|
+
raise Db2Query::Error, "Column name: `#{key}` not found inside sql statement."
|
116
|
+
end
|
117
|
+
Db2Query::Bind.new(key.to_s, value, nil)
|
118
|
+
end
|
119
|
+
else
|
120
|
+
binds = keys.map.with_index do |key, index|
|
121
|
+
Db2Query::Bind.new(key, args[index], nil)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
[binds.map { |bind| [bind, bind.value] }, binds.map { |bind| bind.value }]
|
126
|
+
end
|
163
127
|
end
|
164
128
|
end
|