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