db2_query 0.2.1 → 0.2.2

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