jdbc-helper 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,251 @@
1
+ ```
2
+ _ _ _ _ _
3
+ (_) | | | | | | |
4
+ _ __| | |__ ___ ______| |__ ___| |_ __ ___ _ __
5
+ | |/ _` | '_ \ / __|______| '_ \ / _ \ | '_ \ / _ \ '__|
6
+ | | (_| | |_) | (__ | | | | __/ | |_) | __/ |
7
+ | |\__,_|_.__/ \___| |_| |_|\___|_| .__/ \___|_|
8
+ _/ | | |
9
+ |__/ |_|
10
+ ```
11
+
12
+ # jdbc-helper
13
+
14
+ A JDBC helper for Ruby/Database developers.
15
+ JDBCHelper::Connection object wraps around a JDBC connection and provides much nicer interface to
16
+ crucial database operations from primitive selects and updates to more complex ones involving
17
+ batch updates, prepared statements and transactions.
18
+ As the name implies, this gem only works on JRuby.
19
+
20
+ ## Installation
21
+ ### Install gem
22
+ ```
23
+ gem install jdbc-helper
24
+ ```
25
+
26
+ ### Setting up CLASSPATH
27
+ Add the appropriate JDBC drivers to the CLASSPATH.
28
+
29
+ ```
30
+ export CLASSPATH=$CLASSPATH:~/lib/mysql-connector-java-5.1.16-bin.jar:~/lib/ojdbc6.jar
31
+ ```
32
+
33
+ ### In Ruby
34
+ ```ruby
35
+ require 'jdbc-helper'
36
+ ```
37
+
38
+ ## Examples
39
+ ### Connecting to a database
40
+ ```ruby
41
+ # :driver and :url must be given
42
+ conn = JDBCHelper::Connection.new(
43
+ :driver => 'com.mysql.jdbc.Driver',
44
+ :url => 'jdbc:mysql://localhost/test')
45
+ conn.close
46
+
47
+
48
+ # Optional :user and :password
49
+ conn = JDBCHelper::Connection.new(
50
+ :driver => 'com.mysql.jdbc.Driver',
51
+ :url => 'jdbc:mysql://localhost/test',
52
+ :user => 'mysql',
53
+ :password => '')
54
+ conn.close
55
+
56
+
57
+ # MySQL shortcut connector
58
+ conn = JDBCHelper::MySQLConnector.connect('localhost', 'mysql', '', 'test')
59
+ conn.close
60
+
61
+ # Oracle shortcut connector
62
+ conn = JDBCHelper::OracleConnector.connect(host, user, password, service_name)
63
+ conn.close
64
+ ```
65
+
66
+ ### Querying database table
67
+
68
+ ```ruby
69
+ conn.query("SELECT a, b, c FROM T") do |row|
70
+ row.labels
71
+ row.rownum
72
+
73
+ row.a, row.b, row.c # Dot-notation
74
+ row[0], row[1], row[2] # Numeric index
75
+ row['a'], row['b'], row['c'] # String index. Case-insensitive.
76
+ row[:a], row[:b], row[:c] # Symbol index. Case-insensitive.
77
+
78
+ row[0..-1] # Range index. Returns an array of values.
79
+ row[0, 3] # Offset and length. Returns an array of values.
80
+ end
81
+
82
+ # Returns an array of rows when block is not given
83
+ rows = conn.query("SELECT b FROM T")
84
+ uniq_rows = rows.uniq
85
+
86
+ # You can even nest queries
87
+ conn.query("SELECT a FROM T") do |row1|
88
+ conn.query("SELECT * FROM T_#{row1.a}") do |row2|
89
+ # ...
90
+ end
91
+ end
92
+
93
+ # `enumerate' method returns an Enumerable object if block is not given.
94
+ # When the result set of the query is expected to be large and you wish to
95
+ # chain enumerators, `enumerate' is much preferred over `query'. (which returns the
96
+ # array of the entire rows)
97
+ conn.enumerate("SELECT * FROM LARGE_T").each_slice(1000) do |slice|
98
+ slice.each do | row |
99
+ # ...
100
+ end
101
+ end
102
+ ```
103
+
104
+ ### Updating database table
105
+ ```ruby
106
+ del_count = conn.update("DELETE FROM T")
107
+ ```
108
+
109
+ ### Transaction
110
+ ```ruby
111
+ committed = conn.transaction do |tx|
112
+ # ...
113
+ # Transaction logic here
114
+ # ...
115
+
116
+ if success
117
+ tx.commit
118
+ else
119
+ tx.rollback
120
+ end
121
+ # You never reach here.
122
+ end
123
+ ```
124
+
125
+ ### Using batch interface
126
+ ```ruby
127
+ conn.add_batch("DELETE FROM T");
128
+ conn.execute_batch
129
+ conn.add_batch("DELETE FROM T");
130
+ conn.clear_batch
131
+ ```
132
+
133
+ ### Using prepared statements
134
+ ```ruby
135
+ p_sel = conn.prepare("SELECT * FROM T WHERE b = ? and c = ?")
136
+ p_sel.query(100, 200) do |row|
137
+ p row
138
+ end
139
+ p_sel.close
140
+
141
+ p_upd = conn.prepare("UPDATE T SET a = ? WHERE b = ?")
142
+ count = 0
143
+ 100.times do |i|
144
+ count += p_upd.update('updated a', i)
145
+ end
146
+
147
+ p_upd.add_batch('pstmt + batch', 10)
148
+ p_upd.add_batch('pstmt + batch', 20)
149
+ p_upd.add_batch('pstmt + batch', 30)
150
+ p_upd.execute_batch
151
+ p_upd.close
152
+ ```
153
+
154
+ ### Using table wrappers (since 0.2.0)
155
+ ```ruby
156
+ # For more complex examples, refer to test/test_object_wrapper.rb
157
+
158
+ # Creates a table wrapper
159
+ table = conn.table('test.data')
160
+
161
+ # Counting the records in the table
162
+ table.count
163
+ table.count(:a => 10)
164
+ table.where(:a => 10).count
165
+
166
+ table.empty?
167
+ table.where(:a => 10).empty?
168
+
169
+ # Selects the table by combining select, where, and order methods
170
+ table.select('a apple', :b).where(:c => (1..10)).order('b desc', 'a asc') do |row|
171
+ puts row.apple
172
+ end
173
+
174
+ # Build select SQL
175
+ sql = table.select('a apple', :b).where(:c => (1..10)).order('b desc', 'a asc').sql
176
+
177
+ # Updates with conditions
178
+ table.update(:a => 'hello', :b => JDBCHelper::SQL('now()'), :where => { :c => 3 })
179
+ # Or equivalently,
180
+ table.where(:c => 3).update(:a => 'hello', :b => JDBCHelper::SQL('now()'))
181
+
182
+ # Insert into the table
183
+ table.insert(:a => 10, :b => 20, :c => JDBCHelper::SQL('10 + 20'))
184
+ table.insert_ignore(:a => 10, :b => 20, :c => 30)
185
+ table.replace(:a => 10, :b => 20, :c => 30)
186
+
187
+ # Update with common default values
188
+ with_defaults = table.default(:a => 10, :b => 20)
189
+ with_defaults.insert(:c => 30)
190
+ with_defaults.where('a != 10 or b != 20').update # sets a => 10, b => 20
191
+
192
+ # Batch updates with batch method
193
+ table.batch.insert(:a => 10, :b => 20, :c => JDBCHelper::SQL('10 + 20'))
194
+ table.batch.insert_ignore(:a => 10, :b => 20, :c => 30)
195
+ conn.execute_batch
196
+
197
+ # Delete with conditions
198
+ table.delete(:c => 3)
199
+ # Or equivalently,
200
+ table.where(:c => 3).delete
201
+
202
+ # Truncate or drop table (Cannot be undone)
203
+ table.truncate!
204
+ table.drop!
205
+ ```
206
+
207
+ ### Using function wrappers (since 0.2.2)
208
+ ```ruby
209
+ conn.function(:mod).call 5, 3
210
+ conn.function(:coalesce).call(nil, nil, 'king')
211
+ ```
212
+
213
+ ### Using procedure wrappers (since 0.3.0)
214
+ ```ruby
215
+ # Working with IN/INOUT/OUT parameteres
216
+ # Bind by ordinal number
217
+ conn.procedure(:update_and_fetch_something).call(
218
+ 100, # Input parameter
219
+ ["value", String], # Input/Output parameter
220
+ Fixnum # Output parameter
221
+ )
222
+
223
+ # Bind by parameter name
224
+ conn.procedure(:update_and_fetch_something).call(
225
+ :a => 100, :b => ["value", String], :c => Fixnum)
226
+ ```
227
+
228
+ ### Using sequence wrappers (since 0.4.2)
229
+ ```ruby
230
+ seq = conn.sequence(:my_seq)
231
+ next = seq.nextval
232
+ curr = seq.currval
233
+ seq.reset!
234
+ seq.reset! 100
235
+ ```
236
+
237
+ ## Contributing to jdbc-helper
238
+
239
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
240
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
241
+ * Fork the project
242
+ * Start a feature/bugfix branch
243
+ * Commit and push until you are happy with your contribution
244
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
245
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
246
+
247
+ ## Copyright
248
+
249
+ Copyright (c) 2011 Junegunn Choi. See LICENSE.txt for
250
+ further details.
251
+
data/lib/jdbc-helper.rb CHANGED
@@ -2,19 +2,10 @@
2
2
  # Junegunn Choi (junegunn.c@gmail.com)
3
3
 
4
4
  if RUBY_PLATFORM.match(/java/).nil?
5
- raise LoadError, 'JRuby is required for JDBC'
5
+ raise LoadError, 'JRuby is required for JDBC'
6
6
  end
7
7
 
8
8
  require 'java'
9
-
10
- module JavaLang # :nodoc:
11
- include_package 'java.lang'
12
- end
13
-
14
- module JavaSql # :nodoc:
15
- include_package 'java.sql'
16
- end
17
-
18
9
  require 'jdbc-helper/sql'
19
10
  require 'jdbc-helper/sql_prepared'
20
11
  require 'jdbc-helper/constants'
@@ -46,7 +46,7 @@ module JDBCHelper
46
46
  # # MySQL shortcut connector
47
47
  # conn = JDBCHelper::MySQLConnector.connect('localhost', 'mysql', '', 'test')
48
48
  # conn.close
49
- #
49
+ #
50
50
  # @example Querying database table
51
51
  #
52
52
  # conn.query("SELECT a, b, c FROM T") do | row |
@@ -109,89 +109,85 @@ module JDBCHelper
109
109
  # p_upd.execute_batch
110
110
  # p_upd.close
111
111
  class Connection
112
- # Returns the statistics of the previous operation
113
- # @return [JDBCHelper::Connection::Stat] The statistics of the previous operation.
114
- def prev_stat
115
- @prev_stat.dup
116
- end
117
-
118
- # Returns the accumulated statistics of each operation
119
- # @return [Hash] Accumulated statistics of each type of operation
120
- attr_reader :stats
121
-
122
- # JDBC URL of the connection
123
- # @return [String]
124
- attr_reader :url
125
-
126
- # JDBC driver of the connection
127
- # @return [String]
128
- attr_reader :driver
129
-
130
- # Returns the underlying JDBC Connection object.
131
- # Only use this when you really need to access it directly.
132
- def jdbc_conn
133
- @conn
134
- end
135
- alias java_obj jdbc_conn
136
-
137
- # Creates a database connection.
138
- # - `args` hash must include :driver (or "driver") and :url (or "url")
139
- # - and takes optional :user and :password tuples (or "user", "password")
140
- # - You can also specify :timeout (or "timeout") to override the default connection timeout (60 seconds)
141
- #
142
- # Must be closed explicitly if not used.
143
- # If a block is given, the connection is automatically closed after executing the block.
144
- # @param [Hash] args
145
- def initialize(args = {})
146
- # Subsequent deletes should not affect the input
112
+ # Returns the statistics of the previous operation
113
+ # @return [JDBCHelper::Connection::Stat] The statistics of the previous operation.
114
+ def prev_stat
115
+ @prev_stat.dup
116
+ end
117
+
118
+ # Returns the accumulated statistics of each operation
119
+ # @return [Hash] Accumulated statistics of each type of operation
120
+ attr_reader :stats
121
+
122
+ # JDBC URL of the connection
123
+ # @return [String]
124
+ attr_reader :url
125
+
126
+ # JDBC driver of the connection
127
+ # @return [String]
128
+ attr_reader :driver
129
+
130
+ # Returns the underlying JDBC Connection object.
131
+ # Only use this when you really need to access it directly.
132
+ def jdbc_conn
133
+ @conn
134
+ end
135
+ alias java_obj jdbc_conn
136
+
137
+ # Creates a database connection.
138
+ # - `args` hash must include :driver (or "driver") and :url (or "url")
139
+ # - and takes optional :user and :password tuples (or "user", "password")
140
+ # - You can also specify :timeout (or "timeout") to override the default connection timeout (60 seconds)
141
+ #
142
+ # Must be closed explicitly if not used.
143
+ # If a block is given, the connection is automatically closed after executing the block.
144
+ # @param [Hash] args
145
+ def initialize(args = {})
146
+ # Subsequent deletes should not affect the input
147
147
  @args = args.dup
148
- args = @args.dup
148
+ args = @args.dup
149
149
 
150
- # String/Symbol
151
- %w[driver url user password timeout].each do | strk |
152
- args[strk.to_sym] = args.delete strk if args.has_key? strk
153
- end
150
+ # String/Symbol
151
+ %w[driver url user password timeout].each do | strk |
152
+ args[strk.to_sym] = args.delete strk if args.has_key? strk
153
+ end
154
154
 
155
- raise ArgumentError.new("driver not given") unless args.has_key? :driver
156
- raise ArgumentError.new("url not given") unless args.has_key? :url
155
+ raise ArgumentError.new("driver not given") unless args.has_key? :driver
156
+ raise ArgumentError.new("url not given") unless args.has_key? :url
157
157
 
158
- @driver = args.delete :driver
159
- @url = args.delete :url
158
+ @driver = args.delete :driver
159
+ @url = args.delete :url
160
160
 
161
- begin
162
- Java::JavaClass.for_name @driver
163
- rescue Exception
164
- # TODO
165
- raise
166
- end
161
+ # NameError will be thrown for invalid drivers
162
+ Java::JavaClass.for_name @driver
167
163
 
168
- timeout = args.has_key?(:timeout) ? args.delete(:timeout) : Constants::DEFAULT_LOGIN_TIMEOUT
169
- JavaSql::DriverManager.setLoginTimeout timeout if timeout
164
+ timeout = args.has_key?(:timeout) ? args.delete(:timeout) : Constants::DEFAULT_LOGIN_TIMEOUT
165
+ java.sql.DriverManager.setLoginTimeout timeout if timeout
170
166
 
171
- props = java.util.Properties.new
172
- (args.keys - [:url, :driver]).each do | key |
173
- props.setProperty(key.to_s, args[key].to_s) if args[key]
174
- end
175
-
176
- @conn = JavaSql::DriverManager.get_connection(@url, props)
177
- @spool = StatementPool.send :new, self
178
- @bstmt = nil
167
+ props = java.util.Properties.new
168
+ (args.keys - [:url, :driver]).each do | key |
169
+ props.setProperty(key.to_s, args[key].to_s) if args[key]
170
+ end
171
+
172
+ @conn = java.sql.DriverManager.get_connection(@url, props)
173
+ @spool = StatementPool.send :new, self
174
+ @bstmt = nil
179
175
 
180
- @stats = Hash.new { | h, k | h[k] = Stat.new(k, 0, 0, 0) }
181
- @prev_stat = Stat.new(nil, 0, 0, 0)
176
+ @stats = Hash.new { | h, k | h[k] = Stat.new(k, 0, 0, 0) }
177
+ @prev_stat = Stat.new(nil, 0, 0, 0)
182
178
 
183
179
  @pstmts = []
184
180
 
185
181
  @table_wrappers = {}
186
182
 
187
- if block_given?
188
- begin
189
- yield self
190
- ensure
191
- close rescue nil
192
- end
193
- end
194
- end
183
+ if block_given?
184
+ begin
185
+ yield self
186
+ ensure
187
+ close rescue nil
188
+ end
189
+ end
190
+ end
195
191
 
196
192
  # Creates another connection with the same parameters as this Connection.
197
193
  # @return [JDBCHelper::Connection]
@@ -201,140 +197,140 @@ class Connection
201
197
  nc
202
198
  end
203
199
 
204
- # Creates a prepared statement, which is also an encapsulation of Java PreparedStatement object
205
- # @param [String] qstr SQL string
206
- def prepare(qstr)
207
- check_closed
200
+ # Creates a prepared statement, which is also an encapsulation of Java PreparedStatement object
201
+ # @param [String] qstr SQL string
202
+ def prepare(qstr)
203
+ check_closed
208
204
 
209
- pstmt = PreparedStatement.send(:new, self, qstr,
210
- measure_exec(:prepare) { @conn.prepare_statement(qstr) })
205
+ pstmt = PreparedStatement.send(:new, self, qstr,
206
+ measure_exec(:prepare) { @conn.prepare_statement(qstr) })
211
207
  @pstmts << pstmt
212
208
  pstmt
213
- end
209
+ end
214
210
 
215
211
  # @return [Array] Prepared statements currently opened for this connection
216
212
  def prepared_statements
217
213
  @pstmts
218
214
  end
219
215
 
220
- # Creates a callable statement.
221
- # @param [String] qstr SQL string
222
- def prepare_call(qstr)
223
- check_closed
224
-
225
- CallableStatement.send(:new, self, qstr,
226
- measure_exec(:prepare_call) { @conn.prepare_call qstr })
227
- end
228
-
229
- # Executes the given code block as a transaction. Returns true if the transaction is committed.
230
- # A transaction object is passed to the block, which only has commit and rollback methods.
231
- # The execution breaks out of the code block when either of the methods is called.
232
- # @yield [JDBCHelper::Connection::Transaction] Responds to commit and rollback.
233
- # @return [Boolean] True if committed
234
- def transaction
235
- check_closed
236
-
237
- raise ArgumentError.new("Transaction block not given") unless block_given?
238
- tx = Transaction.send :new, @conn
239
- ac = @conn.get_auto_commit
240
- status = :unknown
241
- begin
242
- @conn.set_auto_commit false
243
- yield tx
244
- @conn.commit
245
- status = :committed
246
- rescue Transaction::Commit
247
- status = :committed
248
- rescue Transaction::Rollback
249
- status = :rolledback
250
- ensure
251
- @conn.rollback if status == :unknown
252
- @conn.set_auto_commit ac
253
- end
254
- status == :committed
255
- end
256
-
257
- # Executes an update and returns the count of the updated rows.
258
- # @param [String] qstr SQL string
259
- # @return [Fixnum] Count of affected records
260
- def update(qstr)
261
- check_closed
262
-
263
- @spool.with do | stmt |
264
- ret = measure_exec(:update) { stmt.execute_update(qstr) }
265
- end
266
- end
267
-
268
- # Executes a select query.
269
- # When a code block is given, each row of the result is passed to the block one by one.
270
- # If a code block not given, this method will return the array of the entire result rows.
271
- # (which can be pretty inefficient when the result set is large. In such cases, use enumerate instead.)
272
- #
273
- # The concept of statement object of JDBC is encapsulated, so there's no need to do additional task,
274
- # when you nest select queries, for example.
275
- #
276
- # conn.query("SELECT a FROM T") do | trow |
277
- # conn.query("SELECT * FROM U_#{trow.a}") do | urow |
278
- # # ... and so on ...
279
- # end
280
- # end
281
- # @param [String] qstr SQL string
282
- # @yield [JDBCHelper::Connection::Row]
283
- # @return [Array]
284
- def query(qstr, &blk)
285
- check_closed
286
-
287
- @spool.with do | stmt |
288
- measure_exec(:query) { stmt.execute(qstr) }
289
- process_and_close_rset(stmt.get_result_set, &blk)
290
- end
291
- end
292
-
293
- # Returns an enumerable object of the query result.
294
- # "enumerate" method is preferable when dealing with a large result set,
295
- # since it doesn't have to build a large array.
296
- #
297
- # The returned enumerator is automatically closed after enumeration.
298
- #
299
- # conn.enumerate('SELECT * FROM T').each_slice(10) do | slice |
300
- # slice.each { | row | print row }
301
- # puts
302
- # end
303
- #
304
- # @param [String] qstr SQL string
305
- # @yield [JDBCHelper::Connection::Row] Yields each record if block is given
306
- # @return [JDBCHelper::Connection::ResultSetEnumerator] Returns an enumerator if block is not given
307
- def enumerate(qstr, &blk)
308
- check_closed
309
-
310
- return query(qstr, &blk) if block_given?
311
-
312
- stmt = @spool.take
313
- begin
314
- measure_exec(:query) { stmt.execute(qstr) }
315
- rescue Exception
316
- @spool.give stmt
317
- raise
318
- end
319
-
320
- ResultSetEnumerator.send(:new, stmt.get_result_set) { @spool.give stmt }
321
- end
322
-
323
- # Adds a statement to be executed in batch
324
- # Adds to the batch
325
- # @param [String] qstr
326
- # @return [NilClass]
327
- def add_batch(qstr)
328
- check_closed
329
-
330
- @bstmt ||= @spool.take
331
- @bstmt.add_batch qstr
332
- end
333
-
334
- # Executes batched statements including prepared statements. No effect when no statment is added
335
- # @return [NilClass]
336
- def execute_batch
337
- check_closed
216
+ # Creates a callable statement.
217
+ # @param [String] qstr SQL string
218
+ def prepare_call(qstr)
219
+ check_closed
220
+
221
+ CallableStatement.send(:new, self, qstr,
222
+ measure_exec(:prepare_call) { @conn.prepare_call qstr })
223
+ end
224
+
225
+ # Executes the given code block as a transaction. Returns true if the transaction is committed.
226
+ # A transaction object is passed to the block, which only has commit and rollback methods.
227
+ # The execution breaks out of the code block when either of the methods is called.
228
+ # @yield [JDBCHelper::Connection::Transaction] Responds to commit and rollback.
229
+ # @return [Boolean] True if committed
230
+ def transaction
231
+ check_closed
232
+
233
+ raise ArgumentError.new("Transaction block not given") unless block_given?
234
+ tx = Transaction.send :new, @conn
235
+ ac = @conn.get_auto_commit
236
+ status = :unknown
237
+ begin
238
+ @conn.set_auto_commit false
239
+ yield tx
240
+ @conn.commit
241
+ status = :committed
242
+ rescue Transaction::Commit
243
+ status = :committed
244
+ rescue Transaction::Rollback
245
+ status = :rolledback
246
+ ensure
247
+ @conn.rollback if status == :unknown
248
+ @conn.set_auto_commit ac
249
+ end
250
+ status == :committed
251
+ end
252
+
253
+ # Executes an update and returns the count of the updated rows.
254
+ # @param [String] qstr SQL string
255
+ # @return [Fixnum] Count of affected records
256
+ def update(qstr)
257
+ check_closed
258
+
259
+ @spool.with do | stmt |
260
+ ret = measure_exec(:update) { stmt.execute_update(qstr) }
261
+ end
262
+ end
263
+
264
+ # Executes a select query.
265
+ # When a code block is given, each row of the result is passed to the block one by one.
266
+ # If a code block not given, this method will return the array of the entire result rows.
267
+ # (which can be pretty inefficient when the result set is large. In such cases, use enumerate instead.)
268
+ #
269
+ # The concept of statement object of JDBC is encapsulated, so there's no need to do additional task,
270
+ # when you nest select queries, for example.
271
+ #
272
+ # conn.query("SELECT a FROM T") do | trow |
273
+ # conn.query("SELECT * FROM U_#{trow.a}") do | urow |
274
+ # # ... and so on ...
275
+ # end
276
+ # end
277
+ # @param [String] qstr SQL string
278
+ # @yield [JDBCHelper::Connection::Row]
279
+ # @return [Array]
280
+ def query(qstr, &blk)
281
+ check_closed
282
+
283
+ @spool.with do | stmt |
284
+ measure_exec(:query) { stmt.execute(qstr) }
285
+ process_and_close_rset(stmt.get_result_set, &blk)
286
+ end
287
+ end
288
+
289
+ # Returns an enumerable object of the query result.
290
+ # "enumerate" method is preferable when dealing with a large result set,
291
+ # since it doesn't have to build a large array.
292
+ #
293
+ # The returned enumerator is automatically closed after enumeration.
294
+ #
295
+ # conn.enumerate('SELECT * FROM T').each_slice(10) do | slice |
296
+ # slice.each { | row | print row }
297
+ # puts
298
+ # end
299
+ #
300
+ # @param [String] qstr SQL string
301
+ # @yield [JDBCHelper::Connection::Row] Yields each record if block is given
302
+ # @return [JDBCHelper::Connection::ResultSetEnumerator] Returns an enumerator if block is not given
303
+ def enumerate(qstr, &blk)
304
+ check_closed
305
+
306
+ return query(qstr, &blk) if block_given?
307
+
308
+ stmt = @spool.take
309
+ begin
310
+ measure_exec(:query) { stmt.execute(qstr) }
311
+ rescue Exception
312
+ @spool.give stmt
313
+ raise
314
+ end
315
+
316
+ ResultSetEnumerator.send(:new, stmt.get_result_set) { @spool.give stmt }
317
+ end
318
+
319
+ # Adds a statement to be executed in batch
320
+ # Adds to the batch
321
+ # @param [String] qstr
322
+ # @return [NilClass]
323
+ def add_batch(qstr)
324
+ check_closed
325
+
326
+ @bstmt ||= @spool.take
327
+ @bstmt.add_batch qstr
328
+ end
329
+
330
+ # Executes batched statements including prepared statements. No effect when no statment is added
331
+ # @return [NilClass]
332
+ def execute_batch
333
+ check_closed
338
334
 
339
335
  if @bstmt
340
336
  measure_exec(:execute_batch) { @bstmt.execute_batch }
@@ -345,12 +341,12 @@ class Connection
345
341
  @pstmts.each do |stmt|
346
342
  measure_exec(:execute_batch) { stmt.execute_batch }
347
343
  end
348
- end
344
+ end
349
345
 
350
- # Clears the batched statements including prepared statements.
351
- # @return [NilClass]
352
- def clear_batch
353
- check_closed
346
+ # Clears the batched statements including prepared statements.
347
+ # @return [NilClass]
348
+ def clear_batch
349
+ check_closed
354
350
 
355
351
  if @bstmt
356
352
  @bstmt.clear_batch
@@ -361,164 +357,164 @@ class Connection
361
357
  @pstmts.each do |stmt|
362
358
  measure_exec(:execute_batch) { stmt.clear_batch }
363
359
  end
364
- end
365
-
366
- # Gives the JDBC driver a hint of the number of rows to fetch from the database by a single interaction.
367
- # This is only a hint. It may have no effect at all.
368
- # @param [Fixnum] fsz
369
- # @return [NilClass]
370
- def set_fetch_size(fsz)
371
- check_closed
372
-
373
- @fetch_size = fsz
374
- @spool.each { | stmt | stmt.set_fetch_size @fetch_size }
375
- end
376
- alias fetch_size= set_fetch_size
377
-
378
- # Returns the fetch size of the connection. If not set, nil is returned.
379
- # @return [Fixnum]
380
- attr_reader :fetch_size
381
-
382
- # Closes the connection
383
- # @return [NilClass]
384
- def close
385
- return if closed?
386
- @spool.close
387
- @conn.close
388
- @conn = @spool = nil
389
- end
390
-
391
- # Returns if this connection is closed or not
392
- # @return [Boolean]
393
- def closed?
394
- @conn.nil?
395
- end
396
-
397
- # Returns a table wrapper for the given table name
398
- # @since 0.2.0
399
- # @param [String/Symbol] table_name Name of the table to be wrapped
400
- # @return [JDBCHelper::TableWrapper]
401
- def table table_name
360
+ end
361
+
362
+ # Gives the JDBC driver a hint of the number of rows to fetch from the database by a single interaction.
363
+ # This is only a hint. It may have no effect at all.
364
+ # @param [Fixnum] fsz
365
+ # @return [NilClass]
366
+ def set_fetch_size(fsz)
367
+ check_closed
368
+
369
+ @fetch_size = fsz
370
+ @spool.each { | stmt | stmt.set_fetch_size @fetch_size }
371
+ end
372
+ alias fetch_size= set_fetch_size
373
+
374
+ # Returns the fetch size of the connection. If not set, nil is returned.
375
+ # @return [Fixnum]
376
+ attr_reader :fetch_size
377
+
378
+ # Closes the connection
379
+ # @return [NilClass]
380
+ def close
381
+ return if closed?
382
+ @spool.close
383
+ @conn.close
384
+ @conn = @spool = nil
385
+ end
386
+
387
+ # Returns if this connection is closed or not
388
+ # @return [Boolean]
389
+ def closed?
390
+ @conn.nil?
391
+ end
392
+
393
+ # Returns a table wrapper for the given table name
394
+ # @since 0.2.0
395
+ # @param [String/Symbol] table_name Name of the table to be wrapped
396
+ # @return [JDBCHelper::TableWrapper]
397
+ def table table_name
402
398
  @table_wrappers[table_name] ||= JDBCHelper::TableWrapper.new self, table_name
403
- end
404
-
405
- # Returns a sequence wrapper for the given name
406
- # @since 0.4.2
407
- # @param [String/Symbol] sequence_name Name of the sequence to be wrapped
408
- # @return [JDBCHelper::SequenceWrapper]
409
- def sequence sequence_name
410
- JDBCHelper::SequenceWrapper.new self, sequence_name
411
- end
412
-
413
- # Returns a function wrapper for the given function name
414
- # @since 0.2.2
415
- # @param [String/Symbol] func_name Name of the function to be wrapped
416
- # @return [JDBCHelper::FunctionWrapper]
417
- def function func_name
418
- JDBCHelper::FunctionWrapper.new self, func_name
419
- end
420
-
421
- # Returns a procedure wrapper for the given procedure name
422
- # @since 0.3.0
423
- # @param [String/Symbol] proc_name Name of the procedure to be wrapped
424
- # @return [JDBCHelper::ProcedureWrapper]
425
- def procedure proc_name
426
- JDBCHelper::ProcedureWrapper.new self, proc_name
427
- end
428
-
429
- # Statistics
430
- class Stat
431
- attr_accessor :type, :elapsed, :success_count, :fail_count
432
-
433
- def initialize(t, e, s, f)
434
- self.type = t
435
- self.elapsed = e
436
- self.success_count = s
437
- self.fail_count = f
438
- end
439
- end
399
+ end
400
+
401
+ # Returns a sequence wrapper for the given name
402
+ # @since 0.4.2
403
+ # @param [String/Symbol] sequence_name Name of the sequence to be wrapped
404
+ # @return [JDBCHelper::SequenceWrapper]
405
+ def sequence sequence_name
406
+ JDBCHelper::SequenceWrapper.new self, sequence_name
407
+ end
408
+
409
+ # Returns a function wrapper for the given function name
410
+ # @since 0.2.2
411
+ # @param [String/Symbol] func_name Name of the function to be wrapped
412
+ # @return [JDBCHelper::FunctionWrapper]
413
+ def function func_name
414
+ JDBCHelper::FunctionWrapper.new self, func_name
415
+ end
416
+
417
+ # Returns a procedure wrapper for the given procedure name
418
+ # @since 0.3.0
419
+ # @param [String/Symbol] proc_name Name of the procedure to be wrapped
420
+ # @return [JDBCHelper::ProcedureWrapper]
421
+ def procedure proc_name
422
+ JDBCHelper::ProcedureWrapper.new self, proc_name
423
+ end
424
+
425
+ # Statistics
426
+ class Stat
427
+ attr_accessor :type, :elapsed, :success_count, :fail_count
428
+
429
+ def initialize(t, e, s, f)
430
+ self.type = t
431
+ self.elapsed = e
432
+ self.success_count = s
433
+ self.fail_count = f
434
+ end
435
+ end
440
436
 
441
437
  private
442
- # Transaction object passed to the code block given to transaction method
443
- class Transaction
444
- # Commits the transaction
445
- # @raise [JDBCHelper::Transaction::Commit]
446
- def commit
447
- @conn.commit
448
- raise Commit
449
- end
450
- # Rolls back this transaction
451
- # @raise [JDBCHelper::Transaction::Rollback]
452
- def rollback
453
- @conn.rollback
454
- raise Rollback
455
- end
456
- private
457
- def initialize(conn) # :nodoc:
458
- @conn = conn
459
- end
460
- class Commit < Exception # :nodoc:
461
- end
462
- class Rollback < Exception # :nodoc:
463
- end
464
- end
465
-
466
- def create_statement # :nodoc:
467
- stmt = @conn.create_statement
468
- stmt.set_fetch_size @fetch_size if @fetch_size
469
- stmt
470
- end
471
-
472
- def process_and_close_rset(rset) # :nodoc:
473
- enum = ResultSetEnumerator.send :new, rset
474
- rows = []
475
-
476
- begin
477
- enum.each do | row |
478
- if block_given?
479
- yield row
480
- else
481
- rows << row
482
- end
483
- end
484
- block_given? ? nil : rows
485
- ensure
486
- enum.close
487
- end
488
- end
438
+ # Transaction object passed to the code block given to transaction method
439
+ class Transaction
440
+ # Commits the transaction
441
+ # @raise [JDBCHelper::Transaction::Commit]
442
+ def commit
443
+ @conn.commit
444
+ raise Commit
445
+ end
446
+ # Rolls back this transaction
447
+ # @raise [JDBCHelper::Transaction::Rollback]
448
+ def rollback
449
+ @conn.rollback
450
+ raise Rollback
451
+ end
452
+ private
453
+ def initialize(conn) # :nodoc:
454
+ @conn = conn
455
+ end
456
+ class Commit < Exception # :nodoc:
457
+ end
458
+ class Rollback < Exception # :nodoc:
459
+ end
460
+ end
461
+
462
+ def create_statement # :nodoc:
463
+ stmt = @conn.create_statement
464
+ stmt.set_fetch_size @fetch_size if @fetch_size
465
+ stmt
466
+ end
467
+
468
+ def process_and_close_rset(rset) # :nodoc:
469
+ enum = ResultSetEnumerator.send :new, rset
470
+ rows = []
471
+
472
+ begin
473
+ enum.each do | row |
474
+ if block_given?
475
+ yield row
476
+ else
477
+ rows << row
478
+ end
479
+ end
480
+ block_given? ? nil : rows
481
+ ensure
482
+ enum.close
483
+ end
484
+ end
489
485
 
490
486
  def close_pstmt pstmt
491
487
  @pstmts.delete pstmt
492
488
  end
493
489
 
494
- def update_stat(type, elapsed, success_count, fail_count) # :nodoc:
495
- @prev_stat.type = type
496
- @prev_stat.elapsed = elapsed
497
- @prev_stat.success_count = success_count
498
- @prev_stat.fail_count = fail_count
499
-
500
- accum = @stats[type]
501
- accum.elapsed += elapsed
502
- accum.success_count += success_count
503
- accum.fail_count += fail_count
504
- end
505
-
506
- def measure_exec(type)
507
- begin
508
- st = Time.now
509
- ret = yield
510
- elapsed = Time.now - st
511
- update_stat(type, elapsed, 1, 0)
512
- rescue Exception
513
- update_stat(type, 0, 0, 1)
514
- raise
515
- end
516
- ret
517
- end
518
-
519
- def check_closed
520
- raise RuntimeError.new('Connection already closed') if closed?
521
- end
490
+ def update_stat(type, elapsed, success_count, fail_count) # :nodoc:
491
+ @prev_stat.type = type
492
+ @prev_stat.elapsed = elapsed
493
+ @prev_stat.success_count = success_count
494
+ @prev_stat.fail_count = fail_count
495
+
496
+ accum = @stats[type]
497
+ accum.elapsed += elapsed
498
+ accum.success_count += success_count
499
+ accum.fail_count += fail_count
500
+ end
501
+
502
+ def measure_exec(type)
503
+ begin
504
+ st = Time.now
505
+ ret = yield
506
+ elapsed = Time.now - st
507
+ update_stat(type, elapsed, 1, 0)
508
+ rescue Exception
509
+ update_stat(type, 0, 0, 1)
510
+ raise
511
+ end
512
+ ret
513
+ end
514
+
515
+ def check_closed
516
+ raise RuntimeError.new('Connection already closed') if closed?
517
+ end
522
518
  end#Connection
523
519
  end#JDBCHelper
524
520