jdbc_helpers 0.0.4-java
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 +7 -0
- data/lib/jdbc_helpers.rb +278 -0
- metadata +72 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: da31e2a345c9a088a11b6f3ebb6d7cb1615e68de
|
4
|
+
data.tar.gz: 9132e05e016094c28a6390d3120f060057cabd79
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1cbdbe86780d684d11152ab64e0ce1023e4ed53b9d247d69e12798a644088bf3e9a28a3e9d6dd913e86b1895e37790efccd744f9062e57723e4260dd093bf8dd
|
7
|
+
data.tar.gz: ace30512f1a002d7f2846031651b5e433c970c03f63900e8bb253977d19e6116717ec8f3a3f192acff3a344e26571117717bb8b29461eed26787b0241562edb1
|
data/lib/jdbc_helpers.rb
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
# JDBC helper classes to simplify interactions with JDBC databases.
|
5
|
+
# note that this assumes that you have already instantiated
|
6
|
+
# a JDBC driver into the current namespace.
|
7
|
+
module JDBCHelpers
|
8
|
+
# Base class to provide helper methods that may be generally useful to the concrete classes
|
9
|
+
class Base
|
10
|
+
# Cleanses SQL statements so they can be presented in a log without security risk
|
11
|
+
# currently removes AWS credentials for Redshift copy/unload
|
12
|
+
# @param [String] stmt SQL statement to cleanse
|
13
|
+
# @return [String] cleansed statement
|
14
|
+
def cleanse_statement(stmt)
|
15
|
+
# filter out aws creds from redshift
|
16
|
+
tmp = stmt.to_s
|
17
|
+
return tmp.gsub(
|
18
|
+
/aws_access_key_id\s*=\s*.{20}\s*\;\s*|aws_secret_access_key\s*=\s*(\w|\/|^'){40,41}/i,
|
19
|
+
'<removed>'
|
20
|
+
).gsub(/\s+/,' ')
|
21
|
+
end
|
22
|
+
|
23
|
+
# Array of classes that should be casted to Strings
|
24
|
+
# @return [Array<Class>]
|
25
|
+
def convert_to_string_classes
|
26
|
+
[Java::JavaSql::Timestamp, Java::JavaSql::Date]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Creates a discreet JDBC connection and makes it available via connection attribute
|
31
|
+
class ConnectionFactory < JDBCHelpers::Base
|
32
|
+
attr_accessor :connection
|
33
|
+
|
34
|
+
# @param [String] jdbc_url
|
35
|
+
# @param [String] db_user
|
36
|
+
# @param [String] db_pass
|
37
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
38
|
+
def initialize(jdbc_url, db_user, db_pass, logger = nil)
|
39
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
40
|
+
@logger.info("connecting to #{jdbc_url} as user #{db_user}...")
|
41
|
+
@connection = java.sql.DriverManager.get_connection(
|
42
|
+
jdbc_url,
|
43
|
+
db_user,
|
44
|
+
db_pass
|
45
|
+
)
|
46
|
+
@logger.info("connection successful!") if @connection
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# executes a sql statement
|
51
|
+
class Execute < JDBCHelpers::Base
|
52
|
+
# Rows affected by the SQL statement execution. Note that
|
53
|
+
# not all JDBC drivers/databases handle this appropriately
|
54
|
+
attr_accessor :rows_affected
|
55
|
+
|
56
|
+
# @param [Object] db_connect active connection against which to execute statement
|
57
|
+
# @param [String] statement SQL statement text
|
58
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
59
|
+
def initialize(db_connect, statement, logger = nil)
|
60
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
61
|
+
stmt = db_connect.create_statement
|
62
|
+
@logger.info(
|
63
|
+
"executing statement: #{cleanse_statement(statement)}"
|
64
|
+
)
|
65
|
+
start = Time.new.utc
|
66
|
+
@rows_affected = stmt.execute_update(statement)
|
67
|
+
@logger.info "query executed #{Time.new.utc - start} seconds"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Executes a select query, then returns the first field from the first row
|
72
|
+
class SingleValueFromQuery < JDBCHelpers::Base
|
73
|
+
# Value of the first field from the first row. class will vary.
|
74
|
+
attr_accessor :result
|
75
|
+
|
76
|
+
# @param [Object] db_connect active connection against which to execute statement
|
77
|
+
# @param [String] statement SQL statement text
|
78
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
79
|
+
def initialize(db_connect, statement, logger = nil)
|
80
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
81
|
+
stmt = db_connect.create_statement
|
82
|
+
@logger.info(
|
83
|
+
"executing query: #{cleanse_statement(statement)}"
|
84
|
+
)
|
85
|
+
start = Time.new.utc
|
86
|
+
rs = stmt.execute_query(statement)
|
87
|
+
@logger.info "query executed #{Time.new.utc-start} seconds"
|
88
|
+
rs.next
|
89
|
+
value = rs.getObject(1)
|
90
|
+
rs.close
|
91
|
+
|
92
|
+
#the below simplifies things... especially comparisons
|
93
|
+
#if you don't like it you can write your own damn helper!
|
94
|
+
value = value.to_s if convert_to_string_classes.include?(value.class)
|
95
|
+
@result = value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Execute a SQL query, store the results as an array of hashes.
|
100
|
+
class QueryResultsToArray < JDBCHelpers::Base
|
101
|
+
# Contains the array of hashes returned from the select query
|
102
|
+
attr_accessor :results
|
103
|
+
|
104
|
+
# @param [Object] db_connect active connection against which to execute statement
|
105
|
+
# @param [String] statement SQL statement text
|
106
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
107
|
+
def initialize(db_connect, statement, logger = nil)
|
108
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
109
|
+
stmt = db_connect.create_statement
|
110
|
+
@logger.info "executing query: #{cleanse_statement(statement)}"
|
111
|
+
start = Time.new.utc
|
112
|
+
rs = stmt.execute_query(statement)
|
113
|
+
@logger.info("query executed #{Time.new.utc - start} seconds")
|
114
|
+
@results = rs_to_array(rs)
|
115
|
+
end
|
116
|
+
|
117
|
+
# converts a JDBC recordset to an array of hashes, with one hash per record
|
118
|
+
# @param [Object] rs JDBC result set object
|
119
|
+
# @return [Array<Hash>] Array of Hash with a Hash for each record
|
120
|
+
def rs_to_array(rs)
|
121
|
+
# creates an array of hashes from a jdbc record set
|
122
|
+
arr = []
|
123
|
+
|
124
|
+
# get basic metadata for the recordset
|
125
|
+
meta = rs.getMetaData
|
126
|
+
cols = meta.getColumnCount.to_i
|
127
|
+
|
128
|
+
# loop through the records to add them into hash
|
129
|
+
while rs.next do
|
130
|
+
# r is a temporary hash for the row being processed
|
131
|
+
r = {}
|
132
|
+
|
133
|
+
# add each row to r
|
134
|
+
(1..cols).each do |c|
|
135
|
+
r[meta.get_column_name(c)] = rs.getObject(c)
|
136
|
+
if convert_to_string_classes.include?(r[meta.get_column_name(c)].class)
|
137
|
+
r[meta.get_column_name(c)] = r[meta.get_column_name(c)].to_s
|
138
|
+
end
|
139
|
+
end # each cols
|
140
|
+
|
141
|
+
# append hash to array
|
142
|
+
arr << r
|
143
|
+
end # while
|
144
|
+
|
145
|
+
# completed hash is returned
|
146
|
+
return arr
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Creates a hash of arrays of hashes from a SQL query
|
151
|
+
class QueryResultsToHashOfArrays < JDBCHelpers::Base
|
152
|
+
attr_accessor :results
|
153
|
+
|
154
|
+
# @param [Object] db_connect active connection against which to execute statement
|
155
|
+
# @param [String] statement SQL statement text
|
156
|
+
# @param [String] key_field SQL result set field containing the key to be used in the top level of hash
|
157
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
158
|
+
def initialize(db_connect, statement, key_field, logger = nil)
|
159
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
160
|
+
stmt = db_connect.create_statement
|
161
|
+
@logger.info "executing query in thread #{Thread.current.object_id}:\n#{cleanse_statement(statement)}"
|
162
|
+
start = Time.new.utc
|
163
|
+
rs = stmt.execute_query(statement)
|
164
|
+
@logger.info "query executed #{Time.new.utc - start} seconds"
|
165
|
+
@results=rs_to_hash(rs, key_field, true)
|
166
|
+
end
|
167
|
+
|
168
|
+
# converts a JDBC recordset to an array of hashes, with one hash per record
|
169
|
+
# creates a hash from a jdbc record set
|
170
|
+
# index_key_field is the field you want to use as the top level
|
171
|
+
# hash key... and should exist in the record set
|
172
|
+
# multi_val=true will create an array below each index_key_filed,
|
173
|
+
# false will create a hash as the child
|
174
|
+
# @param [Object] rs JDBC result set object
|
175
|
+
# @param [String] index_key_field field to use as top level hash keys
|
176
|
+
# @return [Hash] Hash of Arrays of Hashes for each record
|
177
|
+
def rs_to_hash(rs, index_key_field, multi_val)
|
178
|
+
# setting default hash value is necessary for appending to arrays
|
179
|
+
hash=Hash.new{ |h, k| h[k] = [] }
|
180
|
+
|
181
|
+
# get basic metadata for the recordset
|
182
|
+
meta = rs.getMetaData
|
183
|
+
cols = meta.getColumnCount.to_i
|
184
|
+
|
185
|
+
# loop through the records to add them into hash
|
186
|
+
while rs.next do
|
187
|
+
# if multi_val is not true... create new hash value as an empty hash if it doesn't already exist
|
188
|
+
hash[rs.getString(index_key_field)]={} if (!hash[rs.getString(index_key_field)] and !multi_val)
|
189
|
+
|
190
|
+
# if multi_val is true... create new hash value as an empty array if it doesn't already exist
|
191
|
+
hash[rs.getString(index_key_field)]=[] if (!hash[rs.getString(index_key_field)] and multi_val)
|
192
|
+
|
193
|
+
# r is a temporary hash for the row being processed
|
194
|
+
r=Hash.new
|
195
|
+
|
196
|
+
# add each row to r
|
197
|
+
(1..cols).each do |c|
|
198
|
+
r[meta.get_column_name(c)] = rs.getObject(c)
|
199
|
+
if convert_to_string_classes.include?(r[meta.get_column_name(c)].class)
|
200
|
+
r[meta.get_column_name(c)] = r[meta.get_column_name(c)].to_s
|
201
|
+
end
|
202
|
+
end # each cols
|
203
|
+
|
204
|
+
# set hash value to r if not multi_val
|
205
|
+
hash[rs.getString(index_key_field)] = r if !multi_val
|
206
|
+
|
207
|
+
# append hash to r if multi_val
|
208
|
+
hash[rs.getString(index_key_field)] << r if multi_val
|
209
|
+
end # while
|
210
|
+
|
211
|
+
# completed hash is returned
|
212
|
+
return hash
|
213
|
+
end
|
214
|
+
end # class
|
215
|
+
|
216
|
+
class QueryResultsToJSONFile < JDBCHelpers::Base
|
217
|
+
# runs a SQL query, then writes the results as JSON objects to a provided file object.
|
218
|
+
# by use of the formatter parameter, you can change the output to any format you desire (CSV, XML, etc)
|
219
|
+
# see json_formatter method for an example of a proc to perform formatting
|
220
|
+
# @param [Object] db_connect active connection against which to execute statement
|
221
|
+
# @param [String] statement SQL statement text
|
222
|
+
# @param [IO] file_object IO object to receive the formatted results
|
223
|
+
# @param [proc] formatter proc to handle the actual formatting, defaults to JSON if nil
|
224
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
225
|
+
def initialize(db_connect, statement, file_object, formatter = nil, logger = nil)
|
226
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
227
|
+
stmt = db_connect.create_statement
|
228
|
+
@logger.info "executing query: #{cleanse_statement(statement)}"
|
229
|
+
start = Time.new.utc
|
230
|
+
rs = stmt.execute_query(statement)
|
231
|
+
@logger.info "query executed initial results #{Time.new.utc - start} seconds"
|
232
|
+
record_count = rs_to_json_file(rs, file_object, formatter)
|
233
|
+
@logger.info "query export time #{Time.new.utc - start} seconds"
|
234
|
+
@logger.info "#{record_count} rows exported"
|
235
|
+
end
|
236
|
+
|
237
|
+
# outputs a JDBC result set to a formatted file
|
238
|
+
# formatter defaults to JSON output unless you provide your own proc
|
239
|
+
# @param [Object] rs JDBC result set
|
240
|
+
# @param [IO] file_object IO object to receive the formatted results
|
241
|
+
# @param [proc] formatter proc to handle the actual formatting, defaults to JSON if nil
|
242
|
+
def rs_to_json_file(rs, file_object, formatter)
|
243
|
+
# default formatter outputs json objects for each row
|
244
|
+
formatter = json_formatter unless formatter
|
245
|
+
|
246
|
+
# get basic metadata for the recordset
|
247
|
+
meta = rs.getMetaData
|
248
|
+
cols = meta.getColumnCount.to_i
|
249
|
+
|
250
|
+
record_count = 0
|
251
|
+
# loop through the records to add them into hash
|
252
|
+
while rs.next do
|
253
|
+
|
254
|
+
# r is a temporary hash for the row being processed
|
255
|
+
r = Hash.new
|
256
|
+
|
257
|
+
# add each row to r
|
258
|
+
(1..cols).each do |c|
|
259
|
+
r[meta.get_column_name(c)] = rs.getObject(c)
|
260
|
+
if convert_to_string_classes.include?(r[meta.get_column_name(c)].class)
|
261
|
+
r[meta.get_column_name(c)] = r[meta.get_column_name(c)].to_s
|
262
|
+
end
|
263
|
+
end # each cols
|
264
|
+
|
265
|
+
# formatter handles output of r to file
|
266
|
+
formatter.call(file_object, r)
|
267
|
+
record_count += 1
|
268
|
+
end # while
|
269
|
+
return record_count
|
270
|
+
end
|
271
|
+
|
272
|
+
# proc must handle two inputs, |file_object, record hash|
|
273
|
+
# @return [proc] returns proc to output json
|
274
|
+
def json_formatter
|
275
|
+
Proc.new { |f,h| f.puts h.to_json }
|
276
|
+
end
|
277
|
+
end # class
|
278
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jdbc_helpers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: java
|
6
|
+
authors:
|
7
|
+
- jeremy winters
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 1.2.8
|
19
|
+
name: logger
|
20
|
+
prerelease: false
|
21
|
+
type: :runtime
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.2.8
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.9.0
|
33
|
+
name: minitest
|
34
|
+
prerelease: false
|
35
|
+
type: :development
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 5.9.0
|
41
|
+
description: allows for easy conversion of query results to hashes, arrays of hashes, json files, etc.
|
42
|
+
email: jeremy.winters@full360.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/jdbc_helpers.rb
|
48
|
+
homepage: https://www.full360.com
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
metadata: {}
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.6.4
|
69
|
+
signing_key:
|
70
|
+
specification_version: 4
|
71
|
+
summary: helpers for jdbc interaction
|
72
|
+
test_files: []
|