db2_query 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +122 -67
- data/Rakefile +3 -2
- data/lib/db2_query.rb +10 -13
- data/lib/db2_query/base.rb +3 -5
- data/lib/db2_query/config.rb +20 -17
- data/lib/db2_query/connection.rb +87 -123
- data/lib/db2_query/core.rb +73 -28
- data/lib/db2_query/error.rb +1 -1
- data/lib/db2_query/formatter.rb +2 -2
- data/lib/db2_query/logger.rb +42 -0
- data/lib/db2_query/railtie.rb +2 -7
- data/lib/db2_query/result.rb +11 -3
- data/lib/db2_query/tasks/database.rake +17 -33
- data/lib/db2_query/tasks/initializer.rake +7 -8
- data/lib/db2_query/version.rb +2 -2
- metadata +52 -41
- data/lib/db2_query/bind.rb +0 -6
- data/lib/db2_query/connection_handling.rb +0 -112
- data/lib/db2_query/database_statements.rb +0 -89
- data/lib/db2_query/odbc_connector.rb +0 -44
data/lib/db2_query/bind.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
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
|
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
|
-
|
69
|
-
module ConnectionHandling
|
70
|
-
RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
|
71
|
-
DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
|
72
|
-
|
73
|
-
def lookup_connection_handler(handler_key)
|
74
|
-
handler_key = DB2Query::Base.reading_role
|
75
|
-
connection_handlers[handler_key] ||= DB2Query::ConnectionHandler.new
|
76
|
-
end
|
77
|
-
|
78
|
-
def resolve_config_for_connection(config_or_env)
|
79
|
-
raise "Anonymous class is not allowed." unless name
|
80
|
-
|
81
|
-
config_or_env ||= DEFAULT_ENV.call.to_sym
|
82
|
-
pool_name = primary_class? ? "primary" : name
|
83
|
-
self.connection_specification_name = pool_name
|
84
|
-
resolver = DB2Query::ConnectionSpecification::Resolver.new(DB2Query::Base.configurations)
|
85
|
-
|
86
|
-
config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
|
87
|
-
config_hash[:name] = pool_name
|
88
|
-
|
89
|
-
config_hash
|
90
|
-
end
|
91
|
-
|
92
|
-
def connection_specification_name
|
93
|
-
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
|
94
|
-
return self == DB2Query::Base ? "primary" : superclass.connection_specification_name
|
95
|
-
end
|
96
|
-
@connection_specification_name
|
97
|
-
end
|
98
|
-
|
99
|
-
def primary_class?
|
100
|
-
self == DB2Query::Base || defined?(Db2Record) && self == Db2Record
|
101
|
-
end
|
102
|
-
|
103
|
-
private
|
104
|
-
def swap_connection_handler(handler, &blk)
|
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
|
111
|
-
end
|
112
|
-
end
|
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_String_literal: true
|
2
|
-
|
3
|
-
module DB2Query
|
4
|
-
module DatabaseStatements
|
5
|
-
def query(sql)
|
6
|
-
stmt = @connection.run(sql)
|
7
|
-
stmt.to_a
|
8
|
-
ensure
|
9
|
-
stmt.drop unless stmt.nil?
|
10
|
-
end
|
11
|
-
|
12
|
-
def query_rows(sql)
|
13
|
-
query(sql)
|
14
|
-
end
|
15
|
-
|
16
|
-
def query_value(sql)
|
17
|
-
single_value_from_rows(query(sql))
|
18
|
-
end
|
19
|
-
|
20
|
-
def query_values(sql)
|
21
|
-
query(sql).map(&:first)
|
22
|
-
end
|
23
|
-
|
24
|
-
def execute(sql, args = [])
|
25
|
-
@connection.do(sql, *args)
|
26
|
-
end
|
27
|
-
|
28
|
-
def exec_query(formatters, sql, args = [])
|
29
|
-
binds, args = extract_binds_from_sql(sql, args)
|
30
|
-
log(sql, "SQL", binds, args) do
|
31
|
-
begin
|
32
|
-
if args.empty?
|
33
|
-
stmt = @connection.run(sql)
|
34
|
-
else
|
35
|
-
stmt = @connection.run(sql, *args)
|
36
|
-
end
|
37
|
-
columns = stmt.columns.values.map { |col| col.name.downcase }
|
38
|
-
rows = stmt.to_a
|
39
|
-
ensure
|
40
|
-
stmt.drop unless stmt.nil?
|
41
|
-
end
|
42
|
-
DB2Query::Result.new(columns, rows, formatters)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
def single_value_from_rows(rows)
|
48
|
-
row = rows.first
|
49
|
-
row && row.first
|
50
|
-
end
|
51
|
-
|
52
|
-
def key_finder_regex(k)
|
53
|
-
/#{k} .\\? | #{k}.\\? | #{k}. \\? /i
|
54
|
-
end
|
55
|
-
|
56
|
-
def extract_binds_from_sql(sql, args)
|
57
|
-
question_mark_positions = sql.enum_for(:scan, /\?/i).map { Regexp.last_match.begin(0) }
|
58
|
-
args = args.first.is_a?(Hash) ? args.first : args
|
59
|
-
given, expected = args.length, question_mark_positions.length
|
60
|
-
|
61
|
-
if given != expected
|
62
|
-
raise DB2Query::Error, "wrong number of arguments (given #{given}, expected #{expected})"
|
63
|
-
end
|
64
|
-
|
65
|
-
if args.is_a?(Hash)
|
66
|
-
binds = args.map do |key, value|
|
67
|
-
position = sql.enum_for(:scan, key_finder_regex(key)).map { Regexp.last_match.begin(0) }
|
68
|
-
if position.empty?
|
69
|
-
raise DB2Query::Error, "Column name: `#{key}` not found inside sql statement."
|
70
|
-
elsif position.length > 1
|
71
|
-
raise DB2Query::Error, "Can't handle such this kind of sql. Please refactor your sql."
|
72
|
-
else
|
73
|
-
index = position[0]
|
74
|
-
end
|
75
|
-
|
76
|
-
DB2Query::Bind.new(key.to_s, value, index)
|
77
|
-
end
|
78
|
-
binds = binds.sort_by { |bind| bind.index }
|
79
|
-
[binds.map { |bind| [bind, bind.value] }, binds.map { |bind| bind.value }]
|
80
|
-
elsif question_mark_positions.length == 1 && args.length == 1
|
81
|
-
column = sql[/(.*?) . \?|(.*?) .\?|(.*?). \?|(.*?).\?/m, 1].split.last.downcase
|
82
|
-
bind = DB2Query::Bind.new(column.gsub(/[)(]/, ""), args, 0)
|
83
|
-
[[[bind, bind.value]], bind.value]
|
84
|
-
else
|
85
|
-
[args.map { |arg| [nil, arg] }, args]
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_String_literal: true
|
2
|
-
|
3
|
-
require "odbc_utf8"
|
4
|
-
|
5
|
-
module DB2Query
|
6
|
-
class ODBCConnector
|
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)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class AbstractConnector
|
14
|
-
attr_reader :config
|
15
|
-
|
16
|
-
def initialize(config)
|
17
|
-
@config = config
|
18
|
-
end
|
19
|
-
|
20
|
-
def connect
|
21
|
-
raise "abstract method #connect must be defined"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class DsnConnector < AbstractConnector
|
26
|
-
def connect
|
27
|
-
::ODBC.connect(config[:dsn], config[:uid], config[:pwd])
|
28
|
-
rescue ::ODBC::Error => e
|
29
|
-
raise DB2Query::Error, "Unable to activate ODBC DSN connection #{e}"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class ConnStringConnector < AbstractConnector
|
34
|
-
def connect
|
35
|
-
driver = ::ODBC::Driver.new.tap do |d|
|
36
|
-
d.attrs = config[:conn_string].transform_keys(&:to_s)
|
37
|
-
d.name = "odbc"
|
38
|
-
end
|
39
|
-
::ODBC::Database.new.drvconnect(driver)
|
40
|
-
rescue ::ODBC::Error => e
|
41
|
-
raise DB2Query::Error, "Unable to activate ODBC Conn String connection #{e}"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|