db2_query 0.2.1 → 0.2.2

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: 7d96a31dfec03b3e0b1fbecff08c2fe16bc85460bb168081fde94c431d45f3c6
4
- data.tar.gz: cbeb3d3342a33a28c222a45fa50bb5ebeec4271195d864b74b7cffe0298519a5
3
+ metadata.gz: a54ab3ca53e8078aee466400a89ec9b18c5c5ed65450d6d35cdf7a8552e0e21c
4
+ data.tar.gz: 72ad54eb98b13a471ba8e022e57cb686a31cf7a2e1d833a4d54d833f3d900105
5
5
  SHA512:
6
- metadata.gz: 7f18c6256a2186145f91380f2d828aa4eaacce7731a2f7f89ad56da22563506e7088ca5a909c3edd9723fd49b4cbec0f0301f9c486a8af0979c3c770077da980
7
- data.tar.gz: 23e3ce4cedcae00e4570edc47bd6316dac424c5b264ef67daebda977d84f16e2d89aede6d6d27663ba13465ec300f7a4a16d50d36144e070ea56fdb1b141d552
6
+ metadata.gz: a037c568900f33c3eefdc2692fd0d8d470dc4167ff87f3b1c0fa11902204fb38523b4bc257cf6cac069590a9706f1c94f506fc13fd35aa7388495fc9e28ea84a
7
+ data.tar.gz: 9f1815d05567d1f972ff1f7d0ff5324a2d11781ae0000b13b552c1c21b4da260de7e7e0d8a633fb48a5ce5636ceea7f90e1fe0a509352a7ff99779788ef0b7d7
data/README.md CHANGED
@@ -2,7 +2,7 @@
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 query plugin to fetch data from Db2 database by using ODBC connection.
5
+ A Rails 6+ query plugin to fetch data from Db2 database by using ODBC connection
6
6
 
7
7
  ## Installation
8
8
  Add this line to your application's Gemfile:
@@ -20,8 +20,6 @@ Or install it yourself as:
20
20
  ```bash
21
21
  $ gem install db2_query
22
22
  ```
23
- ## Note
24
- Start v.0.2.0, this module use `DB2Query` instead of `Db2Query`
25
23
 
26
24
  ## Initialization
27
25
  Execute init task at the app root
@@ -30,10 +28,16 @@ $ rake db2query:init
30
28
  ```
31
29
  DB2Query will generate two required files:
32
30
  - `config/db2query_database.yml`
33
- - `config/initializers/db2query`
31
+ - `config/initializers/db2query.rb`
34
32
 
35
33
  Edit these files according to the requirement.
36
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.
40
+
37
41
  ### Database Configuration
38
42
  At `db2query_database.yml` we can use two type of connection:
39
43
  1. DSN connection config
@@ -59,7 +63,27 @@ development:
59
63
  pwd: <%= ENV["ISERIES_PWD"] %>
60
64
  ```
61
65
 
66
+ Ensure that `unixodbc` have been installed and test your connection first by using `isql` commands.
67
+
68
+ Example:
69
+
70
+ Secondary database connection test
71
+ ```bash
72
+ $ isql -v iseries
73
+ +---------------------------------------+
74
+ | Connected! |
75
+ | |
76
+ | sql-statement |
77
+ | help [tablename] |
78
+ | quit |
79
+ | |
80
+ +---------------------------------------+
81
+ SQL>
82
+ ```
83
+
62
84
  ## Usage
85
+ Note: Version 0.1.0 use `Db2Query` namespace. Please use `DB2Query` in versions greater than it.
86
+
63
87
  ### Basic Usage
64
88
  Create query class that inherit from `DB2Query::Base` in `app/queries` folder
65
89
  ```ruby
@@ -83,7 +107,7 @@ User.find_by 10000
83
107
  SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [[nil, 10000]]
84
108
  => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
85
109
  ```
86
- Or using keywords argument
110
+ Or using keywords argument if the sql use `=` operator, e.g `first_name = ?`
87
111
  ```bash
88
112
  User.by_name first_name: "Strange", last_name: "Stephen"
89
113
  SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
@@ -116,12 +140,58 @@ end
116
140
  Check it at rails console
117
141
  ```bash
118
142
  Doctor.find_by id: 10000
119
- SQL Load (30.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
143
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
120
144
  => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Dr.Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
121
145
  ```
122
146
 
123
147
  ### Available methods
124
- `DB2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom `records` method.
148
+ `DB2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
149
+ 1. `records` to convert query result into array of Record objects.
150
+ 2. `to_h` to convert query result into hash with symbolized keys.
151
+
152
+ ### ActiveRecord Combination
153
+
154
+ Create an abstract class that inherit from `ActiveRecord::Base`
155
+ ```ruby
156
+ class Db2Record < ActiveRecord::Base
157
+ self.abstract_class = true
158
+
159
+ def self.query(sql, formatter = {}, args = [])
160
+ DB2Query::Base.connection.exec_query(sql, formatter, args).to_a.map(&:deep_symbolize_keys)
161
+ end
162
+ end
163
+ ```
164
+
165
+ Utilize the goodness of rails model `scope`
166
+ ```ruby
167
+ class User < Db2Record
168
+ scope :by_name, -> *args {
169
+ query(
170
+ "SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ?", {}, args
171
+ )
172
+ }
173
+ end
174
+ ```
175
+ ```bash
176
+ User.by_name first_name: "Strange", last_name: "Stephen"
177
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
178
+ => [{:id=> 10000, :first_name=> "Strange", :last_name=> "Stephen", :email=> "strange@marvel.universe.com"}]
179
+ ```
180
+
181
+ Another example:
182
+ ```ruby
183
+ class User < Db2Record
184
+ scope :age_gt, -> age {
185
+ query("SELECT * FROM LIBTEST.USERS WHERE age > #{age}")
186
+ }
187
+ end
188
+ ```
189
+
190
+ ```bash
191
+ User.age_gt 500
192
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE age > 500
193
+ => [{:id=> 99999, :first_name=> "Ancient", :last_name=> "One", :email=> "ancientone@marvel.universe.com"}]
194
+ ```
125
195
 
126
196
  ## License
127
197
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -4,8 +4,8 @@ require "yaml"
4
4
  require "erb"
5
5
  require "active_record"
6
6
  require "active_support"
7
- require "active_record/database_configurations"
8
7
  require "db2_query/config"
8
+ require "db2_query/connection_handling"
9
9
 
10
10
  module DB2Query
11
11
  extend ActiveSupport::Autoload
@@ -13,8 +13,8 @@ module DB2Query
13
13
  autoload :Version
14
14
  autoload :Base
15
15
  autoload :Core
16
- autoload :ConnectionHandling
17
16
  autoload :DatabaseStatements
17
+ autoload :Connection
18
18
  autoload :ODBCConnector
19
19
  autoload :Formatter
20
20
  autoload :Result
@@ -2,9 +2,8 @@
2
2
 
3
3
  module DB2Query
4
4
  class Base
5
- include DB2Query::Core
6
5
  include ActiveRecord::Inheritance
7
- extend ActiveSupport::Concern
6
+ include DB2Query::Core
8
7
  extend ActiveRecord::ConnectionHandling
9
8
  extend DB2Query::ConnectionHandling
10
9
  end
@@ -20,11 +20,7 @@ module DB2Query
20
20
 
21
21
  def read_config
22
22
  erb = ERB.new(config_file.read)
23
- YAML.parse(erb.result(binding)).transform
24
- end
25
-
26
- def connection_env
27
- ENV["RAILS_ENV"].to_sym
23
+ YAML.parse(erb.result(binding)).transform.transform_keys(&:to_sym)
28
24
  end
29
25
  end
30
26
  end
@@ -0,0 +1,163 @@
1
+ # frozen_String_literal: true
2
+
3
+ module DB2Query
4
+ class Connection
5
+ ADAPTER_NAME = "DB2Query"
6
+
7
+ include DB2Query::DatabaseStatements
8
+ include ActiveSupport::Callbacks
9
+ define_callbacks :checkout, :checkin
10
+
11
+ set_callback :checkin, :after, :enable_lazy_transactions!
12
+
13
+ attr_accessor :pool
14
+ attr_reader :owner, :connector, :lock
15
+ alias :in_use? :owner
16
+
17
+ def initialize(type, config)
18
+ @connector = DB2Query::ODBCConnector.new(type, config)
19
+ @instrumenter = ActiveSupport::Notifications.instrumenter
20
+ @config = config
21
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
22
+ @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
23
+ connect
24
+ end
25
+
26
+ def adapter_name
27
+ self.class::ADAPTER_NAME
28
+ end
29
+
30
+ def current_transaction
31
+ end
32
+
33
+ def begin_transaction(options = {})
34
+ end
35
+
36
+ def transaction_open?
37
+ end
38
+
39
+ def requires_reloading?
40
+ false
41
+ end
42
+
43
+ def close
44
+ pool.checkin self
45
+ end
46
+
47
+ def connect
48
+ @connection = connector.connect
49
+ end
50
+
51
+ def active?
52
+ @connection.connected?
53
+ end
54
+
55
+ def reconnect!
56
+ disconnect!
57
+ connect
58
+ end
59
+ alias reset! reconnect!
60
+
61
+ def disconnect!
62
+ if @connection.connected?
63
+ @connection.commit
64
+ @connection.disconnect
65
+ end
66
+ end
67
+
68
+ def check_version
69
+ end
70
+
71
+ def enable_lazy_transactions!
72
+ @lazy_transactions_enabled = true
73
+ end
74
+
75
+ def lease
76
+ if in_use?
77
+ msg = +"Cannot lease connection, "
78
+ if @owner == Thread.current
79
+ msg << "it is already leased by the current thread."
80
+ else
81
+ msg << "it is already in use by a different thread: #{@owner}. " \
82
+ "Current thread: #{Thread.current}."
83
+ end
84
+ raise ActiveRecordError, msg
85
+ end
86
+
87
+ @owner = Thread.current
88
+ end
89
+
90
+ def verify!
91
+ reconnect! unless active?
92
+ end
93
+
94
+ def translate_exception_class(e, sql, binds)
95
+ message = "#{e.class.name}: #{e.message}"
96
+
97
+ exception = translate_exception(
98
+ e, message: message, sql: sql, binds: binds
99
+ )
100
+ exception.set_backtrace e.backtrace
101
+ exception
102
+ end
103
+
104
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
105
+ @instrumenter.instrument(
106
+ "sql.active_record",
107
+ sql: sql,
108
+ name: name,
109
+ binds: binds,
110
+ type_casted_binds: type_casted_binds,
111
+ statement_name: statement_name,
112
+ connection_id: object_id,
113
+ connection: self) do
114
+ @lock.synchronize do
115
+ yield
116
+ end
117
+ rescue => e
118
+ raise translate_exception_class(e, sql, binds)
119
+ end
120
+ end
121
+
122
+ def translate_exception(exception, message:, sql:, binds:)
123
+ case exception
124
+ when RuntimeError
125
+ exception
126
+ else
127
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
128
+ end
129
+ end
130
+
131
+ def expire
132
+ if in_use?
133
+ if @owner != Thread.current
134
+ raise ActiveRecordError, "Cannot expire connection, " \
135
+ "it is owned by a different thread: #{@owner}. " \
136
+ "Current thread: #{Thread.current}."
137
+ end
138
+
139
+ @idle_since = Concurrent.monotonic_time
140
+ @owner = nil
141
+ else
142
+ raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
143
+ end
144
+ end
145
+
146
+ def steal!
147
+ if in_use?
148
+ if @owner != Thread.current
149
+ pool.send :remove_connection_from_thread_cache, self, @owner
150
+
151
+ @owner = Thread.current
152
+ end
153
+ else
154
+ raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
155
+ end
156
+ end
157
+
158
+ def seconds_idle # :nodoc:
159
+ return 0 if in_use?
160
+ Concurrent.monotonic_time - @idle_since
161
+ end
162
+ end
163
+ end
@@ -1,17 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DB2Query
4
+ CONNECTION_TYPES = %i[dsn conn_string].freeze
5
+
6
+ class ConnectionPool < ActiveRecord::ConnectionAdapters::ConnectionPool
7
+ attr_reader :conn_type
8
+
9
+ def initialize(spec)
10
+ @conn_type = (spec.config.keys & DB2Query::CONNECTION_TYPES).first
11
+ super(spec)
12
+ end
13
+
14
+ private
15
+ def new_connection
16
+ DB2Query::Connection.new(conn_type, spec.config)
17
+ end
18
+ end
19
+
20
+ class ConnectionSpecification #:nodoc:
21
+ attr_reader :name, :config
22
+
23
+ def initialize(name, config)
24
+ @name, @config = name, config
25
+ end
26
+
27
+ def initialize_dup(original)
28
+ @config = original.config.dup
29
+ end
30
+
31
+ def to_hash
32
+ @config.merge(name: @name)
33
+ end
34
+
35
+ class Resolver < ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver
36
+ def spec(config)
37
+ pool_name = config if config.is_a?(Symbol)
38
+ spec = resolve(config, pool_name).symbolize_keys
39
+ ConnectionSpecification.new(spec.delete(:name) || "primary", spec)
40
+ end
41
+ end
42
+ end
43
+
44
+ class ConnectionHandler < ActiveRecord::ConnectionAdapters::ConnectionHandler
45
+ def establish_connection(config)
46
+ resolver = ConnectionSpecification::Resolver.new(DB2Query::Base.configurations)
47
+
48
+ spec = resolver.spec(config)
49
+
50
+ remove_connection(spec.name)
51
+
52
+ message_bus = ActiveSupport::Notifications.instrumenter
53
+ payload = {
54
+ connection_id: object_id
55
+ }
56
+ if spec
57
+ payload[:spec_name] = spec.name
58
+ payload[:config] = spec.config
59
+ end
60
+
61
+ message_bus.instrument("!connection.active_record", payload) do
62
+ owner_to_pool[spec.name] = DB2Query::ConnectionPool.new(spec)
63
+ end
64
+
65
+ owner_to_pool[spec.name]
66
+ end
67
+ end
68
+
4
69
  module ConnectionHandling
5
70
  RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
6
71
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
7
72
 
73
+ def lookup_connection_handler(handler_key) # :nodoc:
74
+ handler_key = DB2Query::Base.reading_role
75
+ connection_handlers[handler_key] ||= DB2Query::ConnectionHandler.new
76
+ end
77
+
8
78
  def resolve_config_for_connection(config_or_env) # :nodoc:
9
79
  raise "Anonymous class is not allowed." unless name
10
80
 
11
81
  config_or_env ||= DEFAULT_ENV.call.to_sym
12
82
  pool_name = primary_class? ? "primary" : name
13
83
  self.connection_specification_name = pool_name
14
- resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
84
+ resolver = DB2Query::ConnectionSpecification::Resolver.new(DB2Query::Base.configurations)
15
85
 
16
86
  config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
17
87
  config_hash[:name] = pool_name
@@ -21,13 +91,22 @@ module DB2Query
21
91
 
22
92
  def connection_specification_name
23
93
  if !defined?(@connection_specification_name) || @connection_specification_name.nil?
24
- return self == Base ? "primary" : superclass.connection_specification_name
94
+ return self == DB2Query::Base ? "primary" : superclass.connection_specification_name
25
95
  end
26
96
  @connection_specification_name
27
97
  end
28
98
 
29
99
  def primary_class?
30
- self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
100
+ self == DB2Query::Base || defined?(Db2Record) && self == Db2Record
31
101
  end
102
+
103
+ private
104
+ def swap_connection_handler(handler, &blk) # :nodoc:
105
+ old_handler, DB2Query::Base.connection_handler = DB2Query::Base.connection_handler, handler
106
+ return_value = yield
107
+ return_value
108
+ ensure
109
+ DB2Query::Base.connection_handler = old_handler
110
+ end
32
111
  end
33
112
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/database_configurations"
4
+
3
5
  module DB2Query
4
6
  module Core
5
7
  extend ActiveSupport::Concern
6
8
 
7
- included do |base|
9
+ included do
8
10
  def self.configurations=(config)
9
11
  @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
10
12
  end
@@ -14,28 +16,26 @@ module DB2Query
14
16
  @@configurations
15
17
  end
16
18
 
17
- class_attribute :default_connection_handler
18
-
19
19
  mattr_accessor :connection_handlers, instance_accessor: false, default: {}
20
20
 
21
- mattr_accessor :writing_role, instance_accessor: false, default: :writing
22
-
23
- mattr_accessor :reading_role, instance_accessor: false, default: :reading
21
+ class_attribute :default_connection_handler
24
22
 
25
23
  def self.connection_handler
26
- Thread.current.thread_variable_get("ar_connection_handler") || default_connection_handler
24
+ Thread.current.thread_variable_get("db2q_connection_handler") || default_connection_handler
27
25
  end
28
26
 
29
27
  def self.connection_handler=(handler)
30
- Thread.current.thread_variable_set("ar_connection_handler", handler)
28
+ Thread.current.thread_variable_set("db2q_connection_handler", handler)
31
29
  end
32
30
 
33
- self.default_connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
34
-
35
- base.extend ClassMethods
31
+ self.default_connection_handler = DB2Query::ConnectionHandler.new
36
32
  end
37
33
 
38
34
  module ClassMethods
35
+ def initiation
36
+ yield(self) if block_given?
37
+ end
38
+
39
39
  def attributes(attr_name, format)
40
40
  formatters.store(attr_name, format)
41
41
  end
@@ -48,6 +48,11 @@ module DB2Query
48
48
  end
49
49
 
50
50
  private
51
+ def single_value_from_rows(rows)
52
+ row = rows.first
53
+ row && row.first
54
+ end
55
+
51
56
  def key_finder_regex(k)
52
57
  /#{k} =\\? | #{k}=\\? | #{k}= \\? /i
53
58
  end
@@ -1,31 +1,33 @@
1
1
  # frozen_String_literal: true
2
2
 
3
- module DB2Query
4
- CONNECTION_TYPES = %i[dsn conn_string].freeze
3
+ require "odbc_utf8"
5
4
 
5
+ module DB2Query
6
6
  class ODBCConnector
7
- attr_reader :connector, :conn_type, :conn_config
8
-
9
- def initialize(type, config)
10
- @conn_type, @conn_config = type, config.transform_keys(&:to_sym)
11
- @connector = DB2Query.const_get("#{conn_type.to_s.camelize}Connector").new
7
+ def self.new(type, config)
8
+ conn_type, conn_config = type, config.transform_keys(&:to_sym)
9
+ DB2Query.const_get("#{conn_type.to_s.camelize}Connector").new(conn_config)
12
10
  end
11
+ end
13
12
 
14
- def connect
15
- connector.connect(conn_config)
13
+ class AbstractConnector
14
+ attr_reader :config
15
+
16
+ def initialize(config)
17
+ @config = config
16
18
  end
17
19
  end
18
20
 
19
- class DsnConnector
20
- def connect(config)
21
+ class DsnConnector < AbstractConnector
22
+ def connect
21
23
  ::ODBC.connect(config[:dsn], config[:uid], config[:pwd])
22
24
  rescue ::ODBC::Error => e
23
25
  raise ArgumentError, "Unable to activate ODBC DSN connection #{e}"
24
26
  end
25
27
  end
26
28
 
27
- class ConnStringConnector
28
- def connect(config)
29
+ class ConnStringConnector < AbstractConnector
30
+ def connect
29
31
  driver = ::ODBC::Driver.new.tap do |d|
30
32
  d.attrs = config[:conn_string].transform_keys(&:to_s)
31
33
  d.name = "odbc"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'db2_query'
4
- require 'rails'
3
+ require "db2_query"
4
+ require "rails"
5
5
 
6
6
  module DB2Query
7
7
  class Railtie < Rails::Railtie
@@ -15,6 +15,12 @@ module DB2Query
15
15
  end
16
16
  end
17
17
 
18
+ def to_h
19
+ rows.map do |row|
20
+ columns.zip(row).each_with_object({}) { |cr, h| h[cr[0].to_sym] = cr[1] }
21
+ end
22
+ end
23
+
18
24
  def inspect
19
25
  entries = records.take(11).map!(&:inspect)
20
26
 
@@ -25,7 +31,7 @@ module DB2Query
25
31
 
26
32
  class Record
27
33
  attr_reader :formatters
28
-
34
+
29
35
  def initialize(row, columns, formatters)
30
36
  @formatters = formatters
31
37
  columns.zip(row) do |col, val|
@@ -6,4 +6,4 @@ namespace :db2query do
6
6
  Rake::Task["db2query:initializer"].invoke
7
7
  Rake::Task["db2query:database"].invoke
8
8
  end
9
- end
9
+ end
@@ -6,6 +6,11 @@ DB2_QUERY_INITIALIZER_TEMPLATE ||= <<-EOF
6
6
  require "db2_query"
7
7
  require "db2_query/formatter"
8
8
 
9
+ DB2Query::Base.initiation do |base|
10
+ base.configurations = base.parent.config
11
+ base.establish_connection ENV['RAILS_ENV'].to_sym
12
+ end
13
+
9
14
  # Example
10
15
 
11
16
  class FirstNameFormatter < DB2Query::AbstractFormatter
@@ -34,4 +39,4 @@ namespace :db2query do
34
39
  puts " File '#{initializer_path}' created."
35
40
  end
36
41
  end
37
- end
42
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DB2Query
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.2"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db2_query
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - yohanes_l
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-29 00:00:00.000000000 Z
11
+ date: 2020-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-odbc
@@ -31,9 +31,6 @@ dependencies:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: 6.0.3
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: 6.0.3.1
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -41,9 +38,6 @@ dependencies:
41
38
  - - "~>"
42
39
  - !ruby/object:Gem::Version
43
40
  version: 6.0.3
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 6.0.3.1
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: activerecord
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -51,9 +45,6 @@ dependencies:
51
45
  - - "~>"
52
46
  - !ruby/object:Gem::Version
53
47
  version: 6.0.3
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: 6.0.3.1
57
48
  type: :runtime
58
49
  prerelease: false
59
50
  version_requirements: !ruby/object:Gem::Requirement
@@ -61,9 +52,6 @@ dependencies:
61
52
  - - "~>"
62
53
  - !ruby/object:Gem::Version
63
54
  version: 6.0.3
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- version: 6.0.3.1
67
55
  - !ruby/object:Gem::Dependency
68
56
  name: rubocop
69
57
  requirement: !ruby/object:Gem::Requirement
@@ -172,10 +160,10 @@ files:
172
160
  - MIT-LICENSE
173
161
  - README.md
174
162
  - Rakefile
175
- - lib/active_record/connection_adapters/db2_query_adapter.rb
176
163
  - lib/db2_query.rb
177
164
  - lib/db2_query/base.rb
178
165
  - lib/db2_query/config.rb
166
+ - lib/db2_query/connection.rb
179
167
  - lib/db2_query/connection_handling.rb
180
168
  - lib/db2_query/core.rb
181
169
  - lib/db2_query/database_statements.rb
@@ -207,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
195
  - !ruby/object:Gem::Version
208
196
  version: '0'
209
197
  requirements: []
210
- rubygems_version: 3.1.3
198
+ rubygems_version: 3.0.3
211
199
  signing_key:
212
200
  specification_version: 4
213
201
  summary: DB2Query
@@ -1,203 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "odbc_utf8"
4
- require "db2_query/odbc_connector"
5
- require "db2_query/database_statements"
6
-
7
- module ActiveRecord
8
- module ConnectionHandling
9
- def db2_query_connection(config)
10
- conn_type = (config.keys & DB2Query::CONNECTION_TYPES).first
11
- if conn_type.nil?
12
- raise ArgumentError, "No data source name (:dsn) or connection string (:conn_str) provided."
13
- end
14
- connector = DB2Query::ODBCConnector.new(conn_type, config)
15
- ConnectionAdapters::DB2QueryConnection.new(connector, config)
16
- end
17
- end
18
-
19
- module ConnectionAdapters
20
- class DB2QueryConnection
21
- include DB2Query::DatabaseStatements
22
- include ActiveSupport::Callbacks
23
- define_callbacks :checkout, :checkin
24
-
25
- set_callback :checkin, :after, :enable_lazy_transactions!
26
-
27
- attr_accessor :pool
28
- attr_reader :owner, :connector, :lock
29
- alias :in_use? :owner
30
-
31
- def initialize(connector, config)
32
- @connector = connector
33
- @instrumenter = ActiveSupport::Notifications.instrumenter
34
- @config = config
35
- @pool = ActiveRecord::ConnectionAdapters::NullPool.new
36
- @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
37
- connect
38
- end
39
-
40
- def connect
41
- @connection = connector.connect
42
- @connection.use_time = true
43
- end
44
-
45
- def active?
46
- @connection.connected?
47
- end
48
-
49
- def reconnect!
50
- disconnect!
51
- connect
52
- end
53
- alias reset! reconnect!
54
-
55
- def disconnect!
56
- if @connection.connected?
57
- @connection.commit
58
- @connection.disconnect
59
- end
60
- end
61
-
62
- def check_version
63
- end
64
-
65
- def enable_lazy_transactions!
66
- @lazy_transactions_enabled = true
67
- end
68
-
69
- def lease
70
- if in_use?
71
- msg = +"Cannot lease connection, "
72
- if @owner == Thread.current
73
- msg << "it is already leased by the current thread."
74
- else
75
- msg << "it is already in use by a different thread: #{@owner}. " \
76
- "Current thread: #{Thread.current}."
77
- end
78
- raise ActiveRecordError, msg
79
- end
80
-
81
- @owner = Thread.current
82
- end
83
-
84
- def verify!
85
- reconnect! unless active?
86
- end
87
-
88
- def translate_exception_class(e, sql, binds)
89
- message = "#{e.class.name}: #{e.message}"
90
-
91
- exception = translate_exception(
92
- e, message: message, sql: sql, binds: binds
93
- )
94
- exception.set_backtrace e.backtrace
95
- exception
96
- end
97
-
98
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
99
- @instrumenter.instrument(
100
- "sql.active_record",
101
- sql: sql,
102
- name: name,
103
- binds: binds,
104
- type_casted_binds: type_casted_binds,
105
- statement_name: statement_name,
106
- connection_id: object_id,
107
- connection: self) do
108
- @lock.synchronize do
109
- yield
110
- end
111
- rescue => e
112
- raise translate_exception_class(e, sql, binds)
113
- end
114
- end
115
-
116
- def translate_exception(exception, message:, sql:, binds:)
117
- case exception
118
- when RuntimeError
119
- exception
120
- else
121
- ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
122
- end
123
- end
124
-
125
- def expire
126
- if in_use?
127
- if @owner != Thread.current
128
- raise ActiveRecordError, "Cannot expire connection, " \
129
- "it is owned by a different thread: #{@owner}. " \
130
- "Current thread: #{Thread.current}."
131
- end
132
-
133
- @idle_since = Concurrent.monotonic_time
134
- @owner = nil
135
- else
136
- raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
137
- end
138
- end
139
-
140
- def steal!
141
- if in_use?
142
- if @owner != Thread.current
143
- pool.send :remove_connection_from_thread_cache, self, @owner
144
-
145
- @owner = Thread.current
146
- end
147
- else
148
- raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
149
- end
150
- end
151
-
152
- def seconds_idle # :nodoc:
153
- return 0 if in_use?
154
- Concurrent.monotonic_time - @idle_since
155
- end
156
-
157
- private
158
- def type_map
159
- @type_map ||= Type::TypeMap.new.tap do |mapping|
160
- initialize_type_map(mapping)
161
- end
162
- end
163
-
164
- def alias_type(map, new_type, old_type)
165
- map.register_type(new_type) do |_, *args|
166
- map.lookup(old_type, *args)
167
- end
168
- end
169
-
170
- def initialize_type_map(map)
171
- map.register_type "boolean", Type::Boolean.new
172
- map.register_type ODBC::SQL_CHAR, Type::String.new
173
- map.register_type ODBC::SQL_LONGVARCHAR, Type::Text.new
174
- map.register_type ODBC::SQL_TINYINT, Type::Integer.new(limit: 4)
175
- map.register_type ODBC::SQL_SMALLINT, Type::Integer.new(limit: 8)
176
- map.register_type ODBC::SQL_INTEGER, Type::Integer.new(limit: 16)
177
- map.register_type ODBC::SQL_BIGINT, Type::BigInteger.new(limit: 32)
178
- map.register_type ODBC::SQL_REAL, Type::Float.new(limit: 24)
179
- map.register_type ODBC::SQL_FLOAT, Type::Float.new
180
- map.register_type ODBC::SQL_DOUBLE, Type::Float.new(limit: 53)
181
- map.register_type ODBC::SQL_DECIMAL, Type::Float.new
182
- map.register_type ODBC::SQL_NUMERIC, Type::Integer.new
183
- map.register_type ODBC::SQL_BINARY, Type::Binary.new
184
- map.register_type ODBC::SQL_DATE, Type::Date.new
185
- map.register_type ODBC::SQL_DATETIME, Type::DateTime.new
186
- map.register_type ODBC::SQL_TIME, Type::Time.new
187
- map.register_type ODBC::SQL_TIMESTAMP, Type::DateTime.new
188
- map.register_type ODBC::SQL_GUID, Type::String.new
189
-
190
- alias_type map, ODBC::SQL_BIT, "boolean"
191
- alias_type map, ODBC::SQL_VARCHAR, ODBC::SQL_CHAR
192
- alias_type map, ODBC::SQL_WCHAR, ODBC::SQL_CHAR
193
- alias_type map, ODBC::SQL_WVARCHAR, ODBC::SQL_CHAR
194
- alias_type map, ODBC::SQL_WLONGVARCHAR, ODBC::SQL_LONGVARCHAR
195
- alias_type map, ODBC::SQL_VARBINARY, ODBC::SQL_BINARY
196
- alias_type map, ODBC::SQL_LONGVARBINARY, ODBC::SQL_BINARY
197
- alias_type map, ODBC::SQL_TYPE_DATE, ODBC::SQL_DATE
198
- alias_type map, ODBC::SQL_TYPE_TIME, ODBC::SQL_TIME
199
- alias_type map, ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP
200
- end
201
- end
202
- end
203
- end