flash-gordons-ruby-plsql 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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