flash-gordons-ruby-plsql 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.
- checksums.yaml +15 -0
- data/Gemfile +14 -0
- data/History.txt +172 -0
- data/License.txt +20 -0
- data/README.md +182 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/plsql/connection.rb +233 -0
- data/lib/plsql/helpers.rb +9 -0
- data/lib/plsql/jdbc_connection.rb +542 -0
- data/lib/plsql/oci8_patches.rb +25 -0
- data/lib/plsql/oci_connection.rb +339 -0
- data/lib/plsql/package.rb +80 -0
- data/lib/plsql/procedure.rb +269 -0
- data/lib/plsql/procedure_call.rb +124 -0
- data/lib/plsql/schema.rb +309 -0
- data/lib/plsql/sequence.rb +49 -0
- data/lib/plsql/sql_statements.rb +87 -0
- data/lib/plsql/table.rb +348 -0
- data/lib/plsql/type.rb +275 -0
- data/lib/plsql/variable.rb +146 -0
- data/lib/plsql/version.rb +3 -0
- data/lib/plsql/view.rb +41 -0
- data/lib/ruby-plsql.rb +1 -0
- data/lib/ruby_plsql.rb +18 -0
- data/ruby-plsql.gemspec +87 -0
- data/spec/plsql/connection_spec.rb +495 -0
- data/spec/plsql/package_spec.rb +149 -0
- data/spec/plsql/procedure_spec.rb +2048 -0
- data/spec/plsql/schema_spec.rb +331 -0
- data/spec/plsql/sequence_spec.rb +67 -0
- data/spec/plsql/sql_statements_spec.rb +91 -0
- data/spec/plsql/table_spec.rb +371 -0
- data/spec/plsql/type_spec.rb +304 -0
- data/spec/plsql/variable_spec.rb +505 -0
- data/spec/plsql/version_spec.rb +8 -0
- data/spec/plsql/view_spec.rb +264 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +79 -0
- metadata +159 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Package" do
|
4
|
+
before(:all) do
|
5
|
+
plsql.connection = get_connection
|
6
|
+
# Not use ROWTYPE definition due to Oracle bug (see http://arjudba.blogspot.com/2011/12/ora-00600-internal-error-code-arguments.html)
|
7
|
+
plsql.execute <<-SQL
|
8
|
+
CREATE OR REPLACE PACKAGE test_package IS
|
9
|
+
TYPE object_record IS RECORD(
|
10
|
+
owner ALL_OBJECTS.OWNER%TYPE,
|
11
|
+
object_name ALL_OBJECTS.OBJECT_NAME%TYPE,
|
12
|
+
object_id ALL_OBJECTS.OBJECT_ID%TYPE,
|
13
|
+
object_type ALL_OBJECTS.OBJECT_TYPE%TYPE);
|
14
|
+
TYPE objects_list IS TABLE OF object_record;
|
15
|
+
|
16
|
+
FUNCTION find_objects_by_name ( p_name ALL_OBJECTS.OBJECT_NAME%TYPE )
|
17
|
+
RETURN objects_list PIPELINED;
|
18
|
+
|
19
|
+
test_variable NUMBER;
|
20
|
+
FUNCTION test_procedure ( p_string VARCHAR2 )
|
21
|
+
RETURN VARCHAR2;
|
22
|
+
END test_package;
|
23
|
+
SQL
|
24
|
+
plsql.execute <<-SQL
|
25
|
+
CREATE OR REPLACE PACKAGE BODY test_package IS
|
26
|
+
FUNCTION find_objects_by_name ( p_name ALL_OBJECTS.OBJECT_NAME%TYPE )
|
27
|
+
RETURN objects_list PIPELINED
|
28
|
+
IS
|
29
|
+
BEGIN
|
30
|
+
FOR l_object IN (
|
31
|
+
SELECT OWNER, OBJECT_NAME, OBJECT_ID, OBJECT_TYPE
|
32
|
+
FROM ALL_OBJECTS
|
33
|
+
WHERE OBJECT_NAME = UPPER(p_name)
|
34
|
+
AND ROWNUM < 11)
|
35
|
+
LOOP
|
36
|
+
PIPE ROW(l_object);
|
37
|
+
END LOOP;
|
38
|
+
END find_objects_by_name;
|
39
|
+
|
40
|
+
FUNCTION test_procedure ( p_string VARCHAR2 )
|
41
|
+
RETURN VARCHAR2
|
42
|
+
IS
|
43
|
+
BEGIN
|
44
|
+
RETURN UPPER(p_string);
|
45
|
+
END test_procedure;
|
46
|
+
END;
|
47
|
+
SQL
|
48
|
+
end
|
49
|
+
|
50
|
+
after(:all) do
|
51
|
+
plsql.execute "DROP PACKAGE test_package"
|
52
|
+
plsql.logoff
|
53
|
+
end
|
54
|
+
|
55
|
+
before(:each) do
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should find existing package" do
|
59
|
+
PLSQL::Package.find(plsql, :test_package).should_not be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not find nonexisting package" do
|
63
|
+
PLSQL::Package.find(plsql, :qwerty123456).should be_nil
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should find existing package in schema" do
|
67
|
+
plsql.test_package.class.should == PLSQL::Package
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should search objects via []" do
|
71
|
+
PLSQL::Package.find(plsql, :test_package)['test_procedure'].should be_a PLSQL::Procedure
|
72
|
+
PLSQL::Package.find(plsql, :test_package)['test_variable'].should be_a PLSQL::Variable
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should tell ordinary function from pipelined" do
|
76
|
+
PLSQL::Package.find(plsql, :test_package)['test_procedure'].should be_a PLSQL::Procedure
|
77
|
+
PLSQL::Package.find(plsql, :test_package)['find_objects_by_name'].should be_a PLSQL::PipelinedFunction
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should execute package function and return correct value" do
|
81
|
+
plsql.test_package.test_procedure('xxx').should == 'XXX'
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "variables" do
|
85
|
+
it "should set and get package variable value" do
|
86
|
+
plsql.test_package.test_variable = 1
|
87
|
+
plsql.test_package.test_variable.should == 1
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "Synonym to package" do
|
94
|
+
|
95
|
+
before(:all) do
|
96
|
+
plsql.connection = get_connection
|
97
|
+
plsql.execute <<-SQL
|
98
|
+
CREATE OR REPLACE PACKAGE hr.test_package IS
|
99
|
+
FUNCTION test_procedure ( p_string VARCHAR2 )
|
100
|
+
RETURN VARCHAR2;
|
101
|
+
END;
|
102
|
+
SQL
|
103
|
+
plsql.execute <<-SQL
|
104
|
+
CREATE OR REPLACE PACKAGE BODY hr.test_package IS
|
105
|
+
FUNCTION test_procedure ( p_string VARCHAR2 )
|
106
|
+
RETURN VARCHAR2
|
107
|
+
IS
|
108
|
+
BEGIN
|
109
|
+
RETURN UPPER(p_string);
|
110
|
+
END test_procedure;
|
111
|
+
END;
|
112
|
+
SQL
|
113
|
+
plsql.execute "CREATE SYNONYM test_pkg_synonym FOR hr.test_package"
|
114
|
+
end
|
115
|
+
|
116
|
+
after(:all) do
|
117
|
+
plsql.execute "DROP SYNONYM test_pkg_synonym" rescue nil
|
118
|
+
plsql.logoff
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should find synonym to package" do
|
122
|
+
PLSQL::Package.find(plsql, :test_pkg_synonym).should_not be_nil
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should execute package function using synonym and return correct value" do
|
126
|
+
plsql.test_pkg_synonym.test_procedure('xxx').should == 'XXX'
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "Public synonym to package" do
|
132
|
+
|
133
|
+
before(:all) do
|
134
|
+
plsql.connection = get_connection
|
135
|
+
end
|
136
|
+
|
137
|
+
after(:all) do
|
138
|
+
plsql.logoff
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should find public synonym to package" do
|
142
|
+
PLSQL::Package.find(plsql, :utl_encode).should_not be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should execute package function using public synonym and return correct value" do
|
146
|
+
plsql.utl_encode.base64_encode('abc').should == '4372773D'
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
@@ -0,0 +1,2048 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Parameter type mapping /" do
|
6
|
+
describe "Function with string parameters" do
|
7
|
+
before(:all) do
|
8
|
+
plsql.connect! CONNECTION_PARAMS
|
9
|
+
plsql.execute <<-SQL
|
10
|
+
CREATE OR REPLACE FUNCTION test_uppercase
|
11
|
+
( p_string VARCHAR2 )
|
12
|
+
RETURN VARCHAR2
|
13
|
+
IS
|
14
|
+
BEGIN
|
15
|
+
RETURN UPPER(p_string);
|
16
|
+
END test_uppercase;
|
17
|
+
SQL
|
18
|
+
end
|
19
|
+
|
20
|
+
after(:all) do
|
21
|
+
plsql.execute "DROP FUNCTION test_uppercase"
|
22
|
+
plsql.logoff
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should find existing procedure" do
|
26
|
+
PLSQL::Procedure.find(plsql, :test_uppercase).should_not be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not find nonexisting procedure" do
|
30
|
+
PLSQL::Procedure.find(plsql, :qwerty123456).should be_nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should execute function and return correct value" do
|
34
|
+
plsql.test_uppercase('xxx').should == 'XXX'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should execute function with named parameters and return correct value" do
|
38
|
+
plsql.test_uppercase(:p_string => 'xxx').should == 'XXX'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should raise error if wrong number of arguments is passed" do
|
42
|
+
lambda { plsql.test_uppercase('xxx','yyy') }.should raise_error(ArgumentError)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should raise error if wrong named argument is passed" do
|
46
|
+
lambda { plsql.test_uppercase(:p_string2 => 'xxx') }.should raise_error(ArgumentError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should execute function with schema name specified" do
|
50
|
+
plsql.hr.test_uppercase('xxx').should == 'XXX'
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should process nil parameter as NULL" do
|
54
|
+
plsql.test_uppercase(nil).should be_nil
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "Function with numeric parameters" do
|
60
|
+
before(:all) do
|
61
|
+
plsql.connect! CONNECTION_PARAMS
|
62
|
+
plsql.execute <<-SQL
|
63
|
+
CREATE OR REPLACE FUNCTION test_sum
|
64
|
+
( p_num1 NUMBER, p_num2 NUMBER )
|
65
|
+
RETURN NUMBER
|
66
|
+
IS
|
67
|
+
BEGIN
|
68
|
+
RETURN p_num1 + p_num2;
|
69
|
+
END test_sum;
|
70
|
+
SQL
|
71
|
+
plsql.execute <<-SQL
|
72
|
+
CREATE OR REPLACE FUNCTION test_number_1
|
73
|
+
( p_num NUMBER )
|
74
|
+
RETURN VARCHAR2
|
75
|
+
IS
|
76
|
+
BEGIN
|
77
|
+
IF p_num = 1 THEN
|
78
|
+
RETURN 'Y';
|
79
|
+
ELSIF p_num = 0 THEN
|
80
|
+
RETURN 'N';
|
81
|
+
ELSIF p_num IS NULL THEN
|
82
|
+
RETURN NULL;
|
83
|
+
ELSE
|
84
|
+
RETURN 'UNKNOWN';
|
85
|
+
END IF;
|
86
|
+
END test_number_1;
|
87
|
+
SQL
|
88
|
+
plsql.execute <<-SQL
|
89
|
+
CREATE OR REPLACE PROCEDURE test_integers
|
90
|
+
( p_pls_int PLS_INTEGER, p_bin_int BINARY_INTEGER, x_pls_int OUT PLS_INTEGER, x_bin_int OUT BINARY_INTEGER )
|
91
|
+
IS
|
92
|
+
BEGIN
|
93
|
+
x_pls_int := p_pls_int;
|
94
|
+
x_bin_int := p_bin_int;
|
95
|
+
END;
|
96
|
+
SQL
|
97
|
+
end
|
98
|
+
|
99
|
+
after(:all) do
|
100
|
+
plsql.execute "DROP FUNCTION test_sum"
|
101
|
+
plsql.execute "DROP FUNCTION test_number_1"
|
102
|
+
plsql.execute "DROP PROCEDURE test_integers"
|
103
|
+
plsql.logoff
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should process integer parameters" do
|
107
|
+
plsql.test_sum(123,456).should == 579
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should process big integer parameters" do
|
111
|
+
plsql.test_sum(123123123123,456456456456).should == 579579579579
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should process float parameters and return BigDecimal" do
|
115
|
+
plsql.test_sum(123.123,456.456).should == BigDecimal("579.579")
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should process BigDecimal parameters and return BigDecimal" do
|
119
|
+
plsql.test_sum(:p_num1 => BigDecimal("123.123"), :p_num2 => BigDecimal("456.456")).should == BigDecimal("579.579")
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should process nil parameter as NULL" do
|
123
|
+
plsql.test_sum(123,nil).should be_nil
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should convert true value to 1 for NUMBER parameter" do
|
127
|
+
plsql.test_number_1(true).should == 'Y'
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should convert false value to 0 for NUMBER parameter" do
|
131
|
+
plsql.test_number_1(false).should == 'N'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should process binary integer parameters" do
|
135
|
+
plsql.test_integers(123, 456).should == {:x_pls_int => 123, :x_bin_int => 456}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "Function with date parameters" do
|
140
|
+
|
141
|
+
before(:all) do
|
142
|
+
plsql.connect! CONNECTION_PARAMS
|
143
|
+
plsql.execute <<-SQL
|
144
|
+
CREATE OR REPLACE FUNCTION test_date
|
145
|
+
( p_date DATE )
|
146
|
+
RETURN DATE
|
147
|
+
IS
|
148
|
+
BEGIN
|
149
|
+
RETURN p_date + 1;
|
150
|
+
END test_date;
|
151
|
+
SQL
|
152
|
+
end
|
153
|
+
|
154
|
+
before(:each) do
|
155
|
+
plsql.default_timezone = :local
|
156
|
+
end
|
157
|
+
|
158
|
+
after(:all) do
|
159
|
+
plsql.execute "DROP FUNCTION test_date"
|
160
|
+
plsql.logoff
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should process Time parameters" do
|
164
|
+
now = Time.local(2008,8,12,14,28,0)
|
165
|
+
plsql.test_date(now).should == now + 60*60*24
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should process UTC Time parameters" do
|
169
|
+
plsql.default_timezone = :utc
|
170
|
+
now = Time.utc(2008,8,12,14,28,0)
|
171
|
+
plsql.test_date(now).should == now + 60*60*24
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should process DateTime parameters" do
|
175
|
+
now = DateTime.parse(Time.local(2008,8,12,14,28,0).iso8601)
|
176
|
+
result = plsql.test_date(now)
|
177
|
+
result.class.should == Time
|
178
|
+
result.should == Time.parse((now + 1).strftime("%c"))
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should process old DateTime parameters" do
|
182
|
+
now = DateTime.civil(1901,1,1,12,0,0,plsql.local_timezone_offset)
|
183
|
+
result = plsql.test_date(now)
|
184
|
+
result.class.should == Time
|
185
|
+
result.should == Time.parse((now + 1).strftime("%c"))
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should process Date parameters" do
|
189
|
+
now = Date.new(2008,8,12)
|
190
|
+
result = plsql.test_date(now)
|
191
|
+
result.class.should == Time
|
192
|
+
result.should == Time.parse((now + 1).strftime("%c"))
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should process old Date parameters" do
|
196
|
+
now = Date.new(1901,1,1)
|
197
|
+
result = plsql.test_date(now)
|
198
|
+
result.class.should == Time
|
199
|
+
result.should == Time.parse((now + 1).strftime("%c"))
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should process nil date parameter as NULL" do
|
203
|
+
plsql.test_date(nil).should be_nil
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "Function with timestamp parameters" do
|
209
|
+
before(:all) do
|
210
|
+
plsql.connect! CONNECTION_PARAMS
|
211
|
+
plsql.execute <<-SQL
|
212
|
+
CREATE OR REPLACE FUNCTION test_timestamp
|
213
|
+
( p_time TIMESTAMP )
|
214
|
+
RETURN TIMESTAMP
|
215
|
+
IS
|
216
|
+
BEGIN
|
217
|
+
RETURN p_time + NUMTODSINTERVAL(1, 'DAY');
|
218
|
+
END test_timestamp;
|
219
|
+
SQL
|
220
|
+
end
|
221
|
+
|
222
|
+
after(:all) do
|
223
|
+
plsql.execute "DROP FUNCTION test_timestamp"
|
224
|
+
plsql.logoff
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should process timestamp parameters" do
|
228
|
+
# now = Time.now
|
229
|
+
now = Time.local(2008,8,12,14,28,0)
|
230
|
+
plsql.test_timestamp(now).should == now + 60*60*24
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
describe "Procedure with output parameters" do
|
236
|
+
before(:all) do
|
237
|
+
plsql.connect! CONNECTION_PARAMS
|
238
|
+
plsql.execute <<-SQL
|
239
|
+
CREATE OR REPLACE PROCEDURE test_copy
|
240
|
+
( p_from VARCHAR2, p_to OUT VARCHAR2, p_to_double OUT VARCHAR2 )
|
241
|
+
IS
|
242
|
+
BEGIN
|
243
|
+
p_to := p_from;
|
244
|
+
p_to_double := p_from || p_from;
|
245
|
+
END test_copy;
|
246
|
+
SQL
|
247
|
+
end
|
248
|
+
|
249
|
+
after(:all) do
|
250
|
+
plsql.execute "DROP PROCEDURE test_copy"
|
251
|
+
plsql.logoff
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should return hash with output parameters" do
|
255
|
+
plsql.test_copy("abc", nil, nil).should == { :p_to => "abc", :p_to_double => "abcabc" }
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should return hash with output parameters when called with named parameters" do
|
259
|
+
plsql.test_copy(:p_from => "abc", :p_to => nil, :p_to_double => nil).should == { :p_to => "abc", :p_to_double => "abcabc" }
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should substitute output parameters with nil if they are not specified" do
|
263
|
+
plsql.test_copy("abc").should == { :p_to => "abc", :p_to_double => "abcabc" }
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should substitute named output parameters with nil if they are not specified" do
|
267
|
+
plsql.test_copy(:p_from => "abc").should == { :p_to => "abc", :p_to_double => "abcabc" }
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
describe "Package with procedures with same name but different argument lists" do
|
273
|
+
before(:all) do
|
274
|
+
plsql.connect! CONNECTION_PARAMS
|
275
|
+
plsql.execute <<-SQL
|
276
|
+
CREATE OR REPLACE PACKAGE test_package2 IS
|
277
|
+
FUNCTION test_procedure ( p_string VARCHAR2 )
|
278
|
+
RETURN VARCHAR2;
|
279
|
+
PROCEDURE test_procedure ( p_string VARCHAR2, p_result OUT VARCHAR2 )
|
280
|
+
;
|
281
|
+
PROCEDURE test_procedure ( p_number NUMBER, p_result OUT VARCHAR2 )
|
282
|
+
;
|
283
|
+
FUNCTION test_procedure2 ( p_string VARCHAR2 )
|
284
|
+
RETURN VARCHAR2;
|
285
|
+
FUNCTION test_function ( p_string VARCHAR2, p_string2 VARCHAR2 DEFAULT ' ')
|
286
|
+
RETURN VARCHAR2;
|
287
|
+
FUNCTION test_function ( p_number NUMBER, p_number2 NUMBER DEFAULT 1 )
|
288
|
+
RETURN NUMBER;
|
289
|
+
END;
|
290
|
+
SQL
|
291
|
+
plsql.execute <<-SQL
|
292
|
+
CREATE OR REPLACE PACKAGE BODY test_package2 IS
|
293
|
+
FUNCTION test_procedure ( p_string VARCHAR2 )
|
294
|
+
RETURN VARCHAR2
|
295
|
+
IS
|
296
|
+
BEGIN
|
297
|
+
RETURN UPPER(p_string);
|
298
|
+
END test_procedure;
|
299
|
+
PROCEDURE test_procedure ( p_string VARCHAR2, p_result OUT VARCHAR2 )
|
300
|
+
IS
|
301
|
+
BEGIN
|
302
|
+
p_result := UPPER(p_string);
|
303
|
+
END test_procedure;
|
304
|
+
PROCEDURE test_procedure ( p_number NUMBER, p_result OUT VARCHAR2 )
|
305
|
+
IS
|
306
|
+
BEGIN
|
307
|
+
p_result := LOWER(TO_CHAR(p_number));
|
308
|
+
END test_procedure;
|
309
|
+
FUNCTION test_procedure2 ( p_string VARCHAR2 )
|
310
|
+
RETURN VARCHAR2
|
311
|
+
IS
|
312
|
+
BEGIN
|
313
|
+
RETURN UPPER(p_string);
|
314
|
+
END test_procedure2;
|
315
|
+
FUNCTION test_function ( p_string VARCHAR2, p_string2 VARCHAR2)
|
316
|
+
RETURN VARCHAR2
|
317
|
+
IS
|
318
|
+
BEGIN
|
319
|
+
RETURN p_string||p_string2;
|
320
|
+
END;
|
321
|
+
FUNCTION test_function ( p_number NUMBER, p_number2 NUMBER)
|
322
|
+
RETURN NUMBER
|
323
|
+
IS
|
324
|
+
BEGIN
|
325
|
+
RETURN p_number + p_number2;
|
326
|
+
END;
|
327
|
+
END;
|
328
|
+
SQL
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
after(:all) do
|
333
|
+
plsql.execute "DROP PACKAGE test_package2"
|
334
|
+
plsql.logoff
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should find existing package" do
|
338
|
+
PLSQL::Package.find(plsql, :test_package2).should_not be_nil
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should identify overloaded procedure definition" do
|
342
|
+
@procedure = PLSQL::Procedure.find(plsql, :test_procedure, "TEST_PACKAGE2")
|
343
|
+
@procedure.should_not be_nil
|
344
|
+
@procedure.should be_overloaded
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should identify non-overloaded procedure definition" do
|
348
|
+
@procedure = PLSQL::Procedure.find(plsql, :test_procedure2, "TEST_PACKAGE2")
|
349
|
+
@procedure.should_not be_nil
|
350
|
+
@procedure.should_not be_overloaded
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should execute correct procedures based on number of arguments and return correct value" do
|
354
|
+
plsql.test_package2.test_procedure('xxx').should == 'XXX'
|
355
|
+
plsql.test_package2.test_procedure('xxx', nil).should == {:p_result => 'XXX'}
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should execute correct procedures based on number of named arguments and return correct value" do
|
359
|
+
plsql.test_package2.test_procedure(:p_string => 'xxx').should == 'XXX'
|
360
|
+
plsql.test_package2.test_procedure(:p_string => 'xxx', :p_result => nil).should == {:p_result => 'XXX'}
|
361
|
+
end
|
362
|
+
|
363
|
+
it "should raise exception if procedure cannot be found based on number of arguments" do
|
364
|
+
lambda { plsql.test_package2.test_procedure() }.should raise_error(/wrong number or types of arguments/i)
|
365
|
+
end
|
366
|
+
|
367
|
+
it "should find procedure based on types of arguments" do
|
368
|
+
plsql.test_package2.test_procedure(111, nil).should == {:p_result => '111'}
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should find procedure based on names of named arguments" do
|
372
|
+
plsql.test_package2.test_procedure(:p_number => 111, :p_result => nil).should == {:p_result => '111'}
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should find matching procedure based on partial list of named arguments" do
|
376
|
+
plsql.test_package2.test_function(:p_string => 'xxx').should == 'xxx '
|
377
|
+
plsql.test_package2.test_function(:p_number => 1).should == 2
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
381
|
+
|
382
|
+
describe "Function with output parameters" do
|
383
|
+
before(:all) do
|
384
|
+
plsql.connect! CONNECTION_PARAMS
|
385
|
+
plsql.execute <<-SQL
|
386
|
+
CREATE OR REPLACE FUNCTION test_copy_function
|
387
|
+
( p_from VARCHAR2, p_to OUT VARCHAR2, p_to_double OUT VARCHAR2 )
|
388
|
+
RETURN NUMBER
|
389
|
+
IS
|
390
|
+
BEGIN
|
391
|
+
p_to := p_from;
|
392
|
+
p_to_double := p_from || p_from;
|
393
|
+
RETURN LENGTH(p_from);
|
394
|
+
END test_copy_function;
|
395
|
+
SQL
|
396
|
+
end
|
397
|
+
|
398
|
+
after(:all) do
|
399
|
+
plsql.execute "DROP FUNCTION test_copy_function"
|
400
|
+
plsql.logoff
|
401
|
+
end
|
402
|
+
|
403
|
+
it "should return array with return value and hash of output parameters" do
|
404
|
+
plsql.test_copy_function("abc", nil, nil).should == [3, { :p_to => "abc", :p_to_double => "abcabc" }]
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should return array with return value and hash of output parameters when called with named parameters" do
|
408
|
+
plsql.test_copy_function(:p_from => "abc", :p_to => nil, :p_to_double => nil).should ==
|
409
|
+
[3, { :p_to => "abc", :p_to_double => "abcabc" }]
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should substitute output parameters with nil if they are not specified" do
|
413
|
+
plsql.test_copy_function("abc").should == [3, { :p_to => "abc", :p_to_double => "abcabc" }]
|
414
|
+
end
|
415
|
+
|
416
|
+
end
|
417
|
+
|
418
|
+
describe "Function or procedure without parameters" do
|
419
|
+
before(:all) do
|
420
|
+
plsql.connect! CONNECTION_PARAMS
|
421
|
+
plsql.execute <<-SQL
|
422
|
+
CREATE OR REPLACE FUNCTION test_no_params
|
423
|
+
RETURN VARCHAR2
|
424
|
+
IS
|
425
|
+
BEGIN
|
426
|
+
RETURN 'dummy';
|
427
|
+
END test_no_params;
|
428
|
+
SQL
|
429
|
+
plsql.execute <<-SQL
|
430
|
+
CREATE OR REPLACE PROCEDURE test_proc_no_params
|
431
|
+
IS
|
432
|
+
BEGIN
|
433
|
+
NULL;
|
434
|
+
END test_proc_no_params;
|
435
|
+
SQL
|
436
|
+
end
|
437
|
+
|
438
|
+
after(:all) do
|
439
|
+
plsql.execute "DROP FUNCTION test_no_params"
|
440
|
+
plsql.execute "DROP PROCEDURE test_proc_no_params"
|
441
|
+
plsql.logoff
|
442
|
+
end
|
443
|
+
|
444
|
+
it "should find function" do
|
445
|
+
PLSQL::Procedure.find(plsql, :test_no_params).should_not be_nil
|
446
|
+
end
|
447
|
+
|
448
|
+
it "should return function value" do
|
449
|
+
plsql.test_no_params.should == "dummy"
|
450
|
+
end
|
451
|
+
|
452
|
+
it "should find procedure" do
|
453
|
+
PLSQL::Procedure.find(plsql, :test_proc_no_params).should_not be_nil
|
454
|
+
end
|
455
|
+
|
456
|
+
it "should execute procedure" do
|
457
|
+
plsql.test_proc_no_params.should be_nil
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
|
462
|
+
describe "Function with CLOB parameter and return value" do
|
463
|
+
before(:all) do
|
464
|
+
plsql.connect! CONNECTION_PARAMS
|
465
|
+
plsql.execute <<-SQL
|
466
|
+
CREATE OR REPLACE FUNCTION test_clob
|
467
|
+
( p_clob CLOB )
|
468
|
+
RETURN CLOB
|
469
|
+
IS
|
470
|
+
BEGIN
|
471
|
+
RETURN p_clob;
|
472
|
+
END test_clob;
|
473
|
+
SQL
|
474
|
+
plsql.execute "CREATE TABLE test_clob_table (clob_col CLOB)"
|
475
|
+
plsql.execute <<-SQL
|
476
|
+
CREATE OR REPLACE FUNCTION test_clob_insert
|
477
|
+
( p_clob CLOB )
|
478
|
+
RETURN CLOB
|
479
|
+
IS
|
480
|
+
CURSOR clob_cur IS
|
481
|
+
SELECT clob_col
|
482
|
+
FROM test_clob_table;
|
483
|
+
|
484
|
+
v_dummy CLOB;
|
485
|
+
BEGIN
|
486
|
+
DELETE FROM test_clob_table;
|
487
|
+
INSERT INTO test_clob_table (clob_col) VALUES (p_clob);
|
488
|
+
|
489
|
+
OPEN clob_cur;
|
490
|
+
FETCH clob_cur INTO v_dummy;
|
491
|
+
CLOSE clob_cur;
|
492
|
+
|
493
|
+
RETURN v_dummy;
|
494
|
+
END test_clob_insert;
|
495
|
+
SQL
|
496
|
+
end
|
497
|
+
|
498
|
+
after(:all) do
|
499
|
+
plsql.execute "DROP FUNCTION test_clob"
|
500
|
+
plsql.execute "DROP FUNCTION test_clob_insert"
|
501
|
+
plsql.execute "DROP TABLE test_clob_table"
|
502
|
+
plsql.logoff
|
503
|
+
end
|
504
|
+
|
505
|
+
it "should find existing procedure" do
|
506
|
+
PLSQL::Procedure.find(plsql, :test_clob).should_not be_nil
|
507
|
+
end
|
508
|
+
|
509
|
+
it "should execute function and return correct value" do
|
510
|
+
large_text = 'ābčdēfghij' * 10_000
|
511
|
+
plsql.test_clob(large_text).should == large_text
|
512
|
+
end
|
513
|
+
|
514
|
+
unless defined?(JRUBY_VERSION)
|
515
|
+
|
516
|
+
it "should execute function with empty string and return nil (oci8 cannot pass empty CLOB parameter)" do
|
517
|
+
text = ''
|
518
|
+
plsql.test_clob(text).should be_nil
|
519
|
+
end
|
520
|
+
|
521
|
+
it "should execute function which inserts the CLOB parameter into a table with empty string and return nil" do
|
522
|
+
text = ''
|
523
|
+
plsql.test_clob_insert(text).should be_nil
|
524
|
+
end
|
525
|
+
|
526
|
+
else
|
527
|
+
|
528
|
+
it "should execute function with empty string and return empty string" do
|
529
|
+
text = ''
|
530
|
+
plsql.test_clob(text).should == text
|
531
|
+
end
|
532
|
+
|
533
|
+
end
|
534
|
+
|
535
|
+
it "should execute function with nil and return nil" do
|
536
|
+
plsql.test_clob(nil).should be_nil
|
537
|
+
end
|
538
|
+
|
539
|
+
it "should execute function which inserts the CLOB parameter into a table with nil and return nil" do
|
540
|
+
plsql.test_clob_insert(nil).should be_nil
|
541
|
+
end
|
542
|
+
|
543
|
+
end
|
544
|
+
|
545
|
+
describe "Procedrue with CLOB parameter and return value" do
|
546
|
+
before(:all) do
|
547
|
+
plsql.connect! CONNECTION_PARAMS
|
548
|
+
plsql.execute <<-SQL
|
549
|
+
CREATE OR REPLACE PROCEDURE test_clob_proc
|
550
|
+
( p_clob CLOB,
|
551
|
+
p_return OUT CLOB)
|
552
|
+
IS
|
553
|
+
BEGIN
|
554
|
+
p_return := p_clob;
|
555
|
+
END test_clob_proc;
|
556
|
+
SQL
|
557
|
+
end
|
558
|
+
|
559
|
+
after(:all) do
|
560
|
+
plsql.execute "DROP PROCEDURE test_clob_proc"
|
561
|
+
plsql.logoff
|
562
|
+
end
|
563
|
+
|
564
|
+
it "should find existing procedure" do
|
565
|
+
PLSQL::Procedure.find(plsql, :test_clob_proc).should_not be_nil
|
566
|
+
end
|
567
|
+
|
568
|
+
it "should execute function and return correct value" do
|
569
|
+
large_text = 'ābčdēfghij' * 10_000
|
570
|
+
plsql.test_clob_proc(large_text)[:p_return].should == large_text
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
describe "Procedrue with BLOB parameter and return value" do
|
575
|
+
before(:all) do
|
576
|
+
plsql.connect! CONNECTION_PARAMS
|
577
|
+
plsql.execute <<-SQL
|
578
|
+
CREATE OR REPLACE PROCEDURE test_blob_proc
|
579
|
+
( p_blob BLOB,
|
580
|
+
p_return OUT BLOB)
|
581
|
+
IS
|
582
|
+
BEGIN
|
583
|
+
p_return := p_blob;
|
584
|
+
END test_blob_proc;
|
585
|
+
SQL
|
586
|
+
end
|
587
|
+
|
588
|
+
after(:all) do
|
589
|
+
plsql.execute "DROP PROCEDURE test_blob_proc"
|
590
|
+
plsql.logoff
|
591
|
+
end
|
592
|
+
|
593
|
+
it "should find existing procedure" do
|
594
|
+
PLSQL::Procedure.find(plsql, :test_blob_proc).should_not be_nil
|
595
|
+
end
|
596
|
+
|
597
|
+
it "should execute function and return correct value" do
|
598
|
+
large_binary = '\000\001\002\003\004\005\006\007\010\011' * 10_000
|
599
|
+
plsql.test_blob_proc(large_binary)[:p_return].should == large_binary
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
describe "Function with record parameter" do
|
604
|
+
before(:all) do
|
605
|
+
plsql.connect! CONNECTION_PARAMS
|
606
|
+
plsql.execute "DROP TABLE test_employees" rescue nil
|
607
|
+
plsql.execute <<-SQL
|
608
|
+
CREATE TABLE test_employees (
|
609
|
+
employee_id NUMBER(15),
|
610
|
+
first_name VARCHAR2(50),
|
611
|
+
last_name VARCHAR2(50),
|
612
|
+
hire_date DATE
|
613
|
+
)
|
614
|
+
SQL
|
615
|
+
plsql.execute <<-SQL
|
616
|
+
CREATE OR REPLACE FUNCTION test_full_name (p_employee test_employees%ROWTYPE)
|
617
|
+
RETURN VARCHAR2
|
618
|
+
IS
|
619
|
+
BEGIN
|
620
|
+
RETURN p_employee.first_name || ' ' || p_employee.last_name;
|
621
|
+
END test_full_name;
|
622
|
+
SQL
|
623
|
+
plsql.execute <<-SQL
|
624
|
+
CREATE OR REPLACE PACKAGE test_record IS
|
625
|
+
TYPE t_employee IS RECORD(
|
626
|
+
employee_id NUMBER(15),
|
627
|
+
first_name VARCHAR2(50),
|
628
|
+
last_name VARCHAR2(50),
|
629
|
+
hire_date DATE
|
630
|
+
);
|
631
|
+
|
632
|
+
TYPE table_of_records IS TABLE OF t_employee;
|
633
|
+
|
634
|
+
FUNCTION test_full_name(p_employee t_employee)
|
635
|
+
RETURN VARCHAR2;
|
636
|
+
|
637
|
+
FUNCTION test_empty_records
|
638
|
+
RETURN table_of_records;
|
639
|
+
END;
|
640
|
+
SQL
|
641
|
+
plsql.execute <<-SQL
|
642
|
+
CREATE OR REPLACE PACKAGE BODY test_record IS
|
643
|
+
FUNCTION test_full_name (p_employee t_employee)
|
644
|
+
RETURN VARCHAR2
|
645
|
+
IS
|
646
|
+
BEGIN
|
647
|
+
RETURN p_employee.first_name || ' ' || p_employee.last_name;
|
648
|
+
END;
|
649
|
+
|
650
|
+
FUNCTION test_empty_records
|
651
|
+
RETURN table_of_records
|
652
|
+
IS
|
653
|
+
CURSOR employees_cur
|
654
|
+
IS
|
655
|
+
SELECT
|
656
|
+
null employee_id,
|
657
|
+
null first_name,
|
658
|
+
null last_name,
|
659
|
+
null hire_date
|
660
|
+
FROM dual
|
661
|
+
WHERE 1 = 2;
|
662
|
+
employees_tab table_of_records;
|
663
|
+
BEGIN
|
664
|
+
OPEN employees_cur;
|
665
|
+
FETCH employees_cur BULK COLLECT INTO employees_tab;
|
666
|
+
CLOSE employees_cur;
|
667
|
+
RETURN employees_tab;
|
668
|
+
END;
|
669
|
+
END;
|
670
|
+
SQL
|
671
|
+
plsql.execute <<-SQL
|
672
|
+
CREATE OR REPLACE FUNCTION test_employee_record (p_employee test_employees%ROWTYPE)
|
673
|
+
RETURN test_employees%ROWTYPE
|
674
|
+
IS
|
675
|
+
BEGIN
|
676
|
+
RETURN p_employee;
|
677
|
+
END test_employee_record;
|
678
|
+
SQL
|
679
|
+
plsql.execute <<-SQL
|
680
|
+
CREATE OR REPLACE FUNCTION test_employee_record2 (p_employee test_employees%ROWTYPE, x_employee IN OUT test_employees%ROWTYPE)
|
681
|
+
RETURN test_employees%ROWTYPE
|
682
|
+
IS
|
683
|
+
BEGIN
|
684
|
+
x_employee.employee_id := p_employee.employee_id;
|
685
|
+
x_employee.first_name := p_employee.first_name;
|
686
|
+
x_employee.last_name := p_employee.last_name;
|
687
|
+
x_employee.hire_date := p_employee.hire_date;
|
688
|
+
RETURN p_employee;
|
689
|
+
END test_employee_record2;
|
690
|
+
SQL
|
691
|
+
@p_employee = {
|
692
|
+
:employee_id => 1,
|
693
|
+
:first_name => 'First',
|
694
|
+
:last_name => 'Last',
|
695
|
+
:hire_date => Time.local(2000,01,31)
|
696
|
+
}
|
697
|
+
@p_employee2 = {
|
698
|
+
'employee_id' => 1,
|
699
|
+
'FIRST_NAME' => 'Second',
|
700
|
+
'last_name' => 'Last',
|
701
|
+
'hire_date' => Time.local(2000,01,31)
|
702
|
+
}
|
703
|
+
end
|
704
|
+
|
705
|
+
after(:all) do
|
706
|
+
plsql.execute "DROP FUNCTION test_full_name"
|
707
|
+
plsql.execute "DROP PACKAGE test_record"
|
708
|
+
plsql.execute "DROP FUNCTION test_employee_record"
|
709
|
+
plsql.execute "DROP FUNCTION test_employee_record2"
|
710
|
+
plsql.execute "DROP TABLE test_employees"
|
711
|
+
plsql.logoff
|
712
|
+
end
|
713
|
+
|
714
|
+
it "should find existing function" do
|
715
|
+
PLSQL::Procedure.find(plsql, :test_full_name).should_not be_nil
|
716
|
+
end
|
717
|
+
|
718
|
+
it "should execute function with named parameter and return correct value" do
|
719
|
+
plsql.test_full_name(:p_employee => @p_employee).should == 'First Last'
|
720
|
+
end
|
721
|
+
|
722
|
+
it "should execute function with sequential parameter and return correct value" do
|
723
|
+
plsql.test_full_name(@p_employee).should == 'First Last'
|
724
|
+
end
|
725
|
+
|
726
|
+
it "should execute function with Hash parameter using strings as keys" do
|
727
|
+
plsql.test_full_name(@p_employee2).should == 'Second Last'
|
728
|
+
end
|
729
|
+
|
730
|
+
it "should raise error if wrong field name is passed for record parameter" do
|
731
|
+
lambda do
|
732
|
+
plsql.test_full_name(@p_employee.merge :xxx => 'xxx').should == 'Second Last'
|
733
|
+
end.should raise_error(ArgumentError)
|
734
|
+
end
|
735
|
+
|
736
|
+
it "should return empty table of records" do
|
737
|
+
plsql.test_record.test_empty_records().should == []
|
738
|
+
end
|
739
|
+
|
740
|
+
it "should return record return value" do
|
741
|
+
plsql.test_employee_record(@p_employee).should == @p_employee
|
742
|
+
end
|
743
|
+
|
744
|
+
it "should return record return value and output record parameter value" do
|
745
|
+
plsql.test_employee_record2(@p_employee, @p_employee2).should == [@p_employee, {:x_employee => @p_employee}]
|
746
|
+
end
|
747
|
+
|
748
|
+
it "should execute package function with parameter with record type defined in package" do
|
749
|
+
plsql.test_record.test_full_name(@p_employee).should == 'First Last'
|
750
|
+
end
|
751
|
+
|
752
|
+
end
|
753
|
+
|
754
|
+
describe "Function with boolean parameters" do
|
755
|
+
before(:all) do
|
756
|
+
plsql.connect! CONNECTION_PARAMS
|
757
|
+
plsql.execute <<-SQL
|
758
|
+
CREATE OR REPLACE FUNCTION test_boolean
|
759
|
+
( p_boolean BOOLEAN )
|
760
|
+
RETURN BOOLEAN
|
761
|
+
IS
|
762
|
+
BEGIN
|
763
|
+
RETURN p_boolean;
|
764
|
+
END test_boolean;
|
765
|
+
SQL
|
766
|
+
plsql.execute <<-SQL
|
767
|
+
CREATE OR REPLACE PROCEDURE test_boolean2
|
768
|
+
( p_boolean BOOLEAN, x_boolean OUT BOOLEAN )
|
769
|
+
IS
|
770
|
+
BEGIN
|
771
|
+
x_boolean := p_boolean;
|
772
|
+
END test_boolean2;
|
773
|
+
SQL
|
774
|
+
end
|
775
|
+
|
776
|
+
after(:all) do
|
777
|
+
plsql.execute "DROP FUNCTION test_boolean"
|
778
|
+
plsql.execute "DROP PROCEDURE test_boolean2"
|
779
|
+
plsql.logoff
|
780
|
+
end
|
781
|
+
|
782
|
+
it "should accept true value and return true value" do
|
783
|
+
plsql.test_boolean(true).should == true
|
784
|
+
end
|
785
|
+
|
786
|
+
it "should accept false value and return false value" do
|
787
|
+
plsql.test_boolean(false).should == false
|
788
|
+
end
|
789
|
+
|
790
|
+
it "should accept nil value and return nil value" do
|
791
|
+
plsql.test_boolean(nil).should be_nil
|
792
|
+
end
|
793
|
+
|
794
|
+
it "should accept true value and assign true value to output parameter" do
|
795
|
+
plsql.test_boolean2(true, nil).should == {:x_boolean => true}
|
796
|
+
end
|
797
|
+
|
798
|
+
it "should accept false value and assign false value to output parameter" do
|
799
|
+
plsql.test_boolean2(false, nil).should == {:x_boolean => false}
|
800
|
+
end
|
801
|
+
|
802
|
+
it "should accept nil value and assign nil value to output parameter" do
|
803
|
+
plsql.test_boolean2(nil, nil).should == {:x_boolean => nil}
|
804
|
+
end
|
805
|
+
|
806
|
+
end
|
807
|
+
|
808
|
+
describe "Function with object type parameter" do
|
809
|
+
before(:all) do
|
810
|
+
plsql.connect! CONNECTION_PARAMS
|
811
|
+
plsql.execute "DROP TYPE t_employee" rescue nil
|
812
|
+
plsql.execute "DROP TYPE t_phones" rescue nil
|
813
|
+
plsql.execute <<-SQL
|
814
|
+
CREATE OR REPLACE TYPE t_address AS OBJECT (
|
815
|
+
street VARCHAR2(50),
|
816
|
+
city VARCHAR2(50),
|
817
|
+
country VARCHAR2(50)
|
818
|
+
)
|
819
|
+
SQL
|
820
|
+
plsql.execute <<-SQL
|
821
|
+
CREATE OR REPLACE TYPE t_phone AS OBJECT (
|
822
|
+
type VARCHAR2(10),
|
823
|
+
phone_number VARCHAR2(50)
|
824
|
+
)
|
825
|
+
SQL
|
826
|
+
plsql.execute <<-SQL
|
827
|
+
CREATE OR REPLACE TYPE t_phones AS TABLE OF T_PHONE
|
828
|
+
SQL
|
829
|
+
plsql.execute <<-SQL
|
830
|
+
CREATE OR REPLACE TYPE t_employee AS OBJECT (
|
831
|
+
employee_id NUMBER(15),
|
832
|
+
first_name VARCHAR2(50),
|
833
|
+
last_name VARCHAR2(50),
|
834
|
+
hire_date DATE,
|
835
|
+
address t_address,
|
836
|
+
phones t_phones
|
837
|
+
)
|
838
|
+
SQL
|
839
|
+
plsql.execute <<-SQL
|
840
|
+
CREATE OR REPLACE FUNCTION test_full_name (p_employee t_employee)
|
841
|
+
RETURN VARCHAR2
|
842
|
+
IS
|
843
|
+
BEGIN
|
844
|
+
RETURN p_employee.first_name || ' ' || p_employee.last_name;
|
845
|
+
END;
|
846
|
+
SQL
|
847
|
+
plsql.execute <<-SQL
|
848
|
+
CREATE OR REPLACE FUNCTION test_employee_object (p_employee t_employee)
|
849
|
+
RETURN t_employee
|
850
|
+
IS
|
851
|
+
BEGIN
|
852
|
+
RETURN p_employee;
|
853
|
+
END;
|
854
|
+
SQL
|
855
|
+
plsql.execute <<-SQL
|
856
|
+
CREATE OR REPLACE FUNCTION test_employee_object2 (p_employee t_employee, x_employee OUT t_employee)
|
857
|
+
RETURN t_employee
|
858
|
+
IS
|
859
|
+
BEGIN
|
860
|
+
x_employee := p_employee;
|
861
|
+
RETURN p_employee;
|
862
|
+
END;
|
863
|
+
SQL
|
864
|
+
@p_employee = {
|
865
|
+
:employee_id => 1,
|
866
|
+
:first_name => 'First',
|
867
|
+
:last_name => 'Last',
|
868
|
+
:hire_date => Time.local(2000,01,31),
|
869
|
+
:address => {:street => 'Main street 1', :city => 'Riga', :country => 'Latvia'},
|
870
|
+
:phones => [{:type => 'mobile', :phone_number => '123456'}, {:type => 'home', :phone_number => '654321'}]
|
871
|
+
}
|
872
|
+
end
|
873
|
+
|
874
|
+
after(:all) do
|
875
|
+
plsql.execute "DROP FUNCTION test_full_name"
|
876
|
+
plsql.execute "DROP FUNCTION test_employee_object"
|
877
|
+
plsql.execute "DROP FUNCTION test_employee_object2"
|
878
|
+
plsql.execute "DROP TYPE t_employee"
|
879
|
+
plsql.execute "DROP TYPE t_address"
|
880
|
+
plsql.execute "DROP TYPE t_phones"
|
881
|
+
plsql.execute "DROP TYPE t_phone"
|
882
|
+
plsql.logoff
|
883
|
+
end
|
884
|
+
|
885
|
+
it "should find existing function" do
|
886
|
+
PLSQL::Procedure.find(plsql, :test_full_name).should_not be_nil
|
887
|
+
end
|
888
|
+
|
889
|
+
it "should execute function with named parameter and return correct value" do
|
890
|
+
plsql.test_full_name(:p_employee => @p_employee).should == 'First Last'
|
891
|
+
end
|
892
|
+
|
893
|
+
it "should execute function with sequential parameter and return correct value" do
|
894
|
+
plsql.test_full_name(@p_employee).should == 'First Last'
|
895
|
+
end
|
896
|
+
|
897
|
+
it "should raise error if wrong field name is passed for record parameter" do
|
898
|
+
lambda do
|
899
|
+
plsql.test_full_name(@p_employee.merge :xxx => 'xxx')
|
900
|
+
end.should raise_error(ArgumentError)
|
901
|
+
end
|
902
|
+
|
903
|
+
it "should return object type return value" do
|
904
|
+
plsql.test_employee_object(@p_employee).should == @p_employee
|
905
|
+
end
|
906
|
+
|
907
|
+
it "should return object type return value and output object type parameter value" do
|
908
|
+
plsql.test_employee_object2(@p_employee, nil).should == [@p_employee, {:x_employee => @p_employee}]
|
909
|
+
end
|
910
|
+
|
911
|
+
it "should accept NULL as input parameter" do
|
912
|
+
plsql.test_employee_object(nil).should == nil
|
913
|
+
end
|
914
|
+
|
915
|
+
end
|
916
|
+
|
917
|
+
|
918
|
+
describe "Function with table parameter" do
|
919
|
+
before(:all) do
|
920
|
+
plsql.connect! CONNECTION_PARAMS
|
921
|
+
# Array of numbers
|
922
|
+
plsql.execute <<-SQL
|
923
|
+
CREATE OR REPLACE TYPE t_numbers AS TABLE OF NUMBER(15)
|
924
|
+
SQL
|
925
|
+
plsql.execute <<-SQL
|
926
|
+
CREATE OR REPLACE FUNCTION test_sum (p_numbers IN t_numbers)
|
927
|
+
RETURN NUMBER
|
928
|
+
IS
|
929
|
+
l_sum NUMBER(15) := 0;
|
930
|
+
BEGIN
|
931
|
+
IF p_numbers.COUNT > 0 THEN
|
932
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
933
|
+
IF p_numbers.EXISTS(i) THEN
|
934
|
+
l_sum := l_sum + p_numbers(i);
|
935
|
+
END IF;
|
936
|
+
END LOOP;
|
937
|
+
RETURN l_sum;
|
938
|
+
ELSE
|
939
|
+
RETURN NULL;
|
940
|
+
END IF;
|
941
|
+
END;
|
942
|
+
SQL
|
943
|
+
|
944
|
+
plsql.execute <<-SQL
|
945
|
+
CREATE OR REPLACE FUNCTION test_increment(p_numbers IN t_numbers, p_increment_by IN NUMBER DEFAULT 1)
|
946
|
+
RETURN t_numbers
|
947
|
+
IS
|
948
|
+
l_numbers t_numbers := t_numbers();
|
949
|
+
BEGIN
|
950
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
951
|
+
IF p_numbers.EXISTS(i) THEN
|
952
|
+
l_numbers.EXTEND;
|
953
|
+
l_numbers(i) := p_numbers(i) + p_increment_by;
|
954
|
+
END IF;
|
955
|
+
END LOOP;
|
956
|
+
RETURN l_numbers;
|
957
|
+
END;
|
958
|
+
SQL
|
959
|
+
|
960
|
+
# Array of strings
|
961
|
+
plsql.execute <<-SQL
|
962
|
+
CREATE OR REPLACE TYPE t_strings AS TABLE OF VARCHAR2(4000)
|
963
|
+
SQL
|
964
|
+
plsql.execute <<-SQL
|
965
|
+
CREATE OR REPLACE FUNCTION test_copy_strings(p_strings IN t_strings, x_strings OUT t_strings)
|
966
|
+
RETURN t_strings
|
967
|
+
IS
|
968
|
+
BEGIN
|
969
|
+
x_strings := t_strings();
|
970
|
+
FOR i IN p_strings.FIRST..p_strings.LAST LOOP
|
971
|
+
IF p_strings.EXISTS(i) THEN
|
972
|
+
x_strings.EXTEND;
|
973
|
+
x_strings(i) := p_strings(i);
|
974
|
+
END IF;
|
975
|
+
END LOOP;
|
976
|
+
RETURN x_strings;
|
977
|
+
END;
|
978
|
+
SQL
|
979
|
+
|
980
|
+
# Type definition inside package
|
981
|
+
plsql.execute <<-SQL
|
982
|
+
CREATE OR REPLACE PACKAGE test_collections IS
|
983
|
+
TYPE t_numbers IS TABLE OF NUMBER(15);
|
984
|
+
FUNCTION test_sum (p_numbers IN t_numbers)
|
985
|
+
RETURN NUMBER;
|
986
|
+
FUNCTION test_function_failure (p_numbers IN t_numbers, p_force_failure IN VARCHAR2 DEFAULT 'N')
|
987
|
+
RETURN NUMBER;
|
988
|
+
FUNCTION test_numbers (p_numbers IN t_numbers, x_numbers OUT t_numbers)
|
989
|
+
RETURN t_numbers;
|
990
|
+
TYPE t_employee IS RECORD(
|
991
|
+
employee_id NUMBER(15),
|
992
|
+
first_name VARCHAR2(50),
|
993
|
+
last_name VARCHAR2(50),
|
994
|
+
hire_date DATE
|
995
|
+
);
|
996
|
+
TYPE t_employees IS TABLE OF t_employee;
|
997
|
+
FUNCTION test_employees (p_employees IN OUT t_employees)
|
998
|
+
RETURN t_employees;
|
999
|
+
-- these types with tables in lower level are not yet supported
|
1000
|
+
TYPE t_employee2 IS RECORD(
|
1001
|
+
employee_id NUMBER(15),
|
1002
|
+
first_name VARCHAR2(50),
|
1003
|
+
last_name VARCHAR2(50),
|
1004
|
+
hire_date DATE,
|
1005
|
+
numbers t_numbers
|
1006
|
+
);
|
1007
|
+
FUNCTION test_employee2 (p_employee IN OUT t_employee2)
|
1008
|
+
RETURN t_employee2;
|
1009
|
+
TYPE t_employees2 IS TABLE OF t_employee2;
|
1010
|
+
FUNCTION test_employees2 (p_employees IN OUT t_employees2)
|
1011
|
+
RETURN t_employees2;
|
1012
|
+
|
1013
|
+
TYPE t_nstring IS RECORD(
|
1014
|
+
ch_10bytes CHAR(10 BYTE),
|
1015
|
+
ch_10chars CHAR(10 CHAR),
|
1016
|
+
nch_10chars NCHAR(10 CHAR),
|
1017
|
+
str_10bytes VARCHAR2(10 BYTE),
|
1018
|
+
str_10chars VARCHAR2(10 CHAR),
|
1019
|
+
nstr_10chars NVARCHAR2(10)
|
1020
|
+
);
|
1021
|
+
TYPE t_nstrings IS TABLE OF t_nstring;
|
1022
|
+
FUNCTION test_nstring (p_strings IN t_nstrings, p_out OUT t_nstrings)
|
1023
|
+
return NVARCHAR2;
|
1024
|
+
END;
|
1025
|
+
SQL
|
1026
|
+
plsql.execute <<-SQL
|
1027
|
+
CREATE OR REPLACE PACKAGE BODY test_collections IS
|
1028
|
+
FUNCTION test_sum (p_numbers IN t_numbers)
|
1029
|
+
RETURN NUMBER
|
1030
|
+
IS
|
1031
|
+
l_sum NUMBER(15) := 0;
|
1032
|
+
BEGIN
|
1033
|
+
IF p_numbers.COUNT > 0 THEN
|
1034
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
1035
|
+
IF p_numbers.EXISTS(i) THEN
|
1036
|
+
l_sum := l_sum + p_numbers(i);
|
1037
|
+
END IF;
|
1038
|
+
END LOOP;
|
1039
|
+
RETURN l_sum;
|
1040
|
+
ELSE
|
1041
|
+
RETURN NULL;
|
1042
|
+
END IF;
|
1043
|
+
END;
|
1044
|
+
FUNCTION test_function_failure (p_numbers IN t_numbers, p_force_failure IN VARCHAR2 DEFAULT 'N')
|
1045
|
+
RETURN NUMBER
|
1046
|
+
IS
|
1047
|
+
BEGIN
|
1048
|
+
IF p_force_failure = 'Y' THEN
|
1049
|
+
raise_application_error(-20000, 'Simulate business error to test clearing of temp table.');
|
1050
|
+
END IF;
|
1051
|
+
RETURN p_numbers.COUNT;
|
1052
|
+
END;
|
1053
|
+
FUNCTION test_numbers (p_numbers IN t_numbers, x_numbers OUT t_numbers)
|
1054
|
+
RETURN t_numbers
|
1055
|
+
IS
|
1056
|
+
BEGIN
|
1057
|
+
x_numbers := p_numbers;
|
1058
|
+
RETURN p_numbers;
|
1059
|
+
END;
|
1060
|
+
FUNCTION test_employees (p_employees IN OUT t_employees)
|
1061
|
+
RETURN t_employees
|
1062
|
+
IS
|
1063
|
+
BEGIN
|
1064
|
+
RETURN p_employees;
|
1065
|
+
END;
|
1066
|
+
FUNCTION test_employee2 (p_employee IN OUT t_employee2)
|
1067
|
+
RETURN t_employee2
|
1068
|
+
IS
|
1069
|
+
BEGIN
|
1070
|
+
RETURN p_employee;
|
1071
|
+
END;
|
1072
|
+
FUNCTION test_employees2 (p_employees IN OUT t_employees2)
|
1073
|
+
RETURN t_employees2
|
1074
|
+
IS
|
1075
|
+
BEGIN
|
1076
|
+
RETURN p_employees;
|
1077
|
+
END;
|
1078
|
+
FUNCTION test_nstring (p_strings IN t_nstrings, p_out OUT t_nstrings)
|
1079
|
+
return NVARCHAR2
|
1080
|
+
IS
|
1081
|
+
tmp1 NVARCHAR2(2000);
|
1082
|
+
x pls_integer;
|
1083
|
+
BEGIN
|
1084
|
+
p_out := p_strings;
|
1085
|
+
IF p_strings.count=0 THEN
|
1086
|
+
RETURN N'';
|
1087
|
+
END IF;
|
1088
|
+
x := p_strings.first;
|
1089
|
+
WHILE x IS NOT NULL LOOP
|
1090
|
+
tmp1 := tmp1 || rtrim(p_strings(x).nch_10chars) || p_strings(x).nstr_10chars || ',';
|
1091
|
+
x := p_strings.next(x);
|
1092
|
+
END LOOP;
|
1093
|
+
RETURN tmp1;
|
1094
|
+
END;
|
1095
|
+
END;
|
1096
|
+
SQL
|
1097
|
+
@employees = (1..10).map do |i|
|
1098
|
+
{
|
1099
|
+
:employee_id => i,
|
1100
|
+
:first_name => "First #{i}",
|
1101
|
+
:last_name => "Last #{i}",
|
1102
|
+
:hire_date => Time.local(2000,01,i),
|
1103
|
+
}
|
1104
|
+
end
|
1105
|
+
@nstrings = (1..5).map do |i|
|
1106
|
+
{
|
1107
|
+
:ch_10bytes => "Ch #{i}B ",
|
1108
|
+
:ch_10chars => "Ch #{i}C ",
|
1109
|
+
:nch_10chars => "NCh #{i} ",
|
1110
|
+
:str_10bytes => "Str #{i}C",
|
1111
|
+
:str_10chars => "Str #{i}B",
|
1112
|
+
:nstr_10chars => "NStr #{i}",
|
1113
|
+
}
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
# Array of objects
|
1117
|
+
plsql.execute <<-SQL
|
1118
|
+
CREATE OR REPLACE TYPE t_phone AS OBJECT (
|
1119
|
+
type VARCHAR2(10),
|
1120
|
+
phone_number VARCHAR2(50)
|
1121
|
+
)
|
1122
|
+
SQL
|
1123
|
+
plsql.execute <<-SQL
|
1124
|
+
CREATE OR REPLACE TYPE t_phones AS TABLE OF T_PHONE
|
1125
|
+
SQL
|
1126
|
+
plsql.execute <<-SQL
|
1127
|
+
CREATE OR REPLACE FUNCTION test_copy_objects(p_phones IN t_phones, x_phones OUT t_phones)
|
1128
|
+
RETURN t_phones
|
1129
|
+
IS
|
1130
|
+
BEGIN
|
1131
|
+
x_phones := p_phones;
|
1132
|
+
RETURN x_phones;
|
1133
|
+
END;
|
1134
|
+
SQL
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
after(:all) do
|
1138
|
+
plsql.execute "DROP FUNCTION test_sum"
|
1139
|
+
plsql.execute "DROP FUNCTION test_increment"
|
1140
|
+
plsql.execute "DROP FUNCTION test_copy_strings"
|
1141
|
+
plsql.execute "DROP PACKAGE test_collections"
|
1142
|
+
plsql.execute "DROP FUNCTION test_copy_objects"
|
1143
|
+
plsql.execute "DROP TYPE t_numbers"
|
1144
|
+
plsql.execute "DROP TYPE t_strings"
|
1145
|
+
plsql.execute "DROP TYPE t_phones"
|
1146
|
+
plsql.execute "DROP TYPE t_phone"
|
1147
|
+
plsql.connection.drop_session_ruby_temporary_tables
|
1148
|
+
plsql.logoff
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
it "should find existing function" do
|
1152
|
+
PLSQL::Procedure.find(plsql, :test_sum).should_not be_nil
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
it "should execute function with number array parameter" do
|
1156
|
+
plsql.test_sum([1,2,3,4]).should == 10
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
it "should return number array return value" do
|
1160
|
+
plsql.test_increment([1,2,3,4], 1).should == [2,3,4,5]
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
it "should execute function with string array and return string array output parameter" do
|
1164
|
+
strings = ['1','2','3','4']
|
1165
|
+
plsql.test_copy_strings(strings).should == [strings, {:x_strings => strings}]
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
it "should execute function with table of numbers type (defined inside package) parameter" do
|
1169
|
+
plsql.test_collections.test_sum([1,2,3,4]).should == 10
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
it "should clear temporary tables after executing function with table of numbers type even if an error occurs in the package" do
|
1173
|
+
# this should work fine
|
1174
|
+
plsql.test_collections.test_function_failure([1,2,3,4], 'N').should == 4
|
1175
|
+
# we will force a package error here to see if things get cleaned up before the next call
|
1176
|
+
lambda { plsql.test_collections.test_function_failure([1,2,3,4], 'Y') }.should raise_error(/Simulate business error to test clearing of temp table/)
|
1177
|
+
# after the error in the first call temporary tables should be cleared
|
1178
|
+
plsql.test_collections.test_function_failure([5,6,7], 'N').should == 3
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
it "should return table of numbers type (defined inside package)" do
|
1182
|
+
plsql.test_collections.test_numbers([1,2,3,4]).should == [[1,2,3,4], {:x_numbers => [1,2,3,4]}]
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
it "should clear temporary tables after executing function with table of numbers type (defined inside package) parameter" do
|
1186
|
+
plsql.test_collections.test_numbers([1,2,3,4]).should == [[1,2,3,4], {:x_numbers => [1,2,3,4]}]
|
1187
|
+
# after first call temporary tables should be cleared
|
1188
|
+
plsql.test_collections.test_numbers([1,2,3,4]).should == [[1,2,3,4], {:x_numbers => [1,2,3,4]}]
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
it "should clear temporary tables when autocommit with table of numbers type (defined inside package) parameter" do
|
1192
|
+
old_autocommit = plsql.connection.autocommit?
|
1193
|
+
plsql.connection.autocommit = true
|
1194
|
+
numbers_array = (1..400).to_a
|
1195
|
+
plsql.test_collections.test_numbers(numbers_array).should == [numbers_array, {:x_numbers => numbers_array}]
|
1196
|
+
# after first call temporary tables should be cleared
|
1197
|
+
plsql.test_collections.test_numbers(numbers_array).should == [numbers_array, {:x_numbers => numbers_array}]
|
1198
|
+
plsql.connection.autocommit = old_autocommit
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
it "should execute function with table of records type (defined inside package) parameter" do
|
1202
|
+
plsql.test_collections.test_employees(@employees).should == [@employees, {:p_employees => @employees}]
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
it "should execute function with table of records type (defined inside package and includes NVARCHAR columns) parameter" do
|
1206
|
+
plsql.test_collections.test_nstring(@nstrings).should == [(1..5).map{|i| "NCh #{i}NStr #{i},"}.join, {:p_out => @nstrings}]
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
it "should execute function with object array and return object array output parameter" do
|
1210
|
+
phones = [{:type => 'mobile', :phone_number => '123456'}, {:type => 'home', :phone_number => '654321'}]
|
1211
|
+
plsql.test_copy_objects(phones).should == [phones, {:x_phones => phones}]
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
it "should execute function with empty object array" do
|
1215
|
+
phones = []
|
1216
|
+
plsql.test_copy_objects(phones).should == [phones, {:x_phones => phones}]
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
it "should raise error with record parameter that has table as element" do
|
1220
|
+
lambda {
|
1221
|
+
plsql.test_collections.test_employee2(@employees[0]).should == [@employees[0], {:p_employee => @employees[0]}]
|
1222
|
+
}.should raise_error(ArgumentError, /TEST_COLLECTIONS\.T_NUMBERS definition inside package is not supported/)
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
it "should raise error with table of records parameter when record has table as element" do
|
1226
|
+
lambda {
|
1227
|
+
plsql.test_collections.test_employees2(@employees).should == [@employees, {:p_employees => @employees}]
|
1228
|
+
}.should raise_error(ArgumentError, /TEST_COLLECTIONS\.T_NUMBERS definition inside package is not supported/)
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
describe "Function with table indexed by bynary integer parameter" do
|
1234
|
+
before(:all) do
|
1235
|
+
plsql.connect! CONNECTION_PARAMS
|
1236
|
+
plsql.execute <<-SQL
|
1237
|
+
CREATE TABLE test_employees (
|
1238
|
+
employee_id NUMBER(15),
|
1239
|
+
first_name VARCHAR2(50),
|
1240
|
+
last_name VARCHAR2(50),
|
1241
|
+
hire_date DATE
|
1242
|
+
)
|
1243
|
+
SQL
|
1244
|
+
# Type definition inside package
|
1245
|
+
plsql.execute <<-SQL
|
1246
|
+
CREATE OR REPLACE PACKAGE test_collections IS
|
1247
|
+
TYPE t_numbers IS TABLE OF NUMBER(15)
|
1248
|
+
INDEX BY BINARY_INTEGER;
|
1249
|
+
FUNCTION test_sum (p_numbers IN t_numbers)
|
1250
|
+
RETURN NUMBER;
|
1251
|
+
FUNCTION test_function_failure (p_numbers IN t_numbers, p_force_failure IN VARCHAR2 DEFAULT 'N')
|
1252
|
+
RETURN NUMBER;
|
1253
|
+
FUNCTION test_numbers (p_numbers IN t_numbers, x_numbers OUT t_numbers)
|
1254
|
+
RETURN t_numbers;
|
1255
|
+
TYPE t_employee IS RECORD(
|
1256
|
+
employee_id NUMBER(15),
|
1257
|
+
first_name VARCHAR2(50),
|
1258
|
+
last_name VARCHAR2(50),
|
1259
|
+
hire_date DATE
|
1260
|
+
);
|
1261
|
+
TYPE t_employees IS TABLE OF t_employee
|
1262
|
+
INDEX BY BINARY_INTEGER;
|
1263
|
+
FUNCTION test_employees (p_employees IN OUT t_employees)
|
1264
|
+
RETURN t_employees;
|
1265
|
+
PROCEDURE test_employees_prc (p_employees IN OUT t_employees);
|
1266
|
+
PROCEDURE insert_employees(p_employees IN t_employees);
|
1267
|
+
END;
|
1268
|
+
SQL
|
1269
|
+
plsql.execute <<-SQL
|
1270
|
+
CREATE OR REPLACE PACKAGE BODY test_collections IS
|
1271
|
+
FUNCTION test_sum (p_numbers IN t_numbers)
|
1272
|
+
RETURN NUMBER
|
1273
|
+
IS
|
1274
|
+
l_sum NUMBER(15) := 0;
|
1275
|
+
i BINARY_INTEGER;
|
1276
|
+
BEGIN
|
1277
|
+
IF p_numbers.COUNT > 0 THEN
|
1278
|
+
i := p_numbers.FIRST;
|
1279
|
+
LOOP
|
1280
|
+
EXIT WHEN i IS NULL;
|
1281
|
+
l_sum := l_sum + p_numbers(i);
|
1282
|
+
i := p_numbers.NEXT(i);
|
1283
|
+
END LOOP;
|
1284
|
+
RETURN l_sum;
|
1285
|
+
ELSE
|
1286
|
+
RETURN NULL;
|
1287
|
+
END IF;
|
1288
|
+
END;
|
1289
|
+
FUNCTION test_function_failure (p_numbers IN t_numbers, p_force_failure IN VARCHAR2 DEFAULT 'N')
|
1290
|
+
RETURN NUMBER
|
1291
|
+
IS
|
1292
|
+
l_sum NUMBER(15) := 0;
|
1293
|
+
i BINARY_INTEGER;
|
1294
|
+
BEGIN
|
1295
|
+
IF p_force_failure = 'Y' THEN
|
1296
|
+
raise_application_error(-20000, 'Simulate business error to test clearing of temp table.');
|
1297
|
+
END IF;
|
1298
|
+
IF p_numbers.COUNT > 0 THEN
|
1299
|
+
i := p_numbers.FIRST;
|
1300
|
+
LOOP
|
1301
|
+
EXIT WHEN i IS NULL;
|
1302
|
+
l_sum := l_sum + p_numbers(i);
|
1303
|
+
i := p_numbers.NEXT(i);
|
1304
|
+
END LOOP;
|
1305
|
+
RETURN l_sum;
|
1306
|
+
ELSE
|
1307
|
+
RETURN NULL;
|
1308
|
+
END IF;
|
1309
|
+
END;
|
1310
|
+
FUNCTION test_numbers (p_numbers IN t_numbers, x_numbers OUT t_numbers)
|
1311
|
+
RETURN t_numbers
|
1312
|
+
IS
|
1313
|
+
BEGIN
|
1314
|
+
x_numbers := p_numbers;
|
1315
|
+
RETURN p_numbers;
|
1316
|
+
END;
|
1317
|
+
FUNCTION test_employees (p_employees IN OUT t_employees)
|
1318
|
+
RETURN t_employees
|
1319
|
+
IS
|
1320
|
+
BEGIN
|
1321
|
+
RETURN p_employees;
|
1322
|
+
END;
|
1323
|
+
PROCEDURE test_employees_prc (p_employees IN OUT t_employees)
|
1324
|
+
IS
|
1325
|
+
BEGIN
|
1326
|
+
NULL;
|
1327
|
+
END;
|
1328
|
+
PROCEDURE insert_employees(p_employees IN t_employees) IS
|
1329
|
+
BEGIN
|
1330
|
+
FORALL i IN p_employees.FIRST..p_employees.LAST
|
1331
|
+
INSERT INTO test_employees VALUES p_employees(i);
|
1332
|
+
END;
|
1333
|
+
END;
|
1334
|
+
SQL
|
1335
|
+
# test with negative PL/SQL table indexes
|
1336
|
+
@numbers = Hash[*(1..4).map{|i|[-i,i]}.flatten]
|
1337
|
+
@numbers2 = Hash[*(5..7).map{|i|[-i,i]}.flatten]
|
1338
|
+
# test with reversed PL/SQL table indexes
|
1339
|
+
@employees = Hash[*(1..10).map do |i|
|
1340
|
+
[11-i, {
|
1341
|
+
:employee_id => i,
|
1342
|
+
:first_name => "First #{i}",
|
1343
|
+
:last_name => "Last #{i}",
|
1344
|
+
:hire_date => Time.local(2000,01,i)
|
1345
|
+
}]
|
1346
|
+
end.flatten]
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
after(:all) do
|
1350
|
+
plsql.execute "DROP PACKAGE test_collections"
|
1351
|
+
plsql.execute "DROP TABLE test_employees"
|
1352
|
+
plsql.connection.drop_session_ruby_temporary_tables
|
1353
|
+
plsql.logoff
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
it "should clear temporary tables after executing function with index-by table of numbers type even if an error occurs in the package" do
|
1357
|
+
# this should work fine
|
1358
|
+
plsql.test_collections.test_function_failure(@numbers, 'N').should == 10
|
1359
|
+
# we will force a package error here to see if things get cleaned up before the next call
|
1360
|
+
lambda { plsql.test_collections.test_function_failure(@numbers, 'Y') }.should raise_error(/Simulate business error to test clearing of temp table/)
|
1361
|
+
# after the error in the first call temporary tables should be cleared
|
1362
|
+
plsql.test_collections.test_function_failure(@numbers2, 'N').should == 18
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
it "should execute function with index-by table of numbers type (defined inside package) parameter" do
|
1366
|
+
plsql.test_collections.test_sum(@numbers).should == 10
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
it "should return index-by table of numbers type (defined inside package)" do
|
1370
|
+
plsql.test_collections.test_numbers(@numbers).should == [@numbers, {:x_numbers => @numbers}]
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
it "should clear temporary tables when autocommit with index-by table of numbers type (defined inside package) parameter" do
|
1374
|
+
old_autocommit = plsql.connection.autocommit?
|
1375
|
+
plsql.connection.autocommit = true
|
1376
|
+
numbers_hash = Hash[*(1..400).map{|i|[i,i]}.flatten]
|
1377
|
+
plsql.test_collections.test_numbers(numbers_hash).should == [numbers_hash, {:x_numbers => numbers_hash}]
|
1378
|
+
# after first call temporary tables should be cleared
|
1379
|
+
plsql.test_collections.test_numbers(numbers_hash).should == [numbers_hash, {:x_numbers => numbers_hash}]
|
1380
|
+
plsql.connection.autocommit = old_autocommit
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
it "should execute function with index-by table of records type (defined inside package) parameter" do
|
1384
|
+
plsql.test_collections.test_employees(@employees).should == [@employees, {:p_employees => @employees}]
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
it "should execute procedure with index-by table of records type (defined inside package) parameter" do
|
1388
|
+
plsql.test_collections.test_employees_prc(@employees).should == {:p_employees => @employees}
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
it "should create temporary tables in autonomous transaction" do
|
1392
|
+
old_autocommit = plsql.connection.autocommit?
|
1393
|
+
plsql.connection.autocommit = false
|
1394
|
+
plsql.test_employees.insert @employees[1]
|
1395
|
+
# procedure call should not commit initial insert
|
1396
|
+
plsql.test_collections.insert_employees(2=>@employees[2], 3=>@employees[3])
|
1397
|
+
plsql.rollback
|
1398
|
+
plsql.test_employees.all.should be_empty
|
1399
|
+
plsql.connection.autocommit = old_autocommit
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
describe "using Oracle 9.2" do
|
1403
|
+
before(:all) do
|
1404
|
+
# simulate Oracle 9.2 connection
|
1405
|
+
plsql(:oracle_9).connection = get_connection
|
1406
|
+
plsql(:oracle_9).connection.stub!(:database_version).and_return([9, 2, 0, 0])
|
1407
|
+
end
|
1408
|
+
|
1409
|
+
after(:all) do
|
1410
|
+
plsql(:oracle_9).logoff
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
it "should create temporary tables when using Oracle 9.2" do
|
1414
|
+
plsql(:oracle_9).test_collections.test_numbers(@numbers).should == [@numbers, {:x_numbers => @numbers}]
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
end
|
1418
|
+
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
|
1422
|
+
describe "Function with VARRAY parameter" do
|
1423
|
+
before(:all) do
|
1424
|
+
plsql.connect! CONNECTION_PARAMS
|
1425
|
+
# Array of numbers
|
1426
|
+
plsql.execute <<-SQL
|
1427
|
+
CREATE OR REPLACE TYPE t_numbers_array AS VARRAY(100) OF NUMBER(15)
|
1428
|
+
SQL
|
1429
|
+
plsql.execute <<-SQL
|
1430
|
+
CREATE OR REPLACE FUNCTION test_sum (p_numbers IN t_numbers_array)
|
1431
|
+
RETURN NUMBER
|
1432
|
+
IS
|
1433
|
+
l_sum NUMBER(15) := 0;
|
1434
|
+
BEGIN
|
1435
|
+
IF p_numbers.COUNT > 0 THEN
|
1436
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
1437
|
+
l_sum := l_sum + p_numbers(i);
|
1438
|
+
END LOOP;
|
1439
|
+
RETURN l_sum;
|
1440
|
+
ELSE
|
1441
|
+
RETURN NULL;
|
1442
|
+
END IF;
|
1443
|
+
END;
|
1444
|
+
SQL
|
1445
|
+
|
1446
|
+
plsql.execute <<-SQL
|
1447
|
+
CREATE OR REPLACE FUNCTION test_increment(p_numbers IN t_numbers_array, p_increment_by IN NUMBER DEFAULT 1)
|
1448
|
+
RETURN t_numbers_array
|
1449
|
+
IS
|
1450
|
+
l_numbers t_numbers_array := t_numbers_array();
|
1451
|
+
BEGIN
|
1452
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
1453
|
+
l_numbers.EXTEND;
|
1454
|
+
l_numbers(i) := p_numbers(i) + p_increment_by;
|
1455
|
+
END LOOP;
|
1456
|
+
RETURN l_numbers;
|
1457
|
+
END;
|
1458
|
+
SQL
|
1459
|
+
|
1460
|
+
# Array of strings
|
1461
|
+
plsql.execute <<-SQL
|
1462
|
+
CREATE OR REPLACE TYPE t_strings_array AS VARRAY(100) OF VARCHAR2(4000)
|
1463
|
+
SQL
|
1464
|
+
plsql.execute <<-SQL
|
1465
|
+
CREATE OR REPLACE FUNCTION test_copy_strings(p_strings IN t_strings_array, x_strings OUT t_strings_array)
|
1466
|
+
RETURN t_strings_array
|
1467
|
+
IS
|
1468
|
+
BEGIN
|
1469
|
+
x_strings := t_strings_array();
|
1470
|
+
FOR i IN p_strings.FIRST..p_strings.LAST LOOP
|
1471
|
+
x_strings.EXTEND;
|
1472
|
+
x_strings(i) := p_strings(i);
|
1473
|
+
END LOOP;
|
1474
|
+
RETURN x_strings;
|
1475
|
+
END;
|
1476
|
+
SQL
|
1477
|
+
|
1478
|
+
# Array of objects
|
1479
|
+
plsql.execute "DROP TYPE t_phones_array" rescue nil
|
1480
|
+
plsql.execute <<-SQL
|
1481
|
+
CREATE OR REPLACE TYPE t_phone AS OBJECT (
|
1482
|
+
type VARCHAR2(10),
|
1483
|
+
phone_number VARCHAR2(50)
|
1484
|
+
)
|
1485
|
+
SQL
|
1486
|
+
plsql.execute <<-SQL
|
1487
|
+
CREATE OR REPLACE TYPE t_phones_array AS ARRAY(100) OF T_PHONE
|
1488
|
+
SQL
|
1489
|
+
plsql.execute <<-SQL
|
1490
|
+
CREATE OR REPLACE FUNCTION test_copy_objects(p_phones IN t_phones_array, x_phones OUT t_phones_array)
|
1491
|
+
RETURN t_phones_array
|
1492
|
+
IS
|
1493
|
+
BEGIN
|
1494
|
+
x_phones := p_phones;
|
1495
|
+
RETURN x_phones;
|
1496
|
+
END;
|
1497
|
+
SQL
|
1498
|
+
end
|
1499
|
+
|
1500
|
+
after(:all) do
|
1501
|
+
plsql.execute "DROP FUNCTION test_sum"
|
1502
|
+
plsql.execute "DROP FUNCTION test_increment"
|
1503
|
+
plsql.execute "DROP FUNCTION test_copy_strings"
|
1504
|
+
plsql.execute "DROP FUNCTION test_copy_objects"
|
1505
|
+
plsql.execute "DROP TYPE t_numbers_array"
|
1506
|
+
plsql.execute "DROP TYPE t_strings_array"
|
1507
|
+
plsql.execute "DROP TYPE t_phones_array"
|
1508
|
+
plsql.execute "DROP TYPE t_phone"
|
1509
|
+
plsql.logoff
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
it "should find existing function" do
|
1513
|
+
PLSQL::Procedure.find(plsql, :test_sum).should_not be_nil
|
1514
|
+
end
|
1515
|
+
|
1516
|
+
it "should execute function with number array parameter" do
|
1517
|
+
plsql.test_sum([1,2,3,4]).should == 10
|
1518
|
+
end
|
1519
|
+
|
1520
|
+
it "should return number array return value" do
|
1521
|
+
plsql.test_increment([1,2,3,4], 1).should == [2,3,4,5]
|
1522
|
+
end
|
1523
|
+
|
1524
|
+
it "should execute function with string array and return string array output parameter" do
|
1525
|
+
strings = ['1','2','3','4']
|
1526
|
+
plsql.test_copy_strings(strings).should == [strings, {:x_strings => strings}]
|
1527
|
+
end
|
1528
|
+
|
1529
|
+
it "should execute function with object array and return object array output parameter" do
|
1530
|
+
phones = [{:type => 'mobile', :phone_number => '123456'}, {:type => 'home', :phone_number => '654321'}]
|
1531
|
+
plsql.test_copy_objects(phones).should == [phones, {:x_phones => phones}]
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
it "should execute function with empty object array" do
|
1535
|
+
phones = []
|
1536
|
+
plsql.test_copy_objects(phones).should == [phones, {:x_phones => phones}]
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
end
|
1540
|
+
|
1541
|
+
describe "Function in package with VARRAY parameter" do
|
1542
|
+
before(:all) do
|
1543
|
+
plsql.connect! CONNECTION_PARAMS
|
1544
|
+
plsql.execute <<-SQL
|
1545
|
+
CREATE OR REPLACE TYPE t_phone AS OBJECT (
|
1546
|
+
type VARCHAR2(10),
|
1547
|
+
phone_number VARCHAR2(50)
|
1548
|
+
)
|
1549
|
+
SQL
|
1550
|
+
|
1551
|
+
plsql.execute <<-SQL
|
1552
|
+
CREATE OR REPLACE PACKAGE test_collections IS
|
1553
|
+
TYPE t_numbers_array IS VARRAY(100) OF NUMBER(15);
|
1554
|
+
TYPE t_strings_array IS VARRAY(100) OF VARCHAR2(4000);
|
1555
|
+
TYPE t_phones_array IS ARRAY(100) OF T_PHONE;
|
1556
|
+
FUNCTION test_sum (p_numbers IN t_numbers_array)
|
1557
|
+
RETURN NUMBER;
|
1558
|
+
FUNCTION test_function_failure (p_numbers IN t_numbers_array, p_force_failure IN VARCHAR2 DEFAULT 'N')
|
1559
|
+
RETURN NUMBER;
|
1560
|
+
FUNCTION test_increment(p_numbers IN t_numbers_array, p_increment_by IN NUMBER DEFAULT 1)
|
1561
|
+
RETURN t_numbers_array;
|
1562
|
+
FUNCTION test_copy_strings(p_strings IN t_strings_array, x_strings OUT t_strings_array)
|
1563
|
+
RETURN t_strings_array;
|
1564
|
+
FUNCTION test_copy_objects(p_phones IN t_phones_array, x_phones OUT t_phones_array)
|
1565
|
+
RETURN t_phones_array;
|
1566
|
+
END;
|
1567
|
+
SQL
|
1568
|
+
|
1569
|
+
plsql.execute <<-SQL
|
1570
|
+
CREATE OR REPLACE PACKAGE BODY test_collections IS
|
1571
|
+
FUNCTION test_sum (p_numbers IN t_numbers_array)
|
1572
|
+
RETURN NUMBER
|
1573
|
+
IS
|
1574
|
+
l_sum NUMBER(15) := 0;
|
1575
|
+
BEGIN
|
1576
|
+
IF p_numbers.COUNT > 0 THEN
|
1577
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
1578
|
+
l_sum := l_sum + p_numbers(i);
|
1579
|
+
END LOOP;
|
1580
|
+
RETURN l_sum;
|
1581
|
+
ELSE
|
1582
|
+
RETURN NULL;
|
1583
|
+
END IF;
|
1584
|
+
END;
|
1585
|
+
|
1586
|
+
FUNCTION test_function_failure (p_numbers IN t_numbers_array, p_force_failure IN VARCHAR2 DEFAULT 'N')
|
1587
|
+
RETURN NUMBER
|
1588
|
+
IS
|
1589
|
+
l_sum NUMBER(15) := 0;
|
1590
|
+
BEGIN
|
1591
|
+
IF p_force_failure = 'Y' THEN
|
1592
|
+
raise_application_error(-20000, 'Simulate business error to test clearing of temp table.');
|
1593
|
+
END IF;
|
1594
|
+
IF p_numbers.COUNT > 0 THEN
|
1595
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
1596
|
+
l_sum := l_sum + p_numbers(i);
|
1597
|
+
END LOOP;
|
1598
|
+
RETURN l_sum;
|
1599
|
+
ELSE
|
1600
|
+
RETURN NULL;
|
1601
|
+
END IF;
|
1602
|
+
END;
|
1603
|
+
|
1604
|
+
FUNCTION test_increment(p_numbers IN t_numbers_array, p_increment_by IN NUMBER DEFAULT 1)
|
1605
|
+
RETURN t_numbers_array
|
1606
|
+
IS
|
1607
|
+
l_numbers t_numbers_array := t_numbers_array();
|
1608
|
+
BEGIN
|
1609
|
+
FOR i IN p_numbers.FIRST..p_numbers.LAST LOOP
|
1610
|
+
l_numbers.EXTEND;
|
1611
|
+
l_numbers(i) := p_numbers(i) + p_increment_by;
|
1612
|
+
END LOOP;
|
1613
|
+
RETURN l_numbers;
|
1614
|
+
END;
|
1615
|
+
|
1616
|
+
FUNCTION test_copy_strings(p_strings IN t_strings_array, x_strings OUT t_strings_array)
|
1617
|
+
RETURN t_strings_array
|
1618
|
+
IS
|
1619
|
+
BEGIN
|
1620
|
+
x_strings := t_strings_array();
|
1621
|
+
FOR i IN p_strings.FIRST..p_strings.LAST LOOP
|
1622
|
+
x_strings.EXTEND;
|
1623
|
+
x_strings(i) := p_strings(i);
|
1624
|
+
END LOOP;
|
1625
|
+
RETURN x_strings;
|
1626
|
+
END;
|
1627
|
+
|
1628
|
+
FUNCTION test_copy_objects(p_phones IN t_phones_array, x_phones OUT t_phones_array)
|
1629
|
+
RETURN t_phones_array
|
1630
|
+
IS
|
1631
|
+
BEGIN
|
1632
|
+
x_phones := p_phones;
|
1633
|
+
RETURN x_phones;
|
1634
|
+
END;
|
1635
|
+
END;
|
1636
|
+
SQL
|
1637
|
+
end
|
1638
|
+
|
1639
|
+
after(:all) do
|
1640
|
+
plsql.execute "DROP PACKAGE test_collections"
|
1641
|
+
plsql.execute "DROP TYPE t_phone" rescue nil
|
1642
|
+
plsql.logoff
|
1643
|
+
end
|
1644
|
+
|
1645
|
+
it "should execute function with number array parameter" do
|
1646
|
+
plsql.test_collections.test_sum([1,2,3,4]).should == 10
|
1647
|
+
end
|
1648
|
+
|
1649
|
+
it "should clear temporary tables after executing function with varray of numbers type even if an error occurs in the package" do
|
1650
|
+
# this should work fine
|
1651
|
+
plsql.test_collections.test_function_failure([1,2,3,4], 'N').should == 10
|
1652
|
+
# we will force a package error here to see if things get cleaned up before the next call
|
1653
|
+
lambda { plsql.test_collections.test_function_failure([5,6,7], 'Y') }.should raise_error(/Simulate business error to test clearing of temp table/)
|
1654
|
+
# after the error in the first call temporary tables should be cleared
|
1655
|
+
plsql.test_collections.test_function_failure([3,4,5,6], 'N').should == 18
|
1656
|
+
end
|
1657
|
+
|
1658
|
+
it "should return number array return value" do
|
1659
|
+
plsql.test_collections.test_increment([1,2,3,4], 1).should == [2,3,4,5]
|
1660
|
+
end
|
1661
|
+
|
1662
|
+
it "should execute function with string array and return string array output parameter" do
|
1663
|
+
strings = ['1','2','3','4']
|
1664
|
+
plsql.test_collections.test_copy_strings(strings).should == [strings, {:x_strings => strings}]
|
1665
|
+
end
|
1666
|
+
|
1667
|
+
it "should execute function with object array and return object array output parameter" do
|
1668
|
+
phones = [{:type => 'mobile', :phone_number => '123456'}, {:type => 'home', :phone_number => '654321'}]
|
1669
|
+
plsql.test_collections.test_copy_objects(phones).should == [phones, {:x_phones => phones}]
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
# This test fails without wcmatthysen's "Procedure-call Fix." pull request.
|
1673
|
+
# it "should execute function with empty object array" do
|
1674
|
+
# phones = []
|
1675
|
+
# plsql.test_collections.test_copy_objects(phones).should == [phones, {:x_phones => phones}]
|
1676
|
+
# end
|
1677
|
+
|
1678
|
+
end
|
1679
|
+
|
1680
|
+
describe "Function with cursor return value or parameter" do
|
1681
|
+
before(:all) do
|
1682
|
+
plsql.connect! CONNECTION_PARAMS
|
1683
|
+
plsql.execute "DROP TABLE test_employees" rescue nil
|
1684
|
+
plsql.execute <<-SQL
|
1685
|
+
CREATE TABLE test_employees (
|
1686
|
+
employee_id NUMBER(15),
|
1687
|
+
first_name VARCHAR2(50),
|
1688
|
+
last_name VARCHAR2(50),
|
1689
|
+
hire_date DATE
|
1690
|
+
)
|
1691
|
+
SQL
|
1692
|
+
plsql.execute <<-SQL
|
1693
|
+
CREATE OR REPLACE PROCEDURE test_insert_employee(p_employee test_employees%ROWTYPE)
|
1694
|
+
IS
|
1695
|
+
BEGIN
|
1696
|
+
INSERT INTO test_employees
|
1697
|
+
VALUES p_employee;
|
1698
|
+
END;
|
1699
|
+
SQL
|
1700
|
+
plsql.execute <<-SQL
|
1701
|
+
CREATE OR REPLACE FUNCTION test_cursor
|
1702
|
+
RETURN SYS_REFCURSOR
|
1703
|
+
IS
|
1704
|
+
l_cursor SYS_REFCURSOR;
|
1705
|
+
BEGIN
|
1706
|
+
OPEN l_cursor FOR
|
1707
|
+
SELECT * FROM test_employees ORDER BY employee_id;
|
1708
|
+
RETURN l_cursor;
|
1709
|
+
END;
|
1710
|
+
SQL
|
1711
|
+
plsql.execute <<-SQL
|
1712
|
+
CREATE OR REPLACE PROCEDURE test_cursor_out(x_cursor OUT SYS_REFCURSOR)
|
1713
|
+
IS
|
1714
|
+
BEGIN
|
1715
|
+
OPEN x_cursor FOR
|
1716
|
+
SELECT * FROM test_employees ORDER BY employee_id;
|
1717
|
+
END;
|
1718
|
+
SQL
|
1719
|
+
plsql.execute <<-SQL
|
1720
|
+
CREATE OR REPLACE FUNCTION test_cursor_fetch(p_cursor SYS_REFCURSOR)
|
1721
|
+
RETURN test_employees%ROWTYPE
|
1722
|
+
IS
|
1723
|
+
l_record test_employees%ROWTYPE;
|
1724
|
+
BEGIN
|
1725
|
+
FETCH p_cursor INTO l_record;
|
1726
|
+
RETURN l_record;
|
1727
|
+
END;
|
1728
|
+
SQL
|
1729
|
+
@fields = [:employee_id, :first_name, :last_name, :hire_date]
|
1730
|
+
@employees = (1..10).map do |i|
|
1731
|
+
{
|
1732
|
+
:employee_id => i,
|
1733
|
+
:first_name => "First #{i}",
|
1734
|
+
:last_name => "Last #{i}",
|
1735
|
+
:hire_date => Time.local(2000,01,i)
|
1736
|
+
}
|
1737
|
+
end
|
1738
|
+
@employees.each do |e|
|
1739
|
+
plsql.test_insert_employee(e)
|
1740
|
+
end
|
1741
|
+
plsql.connection.commit
|
1742
|
+
end
|
1743
|
+
|
1744
|
+
after(:all) do
|
1745
|
+
plsql.execute "DROP FUNCTION test_cursor"
|
1746
|
+
plsql.execute "DROP PROCEDURE test_cursor_out"
|
1747
|
+
plsql.execute "DROP PROCEDURE test_insert_employee"
|
1748
|
+
plsql.execute "DROP FUNCTION test_cursor_fetch"
|
1749
|
+
plsql.execute "DROP TABLE test_employees"
|
1750
|
+
plsql.logoff
|
1751
|
+
end
|
1752
|
+
|
1753
|
+
it "should find existing function" do
|
1754
|
+
PLSQL::Procedure.find(plsql, :test_cursor).should_not be_nil
|
1755
|
+
end
|
1756
|
+
|
1757
|
+
it "should return cursor and fetch first row" do
|
1758
|
+
plsql.test_cursor do |cursor|
|
1759
|
+
cursor.fetch.should == @fields.map{|f| @employees[0][f]}
|
1760
|
+
end.should be_nil
|
1761
|
+
end
|
1762
|
+
|
1763
|
+
it "should close all returned cursors after block is executed" do
|
1764
|
+
cursor2 = nil
|
1765
|
+
plsql.test_cursor do |cursor|
|
1766
|
+
cursor2 = cursor
|
1767
|
+
end.should be_nil
|
1768
|
+
lambda { cursor2.fetch }.should raise_error
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
it "should not raise error if cursor is closed inside block" do
|
1772
|
+
lambda do
|
1773
|
+
plsql.test_cursor do |cursor|
|
1774
|
+
cursor.close
|
1775
|
+
end
|
1776
|
+
end.should_not raise_error
|
1777
|
+
end
|
1778
|
+
|
1779
|
+
it "should fetch hash from returned cursor" do
|
1780
|
+
plsql.test_cursor do |cursor|
|
1781
|
+
cursor.fetch_hash.should == @employees[0]
|
1782
|
+
end
|
1783
|
+
end
|
1784
|
+
|
1785
|
+
it "should fetch all rows from returned cursor" do
|
1786
|
+
plsql.test_cursor do |cursor|
|
1787
|
+
cursor.fetch_all.should == @employees.map{|e| @fields.map{|f| e[f]}}
|
1788
|
+
end
|
1789
|
+
end
|
1790
|
+
|
1791
|
+
it "should fetch all rows as hash from returned cursor" do
|
1792
|
+
plsql.test_cursor do |cursor|
|
1793
|
+
cursor.fetch_hash_all.should == @employees
|
1794
|
+
end
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
it "should get field names from returned cursor" do
|
1798
|
+
plsql.test_cursor do |cursor|
|
1799
|
+
cursor.fields.should == @fields
|
1800
|
+
end
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
it "should return output parameter with cursor and fetch first row" do
|
1804
|
+
plsql.test_cursor_out do |result|
|
1805
|
+
result[:x_cursor].fetch.should == @fields.map{|f| @employees[0][f]}
|
1806
|
+
end.should be_nil
|
1807
|
+
end
|
1808
|
+
|
1809
|
+
it "should return output parameter with cursor and fetch all rows as hash" do
|
1810
|
+
plsql.test_cursor_out do |result|
|
1811
|
+
result[:x_cursor].fetch_hash_all.should == @employees
|
1812
|
+
end.should be_nil
|
1813
|
+
end
|
1814
|
+
|
1815
|
+
it "should execute function with cursor parameter and return record" do
|
1816
|
+
pending "not possible from JDBC" if defined?(JRUBY_VERSION)
|
1817
|
+
pending "fails with core dump with ruby-oci8 2.1.0" if OCI8::VERSION >= "2.1.0"
|
1818
|
+
plsql.test_cursor do |cursor|
|
1819
|
+
plsql.test_cursor_fetch(cursor).should == @employees[0]
|
1820
|
+
end
|
1821
|
+
end
|
1822
|
+
|
1823
|
+
end
|
1824
|
+
|
1825
|
+
describe "Function with typed ref cursor return value" do
|
1826
|
+
before(:all) do
|
1827
|
+
plsql.connect! CONNECTION_PARAMS
|
1828
|
+
plsql.execute "DROP TABLE typed_ref_cursor_table" rescue nil
|
1829
|
+
|
1830
|
+
plsql.execute <<-SQL
|
1831
|
+
CREATE TABLE typed_ref_cursor_table
|
1832
|
+
( col1 VARCHAR2(10), col2 NUMBER )
|
1833
|
+
SQL
|
1834
|
+
|
1835
|
+
plsql.execute <<-SQL
|
1836
|
+
CREATE OR REPLACE PACKAGE typed_ref_cursor_test IS
|
1837
|
+
TYPE test_rec IS RECORD ( col1 VARCHAR2(10), col2 NUMBER ) ;
|
1838
|
+
TYPE test_rec_ref IS REF CURSOR RETURN test_rec ;
|
1839
|
+
|
1840
|
+
function get_all RETURN test_rec_ref ;
|
1841
|
+
END typed_ref_cursor_test ;
|
1842
|
+
SQL
|
1843
|
+
|
1844
|
+
plsql.execute <<-SQL
|
1845
|
+
CREATE OR REPLACE PACKAGE BODY typed_ref_cursor_test IS
|
1846
|
+
FUNCTION get_all RETURN test_rec_ref IS
|
1847
|
+
rc test_rec_ref ;
|
1848
|
+
BEGIN
|
1849
|
+
OPEN rc FOR SELECT * FROM typed_ref_cursor_table ;
|
1850
|
+
RETURN rc ;
|
1851
|
+
END get_all ;
|
1852
|
+
END typed_ref_cursor_test ;
|
1853
|
+
SQL
|
1854
|
+
|
1855
|
+
@fields = [:col1, :col2 ]
|
1856
|
+
@rows = (1..3).map{|i| ["row #{i}", i]}
|
1857
|
+
plsql.typed_ref_cursor_table.insert_values *@rows
|
1858
|
+
plsql.commit
|
1859
|
+
|
1860
|
+
end
|
1861
|
+
|
1862
|
+
after(:all) do
|
1863
|
+
plsql.execute "DROP PACKAGE typed_ref_cursor_test"
|
1864
|
+
plsql.execute "DROP TABLE typed_ref_cursor_table"
|
1865
|
+
plsql.logoff
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
it "should return cursor and fetch first row" do
|
1869
|
+
plsql.typed_ref_cursor_test.get_all do |cursor|
|
1870
|
+
cursor.fetch.should == @rows[0]
|
1871
|
+
end.should be_nil
|
1872
|
+
end
|
1873
|
+
|
1874
|
+
it "should fetch hash from returned cursor" do
|
1875
|
+
plsql.typed_ref_cursor_test.get_all do |cursor|
|
1876
|
+
cursor.fetch_hash.should == Hash[*@fields.zip(@rows[0]).flatten]
|
1877
|
+
end
|
1878
|
+
end
|
1879
|
+
|
1880
|
+
it "should fetch all rows from returned cursor" do
|
1881
|
+
plsql.typed_ref_cursor_test.get_all do |cursor|
|
1882
|
+
cursor.fetch_all.should == @rows
|
1883
|
+
end
|
1884
|
+
end
|
1885
|
+
|
1886
|
+
end
|
1887
|
+
|
1888
|
+
end
|
1889
|
+
|
1890
|
+
describe "Synonyms /" do
|
1891
|
+
before(:all) do
|
1892
|
+
plsql.connect! CONNECTION_PARAMS
|
1893
|
+
end
|
1894
|
+
|
1895
|
+
after(:all) do
|
1896
|
+
plsql.logoff
|
1897
|
+
end
|
1898
|
+
|
1899
|
+
describe "Local synonym to function" do
|
1900
|
+
|
1901
|
+
before(:all) do
|
1902
|
+
plsql.execute <<-SQL
|
1903
|
+
CREATE OR REPLACE FUNCTION hr.test_uppercase
|
1904
|
+
( p_string VARCHAR2 )
|
1905
|
+
RETURN VARCHAR2
|
1906
|
+
IS
|
1907
|
+
BEGIN
|
1908
|
+
RETURN UPPER(p_string);
|
1909
|
+
END test_uppercase;
|
1910
|
+
SQL
|
1911
|
+
plsql.execute "CREATE SYNONYM test_synonym FOR hr.test_uppercase"
|
1912
|
+
end
|
1913
|
+
|
1914
|
+
after(:all) do
|
1915
|
+
plsql.execute "DROP SYNONYM test_synonym"
|
1916
|
+
plsql.execute "DROP FUNCTION hr.test_uppercase"
|
1917
|
+
end
|
1918
|
+
|
1919
|
+
it "should find synonym to function" do
|
1920
|
+
PLSQL::Procedure.find(plsql, :test_synonym).should_not be_nil
|
1921
|
+
end
|
1922
|
+
|
1923
|
+
it "should execute function using synonym and return correct value" do
|
1924
|
+
plsql.test_synonym('xxx').should == 'XXX'
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
describe "Public synonym to function" do
|
1930
|
+
|
1931
|
+
before(:all) do
|
1932
|
+
plsql.execute <<-SQL
|
1933
|
+
CREATE OR REPLACE FUNCTION hr.test_ora_login_user
|
1934
|
+
RETURN VARCHAR2
|
1935
|
+
IS
|
1936
|
+
BEGIN
|
1937
|
+
RETURN 'XXX';
|
1938
|
+
END test_ora_login_user;
|
1939
|
+
SQL
|
1940
|
+
end
|
1941
|
+
|
1942
|
+
after(:all) do
|
1943
|
+
plsql.execute "DROP FUNCTION hr.test_ora_login_user"
|
1944
|
+
end
|
1945
|
+
|
1946
|
+
it "should find public synonym to function" do
|
1947
|
+
PLSQL::Procedure.find(plsql, :ora_login_user).should_not be_nil
|
1948
|
+
end
|
1949
|
+
|
1950
|
+
it "should execute function using public synonym and return correct value" do
|
1951
|
+
plsql.ora_login_user.should == 'HR'
|
1952
|
+
end
|
1953
|
+
|
1954
|
+
it "should not find public synonym if schema prefix is used" do
|
1955
|
+
lambda { plsql.hr.ora_login_user }.should raise_error(ArgumentError)
|
1956
|
+
end
|
1957
|
+
|
1958
|
+
it "should find private synonym before public synonym" do
|
1959
|
+
# should reconnect to force clearing of procedure cache
|
1960
|
+
plsql.connection = get_connection
|
1961
|
+
plsql.execute "DROP SYNONYM ora_login_user" rescue nil
|
1962
|
+
plsql.execute "CREATE SYNONYM ora_login_user FOR hr.test_ora_login_user"
|
1963
|
+
plsql.ora_login_user.should == 'XXX'
|
1964
|
+
plsql.execute "DROP SYNONYM ora_login_user"
|
1965
|
+
plsql.connection = get_connection
|
1966
|
+
plsql.ora_login_user.should == 'HR'
|
1967
|
+
end
|
1968
|
+
|
1969
|
+
end
|
1970
|
+
|
1971
|
+
describe "invalid objects" do
|
1972
|
+
before(:all) do
|
1973
|
+
plsql.execute <<-SQL
|
1974
|
+
CREATE OR REPLACE FUNCTION test_invalid_function(p_dummy VARCHAR2) RETURN VARCHAR2 IS
|
1975
|
+
l_dummy invalid_table.invalid_column%TYPE;
|
1976
|
+
BEGIN
|
1977
|
+
RETURN p_dummy;
|
1978
|
+
END;
|
1979
|
+
SQL
|
1980
|
+
plsql.execute <<-SQL
|
1981
|
+
CREATE OR REPLACE PACKAGE test_invalid_package IS
|
1982
|
+
FUNCTION test_invalid_function(p_dummy VARCHAR2) RETURN VARCHAR2;
|
1983
|
+
END;
|
1984
|
+
SQL
|
1985
|
+
plsql.execute <<-SQL
|
1986
|
+
CREATE OR REPLACE PACKAGE BODY test_invalid_package IS
|
1987
|
+
FUNCTION test_invalid_function(p_dummy VARCHAR2) RETURN VARCHAR2 IS
|
1988
|
+
l_dummy1 invalid_table.invalid_column%TYPE;
|
1989
|
+
l_dummy2 invalid_table.invalid_column%TYPE;
|
1990
|
+
BEGIN
|
1991
|
+
RETURN p_dummy;
|
1992
|
+
END;
|
1993
|
+
END;
|
1994
|
+
SQL
|
1995
|
+
end
|
1996
|
+
|
1997
|
+
after(:all) do
|
1998
|
+
plsql.execute "DROP FUNCTION test_invalid_function"
|
1999
|
+
plsql.execute "DROP PACKAGE test_invalid_package"
|
2000
|
+
end
|
2001
|
+
|
2002
|
+
it "should raise error when invalid function is called" do
|
2003
|
+
lambda {
|
2004
|
+
plsql.test_invalid_function('test')
|
2005
|
+
}.should raise_error(ArgumentError, /is not in valid status/)
|
2006
|
+
end
|
2007
|
+
|
2008
|
+
it "should raise error when function from invalid package body is called" do
|
2009
|
+
lambda {
|
2010
|
+
plsql.test_invalid_package.test_invalid_function('test')
|
2011
|
+
}.should raise_error(ArgumentError, /body is not in valid status/)
|
2012
|
+
end
|
2013
|
+
end
|
2014
|
+
|
2015
|
+
end
|
2016
|
+
|
2017
|
+
describe "SYS.STANDARD procedures /" do
|
2018
|
+
|
2019
|
+
before(:all) do
|
2020
|
+
plsql.connect! CONNECTION_PARAMS
|
2021
|
+
end
|
2022
|
+
|
2023
|
+
after(:all) do
|
2024
|
+
plsql.logoff
|
2025
|
+
end
|
2026
|
+
|
2027
|
+
it "should execute function from SYS.STANDARD package" do
|
2028
|
+
plsql.upper('abc').should == 'ABC'
|
2029
|
+
end
|
2030
|
+
|
2031
|
+
it "should find function overload based on types of sequential arguments" do
|
2032
|
+
plsql.nvl(1, 2).should == 1
|
2033
|
+
plsql.nvl(nil, 2).should == 2
|
2034
|
+
plsql.nvl(1.1, 2.2).should == 1.1
|
2035
|
+
plsql.nvl(nil, 2.2).should == 2.2
|
2036
|
+
plsql.nvl(BigDecimal('1.1'), BigDecimal('2.2')).should == BigDecimal('1.1')
|
2037
|
+
plsql.nvl(nil, BigDecimal('2.2')).should == BigDecimal('2.2')
|
2038
|
+
plsql.nvl('a', 'b').should == 'a'
|
2039
|
+
plsql.nvl(nil, 'b').should == 'b'
|
2040
|
+
plsql.nvl(Date.new(2010,1,13), Date.new(2010,1,19)).should == Time.local(2010,1,13)
|
2041
|
+
plsql.nvl(nil, Date.new(2010,1,19)).should == Time.local(2010,1,19)
|
2042
|
+
plsql.nvl(Time.local(2010,1,13), Time.local(2010,1,19)).should == Time.local(2010,1,13)
|
2043
|
+
plsql.nvl(nil, Time.local(2010,1,19)).should == Time.local(2010,1,19)
|
2044
|
+
plsql.nvl(true, false).should == true
|
2045
|
+
plsql.nvl(nil, false).should == false
|
2046
|
+
end
|
2047
|
+
|
2048
|
+
end
|