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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/jdbc_helpers.rb +278 -0
  3. 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
@@ -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: []