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 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