jdbc-helper 0.4.10 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/jdbc-helper.rb +1 -0
- data/lib/jdbc-helper/connection.rb +44 -13
- data/lib/jdbc-helper/connection/prepared_statement.rb +7 -0
- data/lib/jdbc-helper/connection/statement_pool.rb +1 -1
- data/lib/jdbc-helper/sql.rb +17 -3
- data/lib/jdbc-helper/sql_prepared.rb +159 -0
- data/lib/jdbc-helper/wrapper/table_wrapper.rb +58 -9
- data/test/helper.rb +1 -0
- data/test/test_connection.rb +62 -17
- data/test/test_object_wrapper.rb +50 -3
- data/test/test_performance.rb +15 -3
- data/test/test_sql.rb +51 -1
- metadata +17 -3
data/lib/jdbc-helper.rb
CHANGED
@@ -144,7 +144,8 @@ class Connection
|
|
144
144
|
# @param [Hash] args
|
145
145
|
def initialize(args = {})
|
146
146
|
# Subsequent deletes should not affect the input
|
147
|
-
|
147
|
+
@args = args.dup
|
148
|
+
args = @args.dup
|
148
149
|
|
149
150
|
# String/Symbol
|
150
151
|
%w[driver url user password timeout].each do | strk |
|
@@ -179,6 +180,8 @@ class Connection
|
|
179
180
|
@stats = Hash.new { | h, k | h[k] = Stat.new(k, 0, 0, 0) }
|
180
181
|
@prev_stat = Stat.new(nil, 0, 0, 0)
|
181
182
|
|
183
|
+
@pstmts = []
|
184
|
+
|
182
185
|
if block_given?
|
183
186
|
begin
|
184
187
|
yield self
|
@@ -188,15 +191,30 @@ class Connection
|
|
188
191
|
end
|
189
192
|
end
|
190
193
|
|
194
|
+
# Creates another connection with the same parameters as this Connection.
|
195
|
+
# @return [JDBCHelper::Connection]
|
196
|
+
def clone
|
197
|
+
nc = JDBCHelper::Connection.new @args
|
198
|
+
nc.fetch_size = @fetch_size if @fetch_size
|
199
|
+
nc
|
200
|
+
end
|
201
|
+
|
191
202
|
# Creates a prepared statement, which is also an encapsulation of Java PreparedStatement object
|
192
203
|
# @param [String] qstr SQL string
|
193
204
|
def prepare(qstr)
|
194
205
|
check_closed
|
195
206
|
|
196
|
-
PreparedStatement.send(:new, self, qstr,
|
207
|
+
pstmt = PreparedStatement.send(:new, self, qstr,
|
197
208
|
measure_exec(:prepare) { @conn.prepare_statement(qstr) })
|
209
|
+
@pstmts << pstmt
|
210
|
+
pstmt
|
198
211
|
end
|
199
212
|
|
213
|
+
# @return [Array] Prepared statements currently opened for this connection
|
214
|
+
def prepared_statements
|
215
|
+
@pstmts
|
216
|
+
end
|
217
|
+
|
200
218
|
# Creates a callable statement.
|
201
219
|
# @param [String] qstr SQL string
|
202
220
|
def prepare_call(qstr)
|
@@ -311,27 +329,36 @@ class Connection
|
|
311
329
|
@bstmt.add_batch qstr
|
312
330
|
end
|
313
331
|
|
314
|
-
# Executes batched statements. No effect when no statment is added
|
332
|
+
# Executes batched statements including prepared statements. No effect when no statment is added
|
315
333
|
# @return [NilClass]
|
316
334
|
def execute_batch
|
317
335
|
check_closed
|
318
336
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
337
|
+
if @bstmt
|
338
|
+
measure_exec(:execute_batch) { @bstmt.execute_batch }
|
339
|
+
@spool.give @bstmt
|
340
|
+
@bstmt = nil
|
341
|
+
end
|
342
|
+
|
343
|
+
@pstmts.each do |stmt|
|
344
|
+
measure_exec(:execute_batch) { stmt.execute_batch }
|
345
|
+
end
|
324
346
|
end
|
325
347
|
|
326
|
-
# Clears the batched statements
|
348
|
+
# Clears the batched statements including prepared statements.
|
327
349
|
# @return [NilClass]
|
328
350
|
def clear_batch
|
329
351
|
check_closed
|
330
352
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
353
|
+
if @bstmt
|
354
|
+
@bstmt.clear_batch
|
355
|
+
@spool.give @bstmt
|
356
|
+
@bstmt = nil
|
357
|
+
end
|
358
|
+
|
359
|
+
@pstmts.each do |stmt|
|
360
|
+
measure_exec(:execute_batch) { stmt.clear_batch }
|
361
|
+
end
|
335
362
|
end
|
336
363
|
|
337
364
|
# Gives the JDBC driver a hint of the number of rows to fetch from the database by a single interaction.
|
@@ -458,6 +485,10 @@ private
|
|
458
485
|
end
|
459
486
|
end
|
460
487
|
|
488
|
+
def close_pstmt pstmt
|
489
|
+
@pstmts.delete pstmt
|
490
|
+
end
|
491
|
+
|
461
492
|
def update_stat(type, elapsed, success_count, fail_count) # :nodoc:
|
462
493
|
@prev_stat.type = type
|
463
494
|
@prev_stat.elapsed = elapsed
|
@@ -19,6 +19,13 @@ class PreparedStatement < ParameterizedStatement
|
|
19
19
|
@pmd.get_parameter_count
|
20
20
|
end
|
21
21
|
|
22
|
+
# @return [NilClass]
|
23
|
+
def close
|
24
|
+
@conn.send(:close_pstmt, self)
|
25
|
+
@java_obj.close
|
26
|
+
@java_obj = nil
|
27
|
+
end
|
28
|
+
|
22
29
|
# @return [Fixnum]
|
23
30
|
def update(*params)
|
24
31
|
check_closed
|
@@ -24,7 +24,7 @@ class StatementPool
|
|
24
24
|
|
25
25
|
def take
|
26
26
|
if @free.empty?
|
27
|
-
raise Exception.new("
|
27
|
+
raise Exception.new("Statement nesting level is too deep (likely a bug)") if
|
28
28
|
@occupied.length >= @max_size
|
29
29
|
@occupied << nstmt = @conn.send(:create_statement)
|
30
30
|
nstmt
|
data/lib/jdbc-helper/sql.rb
CHANGED
@@ -6,9 +6,12 @@ module JDBCHelper
|
|
6
6
|
# Generate SQL snippet, prevents the string from being quoted.
|
7
7
|
# @param [String] SQL snippet
|
8
8
|
# @return [JDBCHelper::SQL]
|
9
|
-
def self.
|
9
|
+
def self.sql str
|
10
10
|
JDBCHelper::SQL.new str
|
11
11
|
end
|
12
|
+
class << self
|
13
|
+
alias_method :SQL, :sql
|
14
|
+
end
|
12
15
|
|
13
16
|
# Class representing an SQL snippet. Also has many SQL generator class methods.
|
14
17
|
class SQL
|
@@ -44,6 +47,9 @@ class SQL
|
|
44
47
|
where_clause.empty? ? where_clause : check(where_clause)
|
45
48
|
end
|
46
49
|
|
50
|
+
def self.where_prepared *conds
|
51
|
+
end
|
52
|
+
|
47
53
|
# Generates SQL order by cluase with the given conditions.
|
48
54
|
def self.order *criteria
|
49
55
|
str = criteria.map(&:to_s).reject(&:empty?).join(', ')
|
@@ -120,7 +126,15 @@ class SQL
|
|
120
126
|
@expr
|
121
127
|
end
|
122
128
|
|
123
|
-
|
129
|
+
def == other
|
130
|
+
self.to_s == other.to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
def eql? other
|
134
|
+
self.class == other.class && self.to_s == other.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
protected
|
124
138
|
def self.esc str
|
125
139
|
str.gsub("'", "''")
|
126
140
|
end
|
@@ -177,7 +191,7 @@ private
|
|
177
191
|
def initialize str
|
178
192
|
@expr = JDBCHelper::SQL.check str
|
179
193
|
end
|
180
|
-
|
194
|
+
|
181
195
|
# Class to represent "(IS) NOT NULL" expression in SQL
|
182
196
|
class NotNilClass
|
183
197
|
# Returns the singleton object of NotNilClass
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# Junegunn Choi (junegunn.c@gmail.com)
|
3
|
+
|
4
|
+
module JDBCHelper
|
5
|
+
# SQL generator class methods for prepared operations.
|
6
|
+
# WARNING: Does not perform SQL.check to minimize performance overhead
|
7
|
+
class SQLPrepared < JDBCHelper::SQL
|
8
|
+
# Generates SQL where cluase with the given conditions.
|
9
|
+
# Parameter can be either Hash of String.
|
10
|
+
def self.where *conds
|
11
|
+
where_internal conds
|
12
|
+
end
|
13
|
+
|
14
|
+
# SQL Helpers
|
15
|
+
# ===========
|
16
|
+
# Generates insert SQL with hash
|
17
|
+
def self.insert table, data_hash
|
18
|
+
insert_internal 'insert', table, data_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generates insert ignore SQL (Non-standard syntax)
|
22
|
+
def self.insert_ignore table, data_hash
|
23
|
+
insert_internal 'insert ignore', table, data_hash
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generates replace SQL (Non-standard syntax)
|
27
|
+
def self.replace table, data_hash
|
28
|
+
insert_internal 'replace', table, data_hash
|
29
|
+
end
|
30
|
+
|
31
|
+
# Generates update SQL with hash.
|
32
|
+
# :where element of the given hash is taken out to generate where clause.
|
33
|
+
def self.update table, data_hash, where
|
34
|
+
where_clause, where_binds = where_internal where
|
35
|
+
|
36
|
+
col_binds = []
|
37
|
+
sql = ("update #{table} set " + data_hash.map { |k, v|
|
38
|
+
case v
|
39
|
+
when JDBCHelper::SQL
|
40
|
+
"#{k} = #{v}"
|
41
|
+
else
|
42
|
+
col_binds << v
|
43
|
+
"#{k} = ?"
|
44
|
+
end
|
45
|
+
}.join(', ') + " #{where_clause}").strip
|
46
|
+
|
47
|
+
return sql, col_binds + where_binds
|
48
|
+
end
|
49
|
+
|
50
|
+
# Generates select SQL with the given conditions
|
51
|
+
def self.select table, opts = {}
|
52
|
+
opts = opts.reject { |k, v| v.nil? }
|
53
|
+
w_c, w_b = where_internal(opts.fetch(:where, {}))
|
54
|
+
sql = [
|
55
|
+
"select #{opts.fetch(:select, ['*']).join(', ')} from #{table}",
|
56
|
+
w_c.to_s,
|
57
|
+
order(opts.fetch(:order, []).join(', '))
|
58
|
+
].reject(&:empty?).join(' ')
|
59
|
+
|
60
|
+
return sql, w_b
|
61
|
+
end
|
62
|
+
|
63
|
+
# Generates count SQL with the given conditions
|
64
|
+
def self.count table, conds = nil
|
65
|
+
w_c, w_b = where_internal(conds)
|
66
|
+
sql = "select count(*) from #{table} #{w_c}".strip
|
67
|
+
|
68
|
+
return sql, w_b
|
69
|
+
end
|
70
|
+
|
71
|
+
# Generates delete SQL with the given conditions
|
72
|
+
def self.delete table, conds = nil
|
73
|
+
w_c, w_b = where_internal(conds)
|
74
|
+
sql = "delete from #{table} #{w_c}".strip
|
75
|
+
return sql, w_b
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def self.where_internal conds
|
80
|
+
conds = [conds] unless conds.is_a? Array
|
81
|
+
|
82
|
+
binds = []
|
83
|
+
clauses = []
|
84
|
+
conds.compact.each do |cond|
|
85
|
+
c, b = where_unit cond
|
86
|
+
next if c.empty?
|
87
|
+
|
88
|
+
binds += b
|
89
|
+
clauses << c
|
90
|
+
end
|
91
|
+
where_clause = clauses.join(' and ')
|
92
|
+
if where_clause.empty?
|
93
|
+
return nil, []
|
94
|
+
else
|
95
|
+
where_clause = 'where ' + where_clause
|
96
|
+
return where_clause, binds
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.where_unit conds
|
101
|
+
binds = []
|
102
|
+
|
103
|
+
clause = case conds
|
104
|
+
when String
|
105
|
+
conds = conds.strip
|
106
|
+
conds.empty? ? '' : "(#{conds})"
|
107
|
+
when Hash
|
108
|
+
conds.map { |k, v|
|
109
|
+
"#{k} " +
|
110
|
+
case v
|
111
|
+
when NilClass
|
112
|
+
"is null"
|
113
|
+
when NotNilClass
|
114
|
+
"is not null"
|
115
|
+
when JDBCHelper::SQL
|
116
|
+
"= #{v}"
|
117
|
+
when Fixnum, Bignum, Float
|
118
|
+
binds << v
|
119
|
+
"= ?"
|
120
|
+
when Range
|
121
|
+
binds << v.begin << v.end
|
122
|
+
">= ? and #{k} <#{'=' unless v.exclude_end?} ?"
|
123
|
+
when Array
|
124
|
+
"in (" +
|
125
|
+
v.map { |e|
|
126
|
+
case e
|
127
|
+
when String
|
128
|
+
"'#{esc e}'"
|
129
|
+
else
|
130
|
+
e.to_s
|
131
|
+
end }.join(', ') + ")"
|
132
|
+
else
|
133
|
+
binds << v
|
134
|
+
"= ?"
|
135
|
+
end
|
136
|
+
}.join(' and ')
|
137
|
+
else
|
138
|
+
raise NotImplementedError.new("Parameter to where must be either Hash or String")
|
139
|
+
end
|
140
|
+
return clause, binds
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.insert_internal cmd, table, data_hash
|
144
|
+
binds = []
|
145
|
+
values = data_hash.values.map { |v|
|
146
|
+
case v
|
147
|
+
when JDBCHelper::SQL
|
148
|
+
v
|
149
|
+
else
|
150
|
+
binds << v
|
151
|
+
'?'
|
152
|
+
end
|
153
|
+
}
|
154
|
+
sql = "#{cmd} into #{table} (#{data_hash.keys.join ', '}) values (#{values.join(', ')})"
|
155
|
+
return sql, binds
|
156
|
+
end
|
157
|
+
end#SQL
|
158
|
+
end#JDBCHelper
|
159
|
+
|
@@ -50,7 +50,9 @@ class TableWrapper < ObjectWrapper
|
|
50
50
|
# @param [List of Hash/String] where Filter conditions
|
51
51
|
# @return [Fixnum] Count of the records.
|
52
52
|
def count *where
|
53
|
-
|
53
|
+
sql, binds = JDBCHelper::SQLPrepared.count(name, @query_where + where)
|
54
|
+
pstmt = prepare :count, sql
|
55
|
+
pstmt.query(*binds)[0][0].to_i
|
54
56
|
end
|
55
57
|
|
56
58
|
# Sees if the table is empty
|
@@ -64,7 +66,9 @@ class TableWrapper < ObjectWrapper
|
|
64
66
|
# @param [Hash] data_hash Column values in Hash
|
65
67
|
# @return [Fixnum] Number of affected records
|
66
68
|
def insert data_hash = {}
|
67
|
-
|
69
|
+
sql, binds = JDBCHelper::SQLPrepared.insert(name, @query_default.merge(data_hash))
|
70
|
+
pstmt = prepare :insert, sql
|
71
|
+
pstmt.send @update_method, *binds
|
68
72
|
end
|
69
73
|
|
70
74
|
# Inserts a record into the table with the given hash.
|
@@ -73,7 +77,9 @@ class TableWrapper < ObjectWrapper
|
|
73
77
|
# @param [Hash] data_hash Column values in Hash
|
74
78
|
# @return [Fixnum] Number of affected records
|
75
79
|
def insert_ignore data_hash = {}
|
76
|
-
|
80
|
+
sql, binds = JDBCHelper::SQLPrepared.insert_ignore(name, @query_default.merge(data_hash))
|
81
|
+
pstmt = prepare :insert, sql
|
82
|
+
pstmt.send @update_method, *binds
|
77
83
|
end
|
78
84
|
|
79
85
|
# Replaces a record in the table with the new one with the same unique key.
|
@@ -81,7 +87,9 @@ class TableWrapper < ObjectWrapper
|
|
81
87
|
# @param [Hash] data_hash Column values in Hash
|
82
88
|
# @return [Fixnum] Number of affected records
|
83
89
|
def replace data_hash = {}
|
84
|
-
|
90
|
+
sql, binds = JDBCHelper::SQLPrepared.replace(name, @query_default.merge(data_hash))
|
91
|
+
pstmt = prepare :insert, sql
|
92
|
+
pstmt.send @update_method, *binds
|
85
93
|
end
|
86
94
|
|
87
95
|
# Executes update with the given hash.
|
@@ -92,15 +100,20 @@ class TableWrapper < ObjectWrapper
|
|
92
100
|
def update data_hash_with_where = {}
|
93
101
|
where_ext = data_hash_with_where.delete(:where)
|
94
102
|
where_ext = [where_ext] unless where_ext.is_a? Array
|
95
|
-
|
96
|
-
|
103
|
+
sql, binds = JDBCHelper::SQLPrepared.update(name,
|
104
|
+
@query_default.merge(data_hash_with_where),
|
105
|
+
@query_where + where_ext.compact)
|
106
|
+
pstmt = prepare :update, sql
|
107
|
+
pstmt.send @update_method, *binds
|
97
108
|
end
|
98
109
|
|
99
110
|
# Deletes records matching given condtion
|
100
111
|
# @param [List of Hash/String] where Delete filters
|
101
112
|
# @return [Fixnum] Number of affected records
|
102
113
|
def delete *where
|
103
|
-
|
114
|
+
sql, binds = JDBCHelper::SQLPrepared.delete(name, @query_where + where)
|
115
|
+
pstmt = prepare :delete, sql
|
116
|
+
pstmt.send @update_method, *binds
|
104
117
|
end
|
105
118
|
|
106
119
|
# Empties the table.
|
@@ -181,19 +194,26 @@ class TableWrapper < ObjectWrapper
|
|
181
194
|
# @return [JDBCHelper::Connection::ResultSetEnumerator]
|
182
195
|
# @since 0.4.0
|
183
196
|
def each &block
|
184
|
-
|
197
|
+
sql, binds = JDBCHelper::SQLPrepared.select(
|
198
|
+
name,
|
199
|
+
:select => @query_select,
|
200
|
+
:where => @query_where,
|
201
|
+
:order => @query_order)
|
202
|
+
pstmt = prepare :select, sql
|
203
|
+
pstmt.enumerate *binds, &block
|
185
204
|
end
|
186
205
|
|
187
206
|
# Returns a new TableWrapper object whose subsequent inserts, updates,
|
188
207
|
# and deletes are added to batch for JDBC batch-execution. The actual execution
|
189
208
|
# is deferred until JDBCHelper::Connection#execute_batch method is called.
|
190
209
|
# Self is returned when batch is called more than once.
|
191
|
-
# @return [JDBCHelper::
|
210
|
+
# @return [JDBCHelper::TableWrapper]
|
192
211
|
# @since 0.4.0
|
193
212
|
def batch
|
194
213
|
if batch?
|
195
214
|
self
|
196
215
|
else
|
216
|
+
# dup makes @pstmts to be shared
|
197
217
|
obj = self.dup
|
198
218
|
obj.instance_variable_set :@update_method, :add_batch
|
199
219
|
obj
|
@@ -223,8 +243,37 @@ class TableWrapper < ObjectWrapper
|
|
223
243
|
@update_method = :update
|
224
244
|
@query_default = {}
|
225
245
|
@query_where = []
|
246
|
+
@pstmts = {
|
247
|
+
:select => {},
|
248
|
+
:insert => {},
|
249
|
+
:delete => {},
|
250
|
+
:count => {},
|
251
|
+
:update => {}
|
252
|
+
}
|
226
253
|
end
|
254
|
+
|
255
|
+
# Closes the prepared statements
|
256
|
+
# @since 0.5.0
|
257
|
+
def close
|
258
|
+
@pstmts.each do |typ, hash|
|
259
|
+
hash.each do |sql, pstmt|
|
260
|
+
pstmt.close if pstmt
|
261
|
+
end
|
262
|
+
@pstmts[typ] = {}
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# @return [Hash] Prepared statements for this wrapper
|
267
|
+
# @since 0.5.0
|
268
|
+
def prepared_statements
|
269
|
+
@pstmts
|
270
|
+
end
|
271
|
+
|
227
272
|
private
|
273
|
+
def prepare type, sql
|
274
|
+
@pstmts[type][sql] ||= @connection.prepare(JDBCHelper::SQL.check(sql))
|
275
|
+
end
|
276
|
+
|
228
277
|
def ret obj, &block
|
229
278
|
if block_given?
|
230
279
|
obj.each &block
|
data/test/helper.rb
CHANGED
data/test/test_connection.rb
CHANGED
@@ -68,7 +68,7 @@ class TestConnection < Test::Unit::TestCase
|
|
68
68
|
|
69
69
|
# ---------------------------------------------------------------
|
70
70
|
|
71
|
-
def
|
71
|
+
def test_connect_clone_and_close
|
72
72
|
config.each do | db, conn_info_org |
|
73
73
|
4.times do | i |
|
74
74
|
conn_info = conn_info_org.reject { |k,v| k == 'database' }
|
@@ -89,6 +89,9 @@ class TestConnection < Test::Unit::TestCase
|
|
89
89
|
assert_equal(conn.driver, conn_info[:driver] || conn_info['driver'])
|
90
90
|
assert_equal(conn.url, conn_info[:url] || conn_info['url'])
|
91
91
|
|
92
|
+
conn.fetch_size = 100
|
93
|
+
assert_equal 100, conn.fetch_size
|
94
|
+
|
92
95
|
conn.close
|
93
96
|
assert_equal(conn.closed?, true)
|
94
97
|
[ :query, :update, :add_batch, :prepare ].each do | met |
|
@@ -98,10 +101,23 @@ class TestConnection < Test::Unit::TestCase
|
|
98
101
|
assert_raise(RuntimeError) { conn.send met }
|
99
102
|
end
|
100
103
|
|
104
|
+
new_conn = conn.clone
|
105
|
+
assert new_conn.java_obj != conn.java_obj
|
106
|
+
assert new_conn.closed? == false
|
107
|
+
assert_equal 100, new_conn.fetch_size
|
108
|
+
new_conn.close
|
109
|
+
|
101
110
|
# initialize with execution block
|
102
111
|
conn = JDBCHelper::Connection.new(conn_info) do | c |
|
112
|
+
c2 = c.clone
|
113
|
+
assert c2.java_obj != c.java_obj
|
114
|
+
|
103
115
|
c.query('select 1 from dual')
|
116
|
+
c2.query('select 1 from dual')
|
104
117
|
assert_equal c.closed?, false
|
118
|
+
|
119
|
+
c2.close
|
120
|
+
assert c2.closed?
|
105
121
|
end
|
106
122
|
assert conn.closed?
|
107
123
|
end
|
@@ -155,6 +171,8 @@ class TestConnection < Test::Unit::TestCase
|
|
155
171
|
iq = lambda do | i |
|
156
172
|
"insert into #{TEST_TABLE} values (#{i}, 'A')"
|
157
173
|
end
|
174
|
+
ins1 = conn.prepare "insert into #{TEST_TABLE} values (? + #{count}, 'B')"
|
175
|
+
ins2 = conn.prepare "insert into #{TEST_TABLE} values (? + #{count * 2}, 'C')"
|
158
176
|
|
159
177
|
# update
|
160
178
|
assert_equal 1, conn.update(iq.call 0)
|
@@ -165,17 +183,24 @@ class TestConnection < Test::Unit::TestCase
|
|
165
183
|
|
166
184
|
count.times do | p |
|
167
185
|
conn.add_batch iq.call(p)
|
186
|
+
ins1.add_batch p
|
187
|
+
ins2.add_batch p
|
168
188
|
end
|
169
189
|
conn.execute_batch
|
170
|
-
assert_equal count, conn.query("select count(*) from #{TEST_TABLE}")[0][0]
|
190
|
+
assert_equal count * 3, conn.query("select count(*) from #{TEST_TABLE}")[0][0]
|
171
191
|
|
172
192
|
# add_batch clear_batch
|
173
193
|
reset_test_table conn
|
174
194
|
|
175
195
|
count.times do | p |
|
176
196
|
conn.add_batch iq.call(p)
|
197
|
+
ins1.add_batch p
|
198
|
+
ins2.add_batch p
|
177
199
|
end
|
178
200
|
conn.clear_batch
|
201
|
+
# Already cleared, no effect
|
202
|
+
ins1.execute_batch
|
203
|
+
ins2.execute_batch
|
179
204
|
assert_equal 0, conn.query("select count(*) from #{TEST_TABLE}")[0][0]
|
180
205
|
end
|
181
206
|
end
|
@@ -184,6 +209,7 @@ class TestConnection < Test::Unit::TestCase
|
|
184
209
|
each_connection do | conn |
|
185
210
|
sel = conn.prepare get_one_two
|
186
211
|
assert sel.closed? == false
|
212
|
+
assert_equal conn.prepared_statements.first, sel
|
187
213
|
|
188
214
|
# Fetch size
|
189
215
|
assert_nil conn.fetch_size
|
@@ -232,6 +258,7 @@ class TestConnection < Test::Unit::TestCase
|
|
232
258
|
reset_test_table conn
|
233
259
|
ins = conn.prepare "insert into #{TEST_TABLE} values (?, ?)"
|
234
260
|
assert_equal 2, ins.parameter_count
|
261
|
+
assert_equal conn.prepared_statements.first, ins
|
235
262
|
|
236
263
|
count = 100
|
237
264
|
|
@@ -240,31 +267,49 @@ class TestConnection < Test::Unit::TestCase
|
|
240
267
|
assert_equal 1, ins.update(0, 'A')
|
241
268
|
assert_equal 1, conn.prev_stat.success_count
|
242
269
|
ins.close
|
270
|
+
assert_equal 0, conn.prepared_statements.length
|
243
271
|
|
244
272
|
# add_batch execute_batch
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
273
|
+
2.times do |iter|
|
274
|
+
reset_test_table conn
|
275
|
+
ins = conn.prepare "insert into #{TEST_TABLE} values (?, ?)"
|
276
|
+
assert_equal conn.prepared_statements.first, ins
|
277
|
+
|
278
|
+
count.times do | p |
|
279
|
+
ins.add_batch(p + 1, 'A')
|
280
|
+
end
|
281
|
+
if iter == 0
|
282
|
+
ins.execute_batch
|
283
|
+
else
|
284
|
+
conn.execute_batch
|
285
|
+
end
|
286
|
+
assert_equal count, conn.query("select count(*) from #{TEST_TABLE}")[0][0]
|
287
|
+
ins.close
|
288
|
+
assert_equal 0, conn.prepared_statements.length
|
289
|
+
end
|
254
290
|
|
255
291
|
# add_batch clear_batch
|
256
292
|
reset_test_table conn
|
257
293
|
ins = conn.prepare "insert into #{TEST_TABLE} values (?, ?)"
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
294
|
+
assert_equal conn.prepared_statements.first, ins
|
295
|
+
|
296
|
+
# clear_batch
|
297
|
+
2.times do |iter|
|
298
|
+
count.times do | p |
|
299
|
+
ins.add_batch(p + 1, 'A')
|
300
|
+
end
|
301
|
+
if iter == 0
|
302
|
+
ins.clear_batch
|
303
|
+
else
|
304
|
+
conn.clear_batch
|
305
|
+
end
|
306
|
+
assert_equal 0, conn.query("select count(*) from #{TEST_TABLE}")[0][0]
|
307
|
+
end
|
264
308
|
|
265
309
|
# close closed?
|
266
310
|
assert ins.closed? == false
|
267
311
|
ins.close
|
312
|
+
assert_equal 0, conn.prepared_statements.length
|
268
313
|
assert ins.closed?
|
269
314
|
[ :query, :update, :add_batch, :execute_batch, :clear_batch ].each do | met |
|
270
315
|
assert_raise(RuntimeError) { ins.send met }
|
data/test/test_object_wrapper.rb
CHANGED
@@ -363,21 +363,24 @@ class TestObjectWrapper < Test::Unit::TestCase
|
|
363
363
|
# Batch updates
|
364
364
|
table.batch.delete
|
365
365
|
assert_equal 100, table.count
|
366
|
+
conn.execute_batch
|
367
|
+
assert_equal 0, table.count
|
366
368
|
|
367
369
|
insert table.batch, 50
|
368
|
-
assert_equal
|
370
|
+
assert_equal 0, table.count
|
371
|
+
conn.execute_batch
|
372
|
+
assert_equal 50, table.count
|
369
373
|
|
370
374
|
table.batch.update(:alpha => JDBCHelper::SQL('alpha * 2'))
|
371
375
|
assert_equal 100, table.select(:alpha).to_a.first.alpha.to_i
|
372
376
|
|
373
377
|
# Independent update inbetween
|
374
378
|
table.delete(:id => 1..10)
|
375
|
-
assert_equal
|
379
|
+
assert_equal 40, table.count
|
376
380
|
|
377
381
|
# Finally
|
378
382
|
conn.execute_batch
|
379
383
|
|
380
|
-
assert_equal 50, table.count
|
381
384
|
assert_equal 200, table.select(:alpha).to_a.first.alpha.to_i
|
382
385
|
end
|
383
386
|
end
|
@@ -429,5 +432,49 @@ class TestObjectWrapper < Test::Unit::TestCase
|
|
429
432
|
seq.drop!
|
430
433
|
end
|
431
434
|
end
|
435
|
+
|
436
|
+
def test_prepared_statements
|
437
|
+
each_connection do |conn|
|
438
|
+
create_table conn
|
439
|
+
|
440
|
+
# No duplicate preparations
|
441
|
+
t = conn.table(@table_name)
|
442
|
+
t.count(:id => 1)
|
443
|
+
t.count('1 = 0')
|
444
|
+
bt = t.batch
|
445
|
+
|
446
|
+
assert_equal 2, t.prepared_statements[:count].length
|
447
|
+
assert_equal 2, bt.prepared_statements[:count].length
|
448
|
+
|
449
|
+
t.count(:id => 2)
|
450
|
+
t.count('2 = 0')
|
451
|
+
bt.count('3 = 0')
|
452
|
+
assert_equal 4, t.prepared_statements[:count].length
|
453
|
+
assert_equal 4, bt.prepared_statements[:count].length
|
454
|
+
|
455
|
+
t.count(:id => 3)
|
456
|
+
t.batch.count('4 = 0')
|
457
|
+
assert_equal 5, t.prepared_statements[:count].length
|
458
|
+
assert_equal 5, bt.prepared_statements[:count].length
|
459
|
+
assert_equal 5, t.batch.prepared_statements[:count].length
|
460
|
+
assert_equal 5, bt.batch.prepared_statements[:count].length
|
461
|
+
|
462
|
+
t.close
|
463
|
+
assert_equal 0, t.prepared_statements[:count].length
|
464
|
+
assert_equal 0, bt.prepared_statements[:count].length
|
465
|
+
assert_equal 0, t.batch.prepared_statements[:count].length
|
466
|
+
assert_equal 0, bt.batch.prepared_statements[:count].length
|
467
|
+
|
468
|
+
t.batch.batch.batch.count(:id => 1)
|
469
|
+
assert_equal 1, t.prepared_statements[:count].length
|
470
|
+
assert_equal 1, bt.prepared_statements[:count].length
|
471
|
+
assert_equal 1, bt.batch.prepared_statements[:count].length
|
472
|
+
assert_equal 1, t.batch.where('1 = 2').select(:a, :b).prepared_statements[:count].length
|
473
|
+
|
474
|
+
# Should be OK
|
475
|
+
bt.close
|
476
|
+
t.batch.close
|
477
|
+
end
|
478
|
+
end
|
432
479
|
end
|
433
480
|
|
data/test/test_performance.rb
CHANGED
@@ -7,7 +7,7 @@ class TestPerformance < Test::Unit::TestCase
|
|
7
7
|
def setup
|
8
8
|
@table = 'tmp_jdbc_helper'
|
9
9
|
@range = 'aaa'..'aaz'
|
10
|
-
@count =
|
10
|
+
@count = 10000 # Increase this for performance measurement
|
11
11
|
end
|
12
12
|
|
13
13
|
def teardown
|
@@ -26,6 +26,17 @@ class TestPerformance < Test::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
}.real}"
|
28
28
|
|
29
|
+
puts "Normal inserts (batch & chuck-transactional): #{Benchmark.measure {
|
30
|
+
(0...@count).each_slice(50) do |slice|
|
31
|
+
conn.transaction do
|
32
|
+
slice.each do |i|
|
33
|
+
conn.add_batch "insert into #{@table} values (#{@range.map{rand @count}.join ','})"
|
34
|
+
end
|
35
|
+
conn.execute_batch
|
36
|
+
end
|
37
|
+
end
|
38
|
+
}.real}"
|
39
|
+
|
29
40
|
puts "Prepared inserts: #{Benchmark.measure {
|
30
41
|
pins = conn.prepare "insert into #{@table} values (#{@range.map{'?'}.join ','})"
|
31
42
|
@count.times do |i|
|
@@ -56,17 +67,18 @@ class TestPerformance < Test::Unit::TestCase
|
|
56
67
|
|
57
68
|
puts "Inserts with hash (batch & chunk-transactional): #{Benchmark.measure {
|
58
69
|
table = conn.table(@table)
|
70
|
+
btable = table.batch
|
59
71
|
(0...@count).each_slice(50) do |slice|
|
60
72
|
conn.transaction do
|
61
73
|
slice.each do |i|
|
62
|
-
|
74
|
+
btable.insert @range.inject({}) { |hash, key| hash[key] = rand; hash }
|
63
75
|
end
|
64
76
|
conn.execute_batch
|
65
77
|
end
|
66
78
|
end
|
67
79
|
}.real}"
|
68
80
|
|
69
|
-
assert_equal @count *
|
81
|
+
assert_equal @count * 6, conn.table(@table).count
|
70
82
|
|
71
83
|
conn.query("select * from #{@table}") do |row|
|
72
84
|
# ...
|
data/test/test_sql.rb
CHANGED
@@ -16,7 +16,6 @@ class TestSQL < Test::Unit::TestCase
|
|
16
16
|
assert_equal "'sysdate'", SQL.value('sysdate')
|
17
17
|
assert_equal "'A''s'", SQL.value("A's")
|
18
18
|
assert_equal "sysdate", SQL.value(JDBCHelper::SQL('sysdate'))
|
19
|
-
|
20
19
|
end
|
21
20
|
|
22
21
|
def test_order
|
@@ -71,6 +70,29 @@ class TestSQL < Test::Unit::TestCase
|
|
71
70
|
assert_raise(ArgumentError) { SQL.where(:a => JDBCHelper::SQL(' aab`bb`` ')) }
|
72
71
|
end
|
73
72
|
|
73
|
+
def test_where_prepared
|
74
|
+
assert_equal ["where a = ?", [1]], SQLPrepared.where(:a => 1)
|
75
|
+
assert_equal ["where a = ?", [1.2]], SQLPrepared.where(:a => 1.2)
|
76
|
+
assert_equal ["where a = ?", [9999999999999999999]], SQLPrepared.where(:a => 9999999999999999999)
|
77
|
+
assert_equal ["where a >= ? and a <= ?", [1,2]], SQLPrepared.where(:a => 1..2)
|
78
|
+
assert_equal ["where a >= ? and a < ?", [1,2]], SQLPrepared.where(:a => 1...2)
|
79
|
+
assert_equal ["where a = ?", ["A's"]], SQLPrepared.where(:a => "A's")
|
80
|
+
assert_equal ["where a is null", []], SQLPrepared.where(:a => nil)
|
81
|
+
assert_equal ["where a is not null", []], SQLPrepared.where(:a => SQL.not_nil)
|
82
|
+
assert_equal ["where a is not null", []], SQLPrepared.where(:a => SQL.not_null)
|
83
|
+
assert_equal ["where a = sysdate", []], SQLPrepared.where(:a => JDBCHelper::SQL('sysdate'))
|
84
|
+
assert_equal ["where sysdate = sysdate", []], SQLPrepared.where(JDBCHelper::SQL('sysdate') => JDBCHelper::SQL('sysdate'))
|
85
|
+
assert_equal ["where a in ('aa', 'bb', 'cc')", []], SQLPrepared.where(:a => %w[aa bb cc])
|
86
|
+
assert_equal ["where a = ? and b = ?", [1, "A's"]], SQLPrepared.where(:a => 1, :b => "A's")
|
87
|
+
assert_equal ["where (a = 1 or b = 1)", []], SQLPrepared.where("a = 1 or b = 1")
|
88
|
+
assert_equal ["where (a = 1 or b = 1) and c = ?", [2]], SQLPrepared.where("a = 1 or b = 1", :c => 2)
|
89
|
+
assert_equal ["where c = ? and (a = 1 or b = 1)", [2]], SQLPrepared.where({:c => 2}, "a = 1 or b = 1")
|
90
|
+
assert_equal ["where c = ? and (a = 1 or b = 1) and (e = 2) and f = ?", [2, 3]],
|
91
|
+
SQLPrepared.where({:c => 2}, "a = 1 or b = 1", nil, "", "e = 2", nil, {:f => 3}, {})
|
92
|
+
assert_equal [nil, []], SQLPrepared.where(nil)
|
93
|
+
assert_equal [nil, []], SQLPrepared.where(" ")
|
94
|
+
end
|
95
|
+
|
74
96
|
def test_select
|
75
97
|
assert_equal "select * from a.b", SQL.select('a.b')
|
76
98
|
assert_equal "select aa, bb from a.b where a is not null",
|
@@ -86,11 +108,17 @@ class TestSQL < Test::Unit::TestCase
|
|
86
108
|
def test_count
|
87
109
|
assert_equal "select count(*) from a.b", SQL.count('a.b')
|
88
110
|
assert_equal "select count(*) from a.b where a is not null", SQL.count('a.b', :a => SQL.not_nil)
|
111
|
+
|
112
|
+
assert_equal ["select count(*) from a.b", []], SQLPrepared.count('a.b')
|
113
|
+
assert_equal ["select count(*) from a.b where a = ?", [1]], SQLPrepared.count('a.b', :a => 1)
|
89
114
|
end
|
90
115
|
|
91
116
|
def test_delete
|
92
117
|
assert_equal "delete from a.b", SQL.delete('a.b')
|
93
118
|
assert_equal "delete from a.b where a is not null", SQL.delete('a.b', :a => SQL.not_nil)
|
119
|
+
|
120
|
+
assert_equal ["delete from a.b", []], SQLPrepared.delete('a.b')
|
121
|
+
assert_equal ["delete from a.b where a = ?", [1]], SQLPrepared.delete('a.b', :a => 1)
|
94
122
|
end
|
95
123
|
|
96
124
|
def test_update
|
@@ -99,11 +127,33 @@ class TestSQL < Test::Unit::TestCase
|
|
99
127
|
|
100
128
|
assert_equal "update a.b set a = 1, b = 'A''s', c = now() where a is not null",
|
101
129
|
SQL.update('a.b', {:a => 1, :b => "A's", :c => JDBCHelper::SQL('now()')}, { :a => SQL.not_nil })
|
130
|
+
|
131
|
+
assert_equal ["update a.b set a = ?, b = ?, c = now()", [1, "A's"]],
|
132
|
+
SQLPrepared.update('a.b', {:a => 1, :b => "A's", :c => JDBCHelper::SQL('now()')}, {})
|
133
|
+
|
134
|
+
assert_equal ["update a.b set a = ?, b = ?, c = now() where a = ?", [1, "A's", 2]],
|
135
|
+
SQLPrepared.update('a.b', {:a => 1, :b => "A's", :c => JDBCHelper::SQL('now()')}, { :a => 2 })
|
102
136
|
end
|
103
137
|
|
104
138
|
def test_insert
|
105
139
|
assert_equal "insert into a.b (a, b, c) values (1, 'A''s', null)",
|
106
140
|
SQL.insert('a.b', :a => 1, :b => "A's", :c => nil)
|
141
|
+
|
142
|
+
assert_equal ["insert into a.b (a, b, c) values (?, ?, ?)", [1, "A's", nil]],
|
143
|
+
SQLPrepared.insert('a.b', :a => 1, :b => "A's", :c => nil)
|
107
144
|
end
|
145
|
+
|
146
|
+
def test_sql_equality
|
147
|
+
assert_equal "a = b", JDBCHelper.SQL('a = b').to_s
|
148
|
+
assert_equal JDBCHelper.SQL('a = b'), JDBCHelper.SQL('a = b')
|
149
|
+
|
150
|
+
# type conversion across ==, but not across eql (TODO TBD)
|
151
|
+
assert JDBCHelper.SQL('a = b') == (JDBCHelper.SQL('a = b'))
|
152
|
+
assert JDBCHelper.SQL('a = b') == 'a = b'
|
153
|
+
assert JDBCHelper.SQL('a = b').eql?(JDBCHelper.SQL('a = b'))
|
154
|
+
assert_false JDBCHelper.SQL('a = b').eql?('a = b')
|
155
|
+
|
156
|
+
assert JDBCHelper.SQL('a = b') != JDBCHelper.SQL('a = c')
|
157
|
+
end
|
108
158
|
end
|
109
159
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: jdbc-helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.5.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Junegunn Choi
|
@@ -10,7 +10,8 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-08-
|
13
|
+
date: 2011-08-29 00:00:00 +09:00
|
14
|
+
default_executable:
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: bundler
|
@@ -56,6 +57,17 @@ dependencies:
|
|
56
57
|
requirement: *id004
|
57
58
|
prerelease: false
|
58
59
|
type: :development
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: pry
|
62
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
requirement: *id005
|
69
|
+
prerelease: false
|
70
|
+
type: :development
|
59
71
|
description: A JDBC helper for JRuby/Database developers.
|
60
72
|
email: junegunn.c@gmail.com
|
61
73
|
executables: []
|
@@ -80,6 +92,7 @@ files:
|
|
80
92
|
- lib/jdbc-helper/connector/oracle_connector.rb
|
81
93
|
- lib/jdbc-helper/constants.rb
|
82
94
|
- lib/jdbc-helper/sql.rb
|
95
|
+
- lib/jdbc-helper/sql_prepared.rb
|
83
96
|
- lib/jdbc-helper/wrapper/function_wrapper.rb
|
84
97
|
- lib/jdbc-helper/wrapper/object_wrapper.rb
|
85
98
|
- lib/jdbc-helper/wrapper/procedure_wrapper.rb
|
@@ -95,6 +108,7 @@ files:
|
|
95
108
|
- test/test_object_wrapper.rb
|
96
109
|
- test/test_performance.rb
|
97
110
|
- test/test_sql.rb
|
111
|
+
has_rdoc: true
|
98
112
|
homepage: http://github.com/junegunn/jdbc-helper
|
99
113
|
licenses:
|
100
114
|
- MIT
|
@@ -121,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
135
|
requirements: []
|
122
136
|
|
123
137
|
rubyforge_project:
|
124
|
-
rubygems_version: 1.
|
138
|
+
rubygems_version: 1.5.1
|
125
139
|
signing_key:
|
126
140
|
specification_version: 3
|
127
141
|
summary: A JDBC helper for JRuby/Database developers.
|