db2_query 0.3.1 → 0.3.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 +4 -4
- data/README.md +399 -147
- data/lib/db2_query.rb +36 -9
- data/lib/db2_query/base.rb +13 -0
- data/lib/db2_query/config.rb +14 -14
- data/lib/db2_query/core.rb +59 -118
- data/lib/db2_query/db_client.rb +56 -0
- data/lib/db2_query/db_connection.rb +67 -0
- data/lib/db2_query/db_statements.rb +87 -0
- data/lib/db2_query/definitions.rb +79 -0
- data/lib/db2_query/error.rb +71 -6
- data/lib/db2_query/field_type.rb +31 -0
- data/lib/db2_query/helper.rb +49 -0
- data/lib/db2_query/logger.rb +14 -4
- data/lib/db2_query/query.rb +117 -0
- data/lib/db2_query/quoting.rb +102 -0
- data/lib/db2_query/railtie.rb +5 -2
- data/lib/db2_query/result.rb +45 -33
- data/lib/db2_query/sql_statement.rb +34 -0
- data/lib/db2_query/tasks.rb +29 -0
- data/lib/db2_query/tasks/database.rake +2 -33
- data/lib/db2_query/tasks/init.rake +1 -1
- data/lib/db2_query/tasks/initializer.rake +2 -33
- data/lib/db2_query/tasks/templates/database.rb.tt +19 -0
- data/lib/db2_query/tasks/templates/initializer.rb.tt +8 -0
- data/lib/db2_query/type/binary.rb +19 -0
- data/lib/db2_query/type/boolean.rb +41 -0
- data/lib/db2_query/type/date.rb +34 -0
- data/lib/db2_query/type/decimal.rb +15 -0
- data/lib/db2_query/type/integer.rb +15 -0
- data/lib/db2_query/type/string.rb +30 -0
- data/lib/db2_query/type/text.rb +11 -0
- data/lib/db2_query/type/time.rb +30 -0
- data/lib/db2_query/type/timestamp.rb +30 -0
- data/lib/db2_query/type/value.rb +29 -0
- data/lib/db2_query/version.rb +1 -1
- data/lib/rails/generators/query/USAGE +15 -0
- data/lib/rails/generators/query/query_generator.rb +70 -0
- data/lib/rails/generators/query/templates/query.rb.tt +26 -0
- data/lib/rails/generators/query/templates/query_definitions.rb.tt +12 -0
- data/lib/rails/generators/query/templates/unit_test.rb.tt +9 -0
- metadata +49 -10
- data/lib/db2_query/connection.rb +0 -125
- data/lib/db2_query/formatter.rb +0 -27
data/lib/db2_query.rb
CHANGED
@@ -3,20 +3,47 @@
|
|
3
3
|
require "active_record"
|
4
4
|
require "active_support"
|
5
5
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
6
|
+
require "active_model/type"
|
6
7
|
require "connection_pool"
|
7
8
|
require "odbc_utf8"
|
9
|
+
require "db2_query/error"
|
8
10
|
|
9
11
|
module Db2Query
|
10
|
-
|
12
|
+
autoload :Version, "db2_query/version"
|
13
|
+
autoload :Error, "db2_query/error"
|
14
|
+
autoload :Config, "db2_query/config"
|
15
|
+
autoload :Logger, "db2_query/logger"
|
16
|
+
autoload :DbClient, "db2_query/db_client"
|
17
|
+
autoload :DbStatements, "db2_query/db_statements"
|
18
|
+
autoload :Validations, "db2_query/validations"
|
19
|
+
autoload :Helper, "db2_query/helper"
|
20
|
+
autoload :Quoting, "db2_query/quoting"
|
21
|
+
autoload :FieldType, "db2_query/field_type"
|
11
22
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
module Type
|
24
|
+
autoload :Value, "db2_query/type/value"
|
25
|
+
autoload :Binary, "db2_query/type/binary"
|
26
|
+
autoload :Boolean, "db2_query/type/boolean"
|
27
|
+
autoload :Decimal, "db2_query/type/decimal"
|
28
|
+
autoload :String, "db2_query/type/string"
|
29
|
+
autoload :Text, "db2_query/type/text"
|
30
|
+
autoload :Integer, "db2_query/type/integer"
|
31
|
+
autoload :Time, "db2_query/type/time"
|
32
|
+
autoload :Timestamp, "db2_query/type/timestamp"
|
33
|
+
autoload :Date, "db2_query/type/date"
|
34
|
+
end
|
35
|
+
|
36
|
+
autoload :SqlStatement, "db2_query/sql_statement"
|
37
|
+
autoload :Query, "db2_query/query"
|
38
|
+
autoload :Definitions, "db2_query/definitions"
|
39
|
+
autoload :DbConnection, "db2_query/db_connection"
|
40
|
+
autoload :Result, "db2_query/result"
|
41
|
+
autoload :Core, "db2_query/core"
|
42
|
+
autoload :Base, "db2_query/base"
|
43
|
+
|
44
|
+
def self.root
|
45
|
+
__dir__
|
46
|
+
end
|
20
47
|
|
21
48
|
require "db2_query/railtie" if defined?(Rails)
|
22
49
|
end
|
data/lib/db2_query/base.rb
CHANGED
@@ -3,6 +3,19 @@
|
|
3
3
|
module Db2Query
|
4
4
|
class Base
|
5
5
|
include Config
|
6
|
+
include Helper
|
7
|
+
include Quoting
|
8
|
+
include DbConnection
|
9
|
+
include FieldType
|
6
10
|
include Core
|
11
|
+
|
12
|
+
def self.inherited(subclass)
|
13
|
+
subclass.define_query_definitions
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.establish_connection
|
17
|
+
load_database_configurations
|
18
|
+
new_database_connection
|
19
|
+
end
|
7
20
|
end
|
8
21
|
end
|
data/lib/db2_query/config.rb
CHANGED
@@ -2,27 +2,27 @@
|
|
2
2
|
|
3
3
|
module Db2Query
|
4
4
|
module Config
|
5
|
-
extend ActiveSupport::Concern
|
6
5
|
DEFAULT_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence }
|
7
6
|
|
8
|
-
included
|
9
|
-
|
7
|
+
def self.included(base)
|
8
|
+
base.send(:extend, ClassMethods)
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
module ClassMethods
|
12
|
+
mattr_accessor :configurations
|
13
|
+
@@configurations = nil
|
14
|
+
|
16
15
|
alias config configurations
|
17
16
|
|
17
|
+
def default_path
|
18
|
+
"#{Rails.root}/config/db2query.yml"
|
19
|
+
end
|
20
|
+
|
18
21
|
def load_database_configurations(path = nil)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
else
|
24
|
-
raise Db2Query::Error, "Could not load db2query database configuration. No such file - #{file_path}"
|
25
|
-
end
|
22
|
+
config_file = IO.read(path || default_path)
|
23
|
+
@@configurations = YAML.load(config_file)[DEFAULT_ENV.call].transform_keys(&:to_sym)
|
24
|
+
rescue Exception => e
|
25
|
+
raise Db2Query::Error, e.message
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/db2_query/core.rb
CHANGED
@@ -1,160 +1,101 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Db2Query
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
delegate :run, :do, to: :client
|
8
|
-
|
9
|
-
def initialize(config)
|
10
|
-
@dsn = config[:dsn]
|
11
|
-
@idle_time_limit = config[:idle] || 5
|
12
|
-
@client = new_db_client
|
13
|
-
@last_active = Time.now
|
14
|
-
end
|
15
|
-
|
16
|
-
def expire?
|
17
|
-
Time.now - @last_active > 60 * @idle_time_limit
|
18
|
-
end
|
19
|
-
|
20
|
-
def active?
|
21
|
-
@client.connected?
|
22
|
-
end
|
23
|
-
|
24
|
-
def connected_and_persist?
|
25
|
-
active? && !expire?
|
26
|
-
end
|
27
|
-
|
28
|
-
def disconnect!
|
29
|
-
@client.drop_all
|
30
|
-
@client.disconnect if active?
|
31
|
-
@client = nil
|
4
|
+
module Core
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:extend, ClassMethods)
|
32
7
|
end
|
33
8
|
|
34
|
-
|
35
|
-
|
36
|
-
end
|
9
|
+
module ClassMethods
|
10
|
+
attr_reader :definitions
|
37
11
|
|
38
|
-
|
39
|
-
return @client if connected_and_persist?
|
40
|
-
disconnect!
|
41
|
-
@last_active = Time.now
|
42
|
-
@client = new_db_client
|
43
|
-
end
|
44
|
-
end
|
12
|
+
delegate :query_rows, :query_value, :query_values, :execute, to: :connection
|
45
13
|
|
46
|
-
module Core
|
47
|
-
extend ActiveSupport::Concern
|
48
|
-
included do
|
49
|
-
@@connection = nil
|
50
|
-
@@mutex = Mutex.new
|
51
|
-
end
|
52
|
-
|
53
|
-
class_methods do
|
54
14
|
def initiation
|
55
15
|
yield(self) if block_given?
|
56
16
|
end
|
57
17
|
|
58
|
-
def
|
59
|
-
|
18
|
+
def define_query_definitions
|
19
|
+
@definitions = new_definitions
|
60
20
|
end
|
61
21
|
|
62
|
-
def
|
63
|
-
|
22
|
+
def exec_query_result(query, args)
|
23
|
+
reset_id_when_required(query)
|
24
|
+
connection.exec_query(query, args)
|
64
25
|
end
|
65
26
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
def establish_connection
|
74
|
-
load_database_configurations
|
75
|
-
create_connection
|
76
|
-
end
|
77
|
-
|
78
|
-
def query(name, body)
|
79
|
-
if defined_method_name?(name)
|
80
|
-
raise Db2Query::Error, "You tried to define a scope named \"#{name}\" " \
|
81
|
-
"on the model \"#{self.name}\", but DB2Query already defined " \
|
82
|
-
"a class method with the same name."
|
83
|
-
end
|
84
|
-
|
85
|
-
if body.respond_to?(:call)
|
86
|
-
singleton_class.define_method(name) do |*args|
|
87
|
-
body.call(*args)
|
27
|
+
def query(*query_args)
|
28
|
+
if query_args[1].respond_to?(:call)
|
29
|
+
query_name, body = query_args
|
30
|
+
singleton_class.define_method(query_name) do |*args|
|
31
|
+
body.call(args << { query_name: query_name })
|
88
32
|
end
|
89
|
-
elsif
|
90
|
-
sql =
|
91
|
-
|
92
|
-
|
33
|
+
elsif query_args[0].is_a?(String)
|
34
|
+
sql, query_args = [query_args.first.strip, query_args.drop(1)]
|
35
|
+
query = raw_query(sql, query_args)
|
36
|
+
connection.raw_query(query.db2_spec_sql, query.args)
|
37
|
+
elsif query_args[1].is_a?(String) && query_args[1].strip.length > 0
|
38
|
+
query_name, sql = query_args
|
39
|
+
query = definitions.lookup_query(query_name, sql.strip)
|
40
|
+
singleton_class.define_method(query_name) do |*args|
|
41
|
+
exec_query_result(query, args)
|
93
42
|
end
|
94
43
|
else
|
95
|
-
raise Db2Query::
|
44
|
+
raise Db2Query::QueryMethodError.new
|
96
45
|
end
|
97
46
|
end
|
98
47
|
alias define query
|
99
48
|
|
100
|
-
def fetch(sql, args)
|
101
|
-
|
102
|
-
|
49
|
+
def fetch(sql, args = [])
|
50
|
+
query = definitions.lookup_query(args, sql)
|
51
|
+
query.validate_select_query
|
52
|
+
connection.exec_query(query, args)
|
103
53
|
end
|
104
54
|
|
105
55
|
def fetch_list(sql, args)
|
106
|
-
|
107
|
-
|
108
|
-
raise Db2Query::Error, "The arguments should be an array of list" unless args.is_a?(Array)
|
109
|
-
connection.exec_query({}, sql.gsub("@list", "'#{args.join("', '")}'"), [])
|
110
|
-
end
|
111
|
-
|
112
|
-
def sql_with_extention(sql, extention)
|
113
|
-
validate_sql(sql)
|
114
|
-
raise Db2Query::Error, "Missing @extention pointer at SQL" if sql.scan(/\@extention+/).length == 0
|
115
|
-
sql.gsub("@extention", extention.strip)
|
56
|
+
list = args.first
|
57
|
+
fetch(sql_with_list(sql, list), args.drop(1))
|
116
58
|
end
|
117
59
|
|
118
60
|
private
|
119
|
-
def
|
120
|
-
|
61
|
+
def new_definitions
|
62
|
+
definition_class = "Definitions::#{name}Definitions"
|
63
|
+
Object.const_get(definition_class).new(field_types_map)
|
64
|
+
rescue Exception => e
|
65
|
+
raise Db2Query::Error, e.message
|
121
66
|
end
|
122
67
|
|
123
|
-
def
|
124
|
-
|
68
|
+
def reset_id_when_required(query)
|
69
|
+
if query.insert_sql? && !query.column_id.nil?
|
70
|
+
connection.reset_id_sequence!(query.table_name)
|
71
|
+
end
|
125
72
|
end
|
126
73
|
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
unless sql_statement.is_a? String
|
135
|
-
raise Db2Query::Error, "Query methods must return a SQL statement string!"
|
136
|
-
end
|
137
|
-
|
138
|
-
query(method_name, sql_statement)
|
74
|
+
def raw_query(sql, args)
|
75
|
+
Query.new.tap do |query|
|
76
|
+
query.define_sql(sql)
|
77
|
+
query.define_args(args)
|
78
|
+
end
|
79
|
+
end
|
139
80
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
81
|
+
def define_sql_query(method_name)
|
82
|
+
sql_query_name = sql_query_symbol(method_name)
|
83
|
+
sql_statement = allocate.method(sql_query_name).call
|
84
|
+
define(method_name, sql_statement)
|
85
|
+
end
|
146
86
|
|
87
|
+
def method_missing(method_name, *args, &block)
|
88
|
+
if sql_query_method?(method_name)
|
89
|
+
define_sql_query(method_name)
|
147
90
|
method(method_name).call(*args)
|
148
|
-
elsif
|
149
|
-
|
91
|
+
elsif method_name == :exec_query
|
92
|
+
sql, args = [args.shift, args.first]
|
93
|
+
query = definitions.lookup_query(args, sql)
|
94
|
+
exec_query_result(query, args)
|
150
95
|
else
|
151
96
|
super
|
152
97
|
end
|
153
98
|
end
|
154
|
-
|
155
|
-
def validate_sql(sql)
|
156
|
-
raise Db2Query::Error, "SQL have to be in string format" unless sql.is_a?(String)
|
157
|
-
end
|
158
99
|
end
|
159
100
|
end
|
160
101
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Db2Query
|
4
|
+
class DbClient
|
5
|
+
attr_reader :dsn
|
6
|
+
|
7
|
+
include ActiveModel::Type::Helpers::Timezone
|
8
|
+
|
9
|
+
delegate :run, :do, to: :client
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
@dsn = config[:dsn]
|
13
|
+
@idle_time_limit = config[:idle] || 5
|
14
|
+
@client = new_client
|
15
|
+
@last_transaction = Time.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def expire?
|
19
|
+
Time.now - @last_transaction > 60 * @idle_time_limit
|
20
|
+
end
|
21
|
+
|
22
|
+
def active?
|
23
|
+
@client.connected?
|
24
|
+
end
|
25
|
+
|
26
|
+
def connected_and_persist?
|
27
|
+
active? && !expire?
|
28
|
+
end
|
29
|
+
|
30
|
+
def disconnect!
|
31
|
+
@client.drop_all
|
32
|
+
@client.disconnect if active?
|
33
|
+
@client = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def new_client
|
37
|
+
ODBC.connect(dsn).tap do |odbc_conn|
|
38
|
+
odbc_conn.use_time = true
|
39
|
+
odbc_conn.use_utc = is_utc?
|
40
|
+
end
|
41
|
+
rescue ::ODBC::Error => e
|
42
|
+
raise Db2Query::ConnectionError.new(e.message)
|
43
|
+
end
|
44
|
+
|
45
|
+
def reconnect!
|
46
|
+
disconnect!
|
47
|
+
@client = new_client
|
48
|
+
end
|
49
|
+
|
50
|
+
def client
|
51
|
+
reconnect! unless connected_and_persist?
|
52
|
+
@last_transaction = Time.now
|
53
|
+
@client
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Db2Query
|
4
|
+
module DbConnection
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:extend, ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
mattr_reader :connection
|
11
|
+
@@connection = nil
|
12
|
+
|
13
|
+
def new_database_connection
|
14
|
+
@@connection = Connection.new(config)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Connection
|
20
|
+
class Pool < ConnectionPool
|
21
|
+
def initialize(config, &block)
|
22
|
+
super(config, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def current_state
|
26
|
+
{ size: self.size, available: self.available }
|
27
|
+
end
|
28
|
+
|
29
|
+
def disconnect!
|
30
|
+
shutdown { |client| client.disconnect! }
|
31
|
+
end
|
32
|
+
|
33
|
+
def reload
|
34
|
+
super { |client| client.disconnect! }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :config, :connection_pool, :instrumenter, :lock
|
39
|
+
|
40
|
+
delegate :with, :current_state, :disconnect!, :reload, to: :connection_pool
|
41
|
+
delegate :instrument, to: :instrumenter
|
42
|
+
delegate :synchronize, to: :lock
|
43
|
+
|
44
|
+
include Logger
|
45
|
+
include DbStatements
|
46
|
+
|
47
|
+
def initialize(config)
|
48
|
+
@config = config
|
49
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
50
|
+
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
51
|
+
create_connection_pool
|
52
|
+
end
|
53
|
+
|
54
|
+
alias pool with
|
55
|
+
|
56
|
+
def pool_config
|
57
|
+
{ size: config[:pool], timeout: config[:timeout] }
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_connection_pool
|
61
|
+
synchronize do
|
62
|
+
return @connection_pool if @connection_pool
|
63
|
+
@connection_pool = Pool.new(pool_config) { DbClient.new(config) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|