jdbc-helper 0.4.10 → 0.5.0
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.
- 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.
|