db2_query 0.2.3 → 0.3.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 +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
|
[](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
|