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.
@@ -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