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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42c29fbb4c23d3cc0198ea49b0cbb9e980150a610dde50b19e422c8dd9519c99
4
- data.tar.gz: 7ff6974cb986bc5df133229f275d4be9bac9a647e7936c35e1f3103967848b95
3
+ metadata.gz: '09ecde3a8404d9aa56b416cbd3aa89efb21519166dea225cf8e3d7a54a2b4ed1'
4
+ data.tar.gz: c72fe3cc5e681d99d3dd5a6ec85863cb15b4b95e72b92e42dcb9d20fac910fbc
5
5
  SHA512:
6
- metadata.gz: 79661005fa24b32c0b03e119549377f5fea9dde025aa91dc5809ad52ad4f212f9b20a278736ea6a077aefb411d4bbc0513bb0e3acb629675d9735202206ebfd4
7
- data.tar.gz: 6f58ef2ceb59765e54310d2ea7c1cf1132da62f6102e7187121691b8018670b6a9abe194d41d86cfd9042f35cb5e5746444dcf10c9adced05b85e2335c37f5e6
6
+ metadata.gz: 20370e437967f91a7b273b21339c22ad4f868310812be5ff40676eb0ae28044def876276044aa1a0f237fec1b47e3acf6b7591e2bf1d76d53b7876ca688528ae
7
+ data.tar.gz: aa62b2ff0df74fb300963ab83c36f066b6e032d26de149ade42ed7cd79daac5579aedeae1bafd264747466a0719520b76ef8cef49e7e1e4c64a9cc84eb9f46db
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2020 yohanes_l
1
+ Copyright 2021 yohanes oktavianus lumentut
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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 6+ query plugin to fetch data from Db2 database by using ODBC connection
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
- DB2Query will generate two required files:
30
- - `config/db2query_database.yml`
31
+ Db2Query will generate two required files:
32
+ - `config/db2query.yml`
31
33
  - `config/initializers/db2query.rb`
32
34
 
33
- Edit these files according to the requirement.
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 `db2query_database.yml` we can use two type of connection:
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
- primary: # Connection String Example
48
- conn_string:
49
- driver: DB2
50
- database: SAMPLE
51
- dbalias: SAMPLE
52
- hostname: LOCALHOST
53
- currentschema: LIBTEST
54
- port: "0"
55
- protocol: IPC
56
- uid: <%= ENV["DB2EC_UID"] %>
57
- pwd: <%= ENV["DB2EC_PWD"] %>
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 iseries
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. Please use `DB2Query` in versions greater than it.
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 < DB2Query::Base
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
- class User < DB2query::Base
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
- The query method must have 2 inputs:
108
- 1. Method name
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 < DB2Query::Base
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
- Check it at rails console
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 using keywords argument if the sql use `=` operator, e.g `first_name = ?`
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.by_name first_name: "Strange", last_name: "Stephen"
130
- SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
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 add a formatter class that inherit `DB2Query::AbstractFormatter` then register at `config\initializers\db2query.rb`
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 < DB2Query::AbstractFormatter
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
- DB2Query::Formatter.registration do |format|
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
- ### Available Result Ogject methods
165
- `DB2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
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 `DB2Query::Connection` methods
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(sql, formatter = {}, args = [])
186
- DB2Query::Base.connection.exec_query(sql, formatter, args).to_a.map(&:deep_symbolize_keys)
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 = ?", {}, args
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.libs << "lib"
9
- t.test_files = FileList["test/**/*_test.rb"]
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 "db2_query/config"
8
- require "db2_query/error"
9
- require "db2_query/connection_handling"
5
+ require "active_support/concurrency/load_interlock_aware_monitor"
6
+ require "connection_pool"
7
+ require "odbc_utf8"
10
8
 
11
- module DB2Query
9
+ module Db2Query
12
10
  extend ActiveSupport::Autoload
13
11
 
14
12
  autoload :Version
15
- autoload :Base
16
- autoload :Bind
17
- autoload :Core
18
- autoload :DatabaseStatements
13
+ autoload :Config
19
14
  autoload :Connection
20
- autoload :ODBCConnector
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
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module DB2Query
3
+ module Db2Query
4
4
  class Base
5
- include ActiveRecord::Inheritance
6
- include DB2Query::Core
7
- extend ActiveRecord::ConnectionHandling
8
- extend DB2Query::ConnectionHandling
5
+ include Config
6
+ include Core
9
7
  end
10
8
  end
@@ -1,26 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module DB2Query
4
- class << self
5
- def config
6
- @config ||= read_config
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
- def config_path
10
- if defined?(Rails)
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
- def config_file
18
- Pathname.new(config_path)
19
- end
12
+ class_methods do
13
+ def configurations
14
+ @@configurations
15
+ end
16
+ alias config configurations
20
17
 
21
- def read_config
22
- erb = ERB.new(config_file.read)
23
- YAML.parse(erb.result(binding)).transform.transform_keys(&:to_sym)
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
@@ -1,164 +1,128 @@
1
- # frozen_String_literal: true
1
+ # frozen_string_literal: true
2
2
 
3
- module DB2Query
4
- class Connection
5
- ADAPTER_NAME = "DB2Query"
3
+ require_relative "error"
4
+ require "connection_pool"
6
5
 
7
- include DB2Query::DatabaseStatements
8
- include ActiveSupport::Callbacks
9
-
10
- define_callbacks :checkout, :checkin
6
+ module Db2Query
7
+ class Bind < Struct.new(:name, :value, :index)
8
+ end
11
9
 
12
- set_callback :checkin, :after, :enable_lazy_transactions!
10
+ class Connection < ConnectionPool
11
+ attr_reader :config
13
12
 
14
- attr_accessor :pool
15
- attr_reader :owner, :connector, :lock
16
- alias :in_use? :owner
13
+ include Logger
17
14
 
18
- def initialize(type, config)
19
- @connector = DB2Query::ODBCConnector.new(type, config)
20
- @instrumenter = ActiveSupport::Notifications.instrumenter
15
+ def initialize(config, &block)
21
16
  @config = config
22
- @pool = ActiveRecord::ConnectionAdapters::NullPool.new
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
- def adapter_name
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 requires_reloading?
41
- false
24
+ def pool_config
25
+ { size: config[:pool], timeout: config[:timeout] }
42
26
  end
43
27
 
44
- def close
45
- pool.checkin self
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 connect
49
- @connection = connector.connect
37
+ def query_rows(sql)
38
+ query(sql)
50
39
  end
51
40
 
52
- def active?
53
- @connection.connected?
41
+ def query_value(sql)
42
+ single_value_from_rows(query(sql))
54
43
  end
55
44
 
56
- def reconnect!
57
- disconnect!
58
- connect
45
+ def query_values(sql)
46
+ query(sql).map(&:first)
59
47
  end
60
- alias reset! reconnect!
61
48
 
62
- def disconnect!
63
- if @connection.connected?
64
- @connection.commit
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 check_version
70
- end
71
-
72
- def enable_lazy_transactions!
73
- @lazy_transactions_enabled = true
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 lease
77
- if in_use?
78
- msg = +"Cannot lease connection, "
79
- if @owner == Thread.current
80
- msg << "it is already leased by the current thread."
81
- else
82
- msg << "it is already in use by a different thread: #{@owner}. " \
83
- "Current thread: #{Thread.current}."
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
- raise DB2Query::Error, msg
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
- def translate_exception_class(e, sql, binds)
96
- message = "#{e.class.name}: #{e.message}"
97
-
98
- exception = translate_exception(
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
- def translate_exception(exception, message:, sql:, binds:)
124
- case exception
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
- def expire
133
- if in_use?
134
- if @owner != Thread.current
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
- @idle_since = Concurrent.monotonic_time
141
- @owner = nil
142
- else
143
- raise DB2Query::Error, "Cannot expire connection, it is not currently leased."
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
- def steal!
148
- if in_use?
149
- if @owner != Thread.current
150
- pool.send :remove_connection_from_thread_cache, self, @owner
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
- @owner = Thread.current
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
- def seconds_idle
160
- return 0 if in_use?
161
- Concurrent.monotonic_time - @idle_since
162
- end
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