simpleOracleJDBC 0.1
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/README.md +65 -0
- data/Rakefile.rb +30 -0
- data/lib/simple_oracle_jdbc.rb +19 -0
- data/lib/simple_oracle_jdbc/bindings.rb +214 -0
- data/lib/simple_oracle_jdbc/db_call.rb +105 -0
- data/lib/simple_oracle_jdbc/interface.rb +120 -0
- data/lib/simple_oracle_jdbc/result_set.rb +155 -0
- data/lib/simple_oracle_jdbc/sql.rb +95 -0
- data/test/binding_test.rb +224 -0
- data/test/db_call_test.rb +91 -0
- data/test/helper.rb +20 -0
- data/test/interface_test.rb +76 -0
- data/test/result_set_test.rb +128 -0
- data/test/sql_test.rb +73 -0
- data/test/test_runner.rb +14 -0
- metadata +61 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
module SimpleOracleJDBC
|
2
|
+
|
3
|
+
# This module provides a wrapper around a JDBC result set object, allowing the result set to be returned
|
4
|
+
# a row at a time, as an array or arrays or an array of hashes etc.
|
5
|
+
#
|
6
|
+
# Requires a instance variable called @result_set to be defined in the class
|
7
|
+
# this module is included into. This should contain a JDBC result set object.
|
8
|
+
|
9
|
+
module ResultSet
|
10
|
+
|
11
|
+
# Retrieves the next row from the database and returns an array. Each element in the array
|
12
|
+
# corresponds to a column in the select statement that created the result set.
|
13
|
+
#
|
14
|
+
# The value nil will be returned if there are no more rows to return, and the result set
|
15
|
+
# will be closed.
|
16
|
+
def next_array
|
17
|
+
next_row
|
18
|
+
end
|
19
|
+
|
20
|
+
# Processes each row of the result set using the provided block, passing one row at a time
|
21
|
+
# to the block as an array, eg:
|
22
|
+
#
|
23
|
+
# obj.each_array do |r|
|
24
|
+
# # process the row
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# The result set will be closed when the method returns.
|
28
|
+
def each_array(&blk)
|
29
|
+
each_result true, &blk
|
30
|
+
end
|
31
|
+
|
32
|
+
# Consumes all the rows of the result set, and returns the result as an array of arrays.
|
33
|
+
#
|
34
|
+
# An empty array will be returned if the result set contains no rows.
|
35
|
+
#
|
36
|
+
# If the result set contains a lot of rows, this will create an array that requires a lot of
|
37
|
+
# memory, so use with caution.
|
38
|
+
#
|
39
|
+
# The result set will be closed when the method returns.
|
40
|
+
def all_array
|
41
|
+
all_results(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Similar to next_array, only it returns the result as a hash. The hash will have one key
|
45
|
+
# for each column in the corresponding select statement. The key will be the upper case name of
|
46
|
+
# the column or alias from the SQL statement.
|
47
|
+
def next_hash
|
48
|
+
next_row(false)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Processes each row of the result set using hte provided block, passing one row as a time to
|
52
|
+
# the block as a hash, eg:
|
53
|
+
#
|
54
|
+
# obj.each_hash do |r|
|
55
|
+
# # process the row
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# The result set will be closed when the method returns.
|
59
|
+
def each_hash(&blk)
|
60
|
+
each_result false, &blk
|
61
|
+
end
|
62
|
+
|
63
|
+
# Consumes all the rows of the result set, and returns the result as an array of hashes.
|
64
|
+
#
|
65
|
+
# An empty array will be returned if the result set contains no rows.
|
66
|
+
#
|
67
|
+
# If the result set contains a lot of rows, this will create an array that requires a lot of
|
68
|
+
# memory, so use with caution.
|
69
|
+
#
|
70
|
+
# The result set will be closed when the method returns.
|
71
|
+
def all_hash
|
72
|
+
all_results(false)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Closes the result set if it exists, and also closes the SQL statement that created the result
|
76
|
+
# set.
|
77
|
+
# TODO - does it make sense to close the statement here too?
|
78
|
+
def close_result_set
|
79
|
+
if @result_set
|
80
|
+
@result_set.close
|
81
|
+
@result_set = nil
|
82
|
+
end
|
83
|
+
close_statement
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def row_as_array
|
89
|
+
cols = @result_set.get_meta_data.get_column_count
|
90
|
+
a = Array.new
|
91
|
+
1.upto(cols) do |i|
|
92
|
+
a.push retrieve_value(@result_set, i)
|
93
|
+
end
|
94
|
+
a
|
95
|
+
end
|
96
|
+
|
97
|
+
def row_as_hash
|
98
|
+
mdata = @result_set.get_meta_data
|
99
|
+
cols = mdata.get_column_count
|
100
|
+
h = Hash.new
|
101
|
+
1.upto(cols) do |i|
|
102
|
+
h[mdata.get_column_name(i)] = retrieve_value(@result_set, i)
|
103
|
+
end
|
104
|
+
h
|
105
|
+
end
|
106
|
+
|
107
|
+
def all_results(array=true)
|
108
|
+
raise SimpleOracleJDBC::NoResultSet unless @result_set
|
109
|
+
results = Array.new
|
110
|
+
begin
|
111
|
+
while(r = @result_set.next) do
|
112
|
+
if array
|
113
|
+
results.push row_as_array
|
114
|
+
else
|
115
|
+
results.push row_as_hash
|
116
|
+
end
|
117
|
+
end
|
118
|
+
ensure
|
119
|
+
close_result_set
|
120
|
+
end
|
121
|
+
results
|
122
|
+
end
|
123
|
+
|
124
|
+
def each_result(as_array=true, &blk)
|
125
|
+
raise SimpleOracleJDBC::NoResultSet unless @result_set
|
126
|
+
begin
|
127
|
+
while(r = @result_set.next)
|
128
|
+
if as_array
|
129
|
+
yield row_as_array
|
130
|
+
else
|
131
|
+
yield row_as_hash
|
132
|
+
end
|
133
|
+
end
|
134
|
+
ensure
|
135
|
+
close_result_set
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def next_row(as_array=true)
|
140
|
+
raise SimpleOracleJDBC::NoResultSet unless @result_set
|
141
|
+
r = @result_set.next
|
142
|
+
if r
|
143
|
+
if as_array
|
144
|
+
row_as_array
|
145
|
+
else
|
146
|
+
row_as_hash
|
147
|
+
end
|
148
|
+
else
|
149
|
+
close_result_set
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module SimpleOracleJDBC
|
2
|
+
|
3
|
+
class Sql
|
4
|
+
|
5
|
+
# The Sql class provides a simple interface for preparing, executing and retrieving results
|
6
|
+
# for SQL statements.
|
7
|
+
#
|
8
|
+
# The ResultSet module is included to allow results to be retrieved from result sets as arrays
|
9
|
+
# or hashes.
|
10
|
+
#
|
11
|
+
# The Binding module is included to allow Ruby types to be convered into sensible Java equivalents
|
12
|
+
# and vice versa.
|
13
|
+
#
|
14
|
+
# @!attribute statement
|
15
|
+
# @return [JDBC Prepared Statement] Returns the raw JDBC prepared statement
|
16
|
+
#
|
17
|
+
# @!attribute sql
|
18
|
+
# @return [String] Returns the original SQL string used to create the object
|
19
|
+
#
|
20
|
+
# @!attribute result_set
|
21
|
+
# @return [JDBC Result Set] Returns the raw JDBC result set after the statement is executed
|
22
|
+
|
23
|
+
|
24
|
+
include Binding
|
25
|
+
include ResultSet
|
26
|
+
|
27
|
+
attr_reader :statement, :sql, :result_set
|
28
|
+
|
29
|
+
attr_writer :result_set
|
30
|
+
|
31
|
+
# Creates a new instance of this class. Not intended to be used directly. Use the factory
|
32
|
+
# class methods prepare or execute instead.
|
33
|
+
def initialize
|
34
|
+
end
|
35
|
+
|
36
|
+
# Takes a JDBC connection object and an SQL statement and returns a SimpleOracleJDBC::Sql
|
37
|
+
# object with the prepared statement.
|
38
|
+
def self.prepare(connection, sql)
|
39
|
+
sql_object = self.new
|
40
|
+
sql_object.prepare(connection,sql)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Takes a JDBC connection object, an SQL statement and an optional list of bind variables and
|
44
|
+
# will prepare and execute the sql statement, returning an SimpleOracle::Sql object.
|
45
|
+
def self.execute(connection, sql, *binds)
|
46
|
+
sql_object = self.new
|
47
|
+
sql_object.prepare(connection, sql)
|
48
|
+
sql_object.execute(*binds)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Given a JDBC connection and a SQL string, the sql will be stored in the @sql instance variable
|
52
|
+
# and a JDBC prepared statement will be stored in @statement.
|
53
|
+
#
|
54
|
+
# This method returns self to allow calls to be chained.
|
55
|
+
def prepare(connection, sql)
|
56
|
+
@sql = sql
|
57
|
+
@statement = connection.prepare_statement(@sql)
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
# Executes the SQL prepared by the prepare method and binds the optional list of bind varibles.
|
62
|
+
#
|
63
|
+
# If the SQL statement does not return data (ie is not a select statement) then @result_set will be
|
64
|
+
# set to nil. Otherwise, the resulting JDBC result set will be stored in @result_set
|
65
|
+
def execute(*binds)
|
66
|
+
binds.each_with_index do |b, i|
|
67
|
+
bind_value(@statement, b, i+1)
|
68
|
+
end
|
69
|
+
|
70
|
+
unless @sql =~ /^\s*select/i
|
71
|
+
@result_set = nil
|
72
|
+
@statement.execute()
|
73
|
+
close_statement
|
74
|
+
else
|
75
|
+
@result_set = @statement.execute_query()
|
76
|
+
end
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Closes both the prepared SQL statement stored in @statement and any result set stored in @result_set
|
81
|
+
def close
|
82
|
+
close_result_set
|
83
|
+
close_statement
|
84
|
+
end
|
85
|
+
|
86
|
+
# Closes the JDBC statement stored in @statement
|
87
|
+
def close_statement
|
88
|
+
if @statement
|
89
|
+
@statement.close
|
90
|
+
@statement = nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class BindingTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include TestHelper
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@interface = @@interface
|
9
|
+
@sql = SimpleOracleJDBC::Sql.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
@sql.close
|
14
|
+
end
|
15
|
+
|
16
|
+
# The binding module provides a set of methods to bind ruby
|
17
|
+
# types to SQL bindable objects (prepared proc calls or SQL statements)
|
18
|
+
# All it needs is a bindable object to be passed.
|
19
|
+
#
|
20
|
+
# For ease of testing, the ::Interface will be used to create the
|
21
|
+
# prepared objects.
|
22
|
+
|
23
|
+
def test_dates_can_be_bound_and_retrieved
|
24
|
+
call = @interface.prepare_proc("begin
|
25
|
+
:l_date := :i_date;
|
26
|
+
end;")
|
27
|
+
call.execute([Date, nil, :out], Date.new(2012,1,1))
|
28
|
+
assert_equal(Date.new(2012,1,1), call[1])
|
29
|
+
assert(call[1].is_a? Date)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_null_dates_can_be_bound_and_retrieved
|
33
|
+
call = @interface.prepare_proc("begin
|
34
|
+
:l_date := :i_date;
|
35
|
+
end;")
|
36
|
+
call.execute([Date, nil, :out], [Date, nil])
|
37
|
+
assert_nil(call[1])
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def test_times_can_be_bound_and_retrieved
|
42
|
+
call = @interface.prepare_proc("begin
|
43
|
+
:l_date := :i_date;
|
44
|
+
end;")
|
45
|
+
t = Time.now
|
46
|
+
call.execute([Time, nil, :out], t)
|
47
|
+
assert_equal(t, call[1])
|
48
|
+
assert(call[1].is_a? Time)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_null_times_can_be_bound_and_retrieved
|
52
|
+
call = @interface.prepare_proc("begin
|
53
|
+
:l_date := :i_date;
|
54
|
+
end;")
|
55
|
+
call.execute([Time, nil, :out], [Time, nil])
|
56
|
+
assert_nil(call[1])
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_string_can_be_bound_and_retrieved
|
60
|
+
call = @interface.prepare_proc("begin
|
61
|
+
:l_date := :i_date;
|
62
|
+
end;")
|
63
|
+
t = "hello there I am a string"
|
64
|
+
call.execute([String, nil, :out], t)
|
65
|
+
assert_equal(t, call[1])
|
66
|
+
assert(call[1].is_a? String)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_null_string_can_be_bound_and_retrieved
|
70
|
+
call = @interface.prepare_proc("begin
|
71
|
+
:l_date := :i_date;
|
72
|
+
end;")
|
73
|
+
call.execute([String, nil, :out], [String, nil])
|
74
|
+
assert_nil(call[1])
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_integer_can_be_bound_and_retrieved
|
78
|
+
call = @interface.prepare_proc("begin
|
79
|
+
:l_date := :i_date;
|
80
|
+
end;")
|
81
|
+
t = 1234
|
82
|
+
call.execute([Integer, nil, :out], t)
|
83
|
+
assert_equal(t, call[1])
|
84
|
+
assert(call[1].is_a? Fixnum)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_null_integer_can_be_bound_and_retrieved
|
88
|
+
call = @interface.prepare_proc("begin
|
89
|
+
:l_date := :i_date;
|
90
|
+
end;")
|
91
|
+
call.execute([Integer, nil, :out], [Integer, nil])
|
92
|
+
assert_nil(call[1])
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_number_can_be_bound_and_retrieved
|
96
|
+
call = @interface.prepare_proc("begin
|
97
|
+
:l_date := :i_date;
|
98
|
+
end;")
|
99
|
+
t = 1234.123456789
|
100
|
+
call.execute([Float, nil, :out], t)
|
101
|
+
assert_equal(t, call[1])
|
102
|
+
assert(call[1].is_a? Float)
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_null_number_can_be_bound_and_retrieved
|
106
|
+
call = @interface.prepare_proc("begin
|
107
|
+
:l_date := :i_date;
|
108
|
+
end;")
|
109
|
+
call.execute([Float, nil, :out], [Float, nil])
|
110
|
+
assert_nil(call[1])
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_refcursor_can_be_bound_and_retrieved
|
114
|
+
call = @interface.prepare_proc("declare
|
115
|
+
v_refcursor sys_refcursor;
|
116
|
+
begin
|
117
|
+
open v_refcursor for
|
118
|
+
select * from dual;
|
119
|
+
|
120
|
+
:ret := v_refcursor;
|
121
|
+
end;")
|
122
|
+
call.execute([:refcursor, nil, :out])
|
123
|
+
results = call[1]
|
124
|
+
assert(results.is_a? SimpleOracleJDBC::Sql)
|
125
|
+
rows = results.all_array
|
126
|
+
assert_equal(rows.length, 1)
|
127
|
+
assert_equal(rows[0][0], 'X')
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_unknown_data_type_raises_exeception_when_bound
|
131
|
+
call = @interface.prepare_proc("begin
|
132
|
+
:l_date := :i_date;
|
133
|
+
end;")
|
134
|
+
assert_raises SimpleOracleJDBC::UnknownBindType do
|
135
|
+
call.execute([SimpleOracleJDBC::Interface, nil, :out], [Float, nil])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_in_out_parameter_can_be_bound
|
140
|
+
call = @interface.prepare_proc("begin
|
141
|
+
:l_date := :i_date;
|
142
|
+
end;")
|
143
|
+
t = 1234.123456789
|
144
|
+
call.execute([Float, nil, :out], [Float, t, :inout])
|
145
|
+
assert_equal(t, call[1])
|
146
|
+
assert(call[1].is_a? Float)
|
147
|
+
end
|
148
|
+
|
149
|
+
### SQL specific tests.
|
150
|
+
|
151
|
+
def test_date_is_retrieved_as_ruby_time
|
152
|
+
sql = @interface.execute_sql("select to_date('20120713 13:23:23', 'YYYYMMDD HH24:MI:SS') from dual")
|
153
|
+
results = sql.all_array
|
154
|
+
assert_equal(results[0][0], Time.local(2012, 7, 13, 13, 23, 23))
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_null_date_is_retrived_as_nil
|
158
|
+
sql = @interface.execute_sql("select cast(null as date) from dual")
|
159
|
+
results = sql.all_array
|
160
|
+
assert_equal(results[0][0], nil)
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_timestamp_is_retrieved_as_ruby_time
|
164
|
+
sql = @interface.execute_sql("select cast(to_date('20120713 13:23:23', 'YYYYMMDD HH24:MI:SS') as timestamp) from dual")
|
165
|
+
results = sql.all_array
|
166
|
+
assert_equal(results[0][0], Time.local(2012, 7, 13, 13, 23, 23))
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_null_timestamp_is_retrived_as_nil
|
170
|
+
sql = @interface.execute_sql("select cast(null as timestamp) from dual")
|
171
|
+
results = sql.all_array
|
172
|
+
assert_equal(results[0][0], nil)
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_integer_is_retrieved_as_ruby_integer
|
176
|
+
sql = @interface.execute_sql("select cast(1234567890 as integer) from dual")
|
177
|
+
results = sql.all_array
|
178
|
+
assert_equal(results[0][0], 1234567890)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_null_integer_is_retrived_as_nil
|
182
|
+
sql = @interface.execute_sql("select cast(null as integer) from dual")
|
183
|
+
results = sql.all_array
|
184
|
+
assert_equal(results[0][0], nil)
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_number_is_retrieved_as_ruby_float
|
188
|
+
sql = @interface.execute_sql("select cast(1234567890.123456789 as number) from dual")
|
189
|
+
results = sql.all_array
|
190
|
+
assert_equal(results[0][0], 1234567890.123456789)
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_null_number_is_retrived_as_nil
|
194
|
+
sql = @interface.execute_sql("select cast(null as number) from dual")
|
195
|
+
results = sql.all_array
|
196
|
+
assert_equal(results[0][0], nil)
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_char_is_retrieved_as_ruby_string
|
200
|
+
sql = @interface.execute_sql("select cast('hello there' as char(11)) from dual")
|
201
|
+
results = sql.all_array
|
202
|
+
assert_equal(results[0][0], "hello there")
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_null_char_is_retrieved_as_nil
|
206
|
+
sql = @interface.execute_sql("select cast(null as char(10)) from dual")
|
207
|
+
results = sql.all_array
|
208
|
+
assert_equal(results[0][0], nil)
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_varchar_is_retrieved_as_ruby_string
|
212
|
+
sql = @interface.execute_sql("select cast('hello there' as varchar2(1000)) from dual")
|
213
|
+
results = sql.all_array
|
214
|
+
assert_equal(results[0][0], "hello there")
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_null_varchar_is_retrieved_as_nil
|
218
|
+
sql = @interface.execute_sql("select cast(null as varchar2(10)) from dual")
|
219
|
+
results = sql.all_array
|
220
|
+
assert_equal(results[0][0], nil)
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
end
|