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,146 @@
1
+ module PLSQL
2
+
3
+ module VariableClassMethods #:nodoc:
4
+ def find(schema, variable, package, override_schema_name = nil)
5
+ variable_upcase = variable.to_s.upcase
6
+ schema.select_all(
7
+ "SELECT text FROM all_source
8
+ WHERE owner = :owner
9
+ AND name = :object_name
10
+ AND type = 'PACKAGE'
11
+ AND UPPER(text) LIKE :variable_name",
12
+ override_schema_name || schema.schema_name, package, "%#{variable_upcase}%").each do |row|
13
+ if row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([\w\s,]+\))?)\s*(NOT\s+NULL)?\s*((:=|DEFAULT).*)?;\s*(--.*)?$/i
14
+ return new(schema, variable, package, $2.strip, override_schema_name)
15
+ end
16
+ end
17
+ nil
18
+ end
19
+ end
20
+
21
+ class Variable #:nodoc:
22
+ extend VariableClassMethods
23
+
24
+ attr_reader :schema_name, :package_name, :variable_name #:nodoc:
25
+
26
+ def initialize(schema, variable, package, variable_type, override_schema_name = nil)
27
+ @schema = schema
28
+ @schema_name = override_schema_name || schema.schema_name
29
+ @variable_name = variable.to_s.upcase
30
+ @package_name = package
31
+ @variable_type = variable_type.upcase
32
+ @metadata = metadata(@variable_type)
33
+ end
34
+
35
+ def value
36
+ @variable_get_proc ||= VariableProcedure.new(@schema, self, :get, @metadata)
37
+ ProcedureCall.new(@variable_get_proc).exec
38
+ end
39
+
40
+ def value=(new_value)
41
+ @variable_set_proc ||= VariableProcedure.new(@schema, self, :set, @metadata)
42
+ ProcedureCall.new(@variable_set_proc, [new_value]).exec
43
+ new_value
44
+ end
45
+
46
+ private
47
+
48
+ def metadata(type_string)
49
+ case type_string
50
+ when /^(VARCHAR2|CHAR|NVARCHAR2|NCHAR)(\((\d+)[\s\w]*\))?$/
51
+ {:data_type => $1, :data_length => $3.to_i, :in_out => 'IN/OUT'}
52
+ when /^(CLOB|NCLOB|BLOB)$/,
53
+ /^(NUMBER)(\(.*\))?$/, /^(PLS_INTEGER|BINARY_INTEGER)$/,
54
+ /^(DATE|TIMESTAMP|TIMESTAMP WITH TIME ZONE|TIMESTAMP WITH LOCAL TIME ZONE)$/
55
+ {:data_type => $1, :in_out => 'IN/OUT'}
56
+ when /^INTEGER$/
57
+ {:data_type => 'NUMBER', :in_out => 'IN/OUT'}
58
+ when /^BOOLEAN$/
59
+ {:data_type => 'PL/SQL BOOLEAN', :in_out => 'IN/OUT'}
60
+ when /^(\w+\.)?(\w+)\.(\w+)%TYPE$/
61
+ schema = $1 ? plsql.send($1.chop) : plsql
62
+ table = schema.send($2.downcase.to_sym)
63
+ column = table.columns[$3.downcase.to_sym]
64
+ {:data_type => column[:data_type], :data_length => column[:data_length], :sql_type_name => column[:sql_type_name], :in_out => 'IN/OUT'}
65
+ when /^(\w+\.)?(\w+)$/
66
+ schema = $1 ? @schema.root_schema.send($1.chop) : @schema
67
+ begin
68
+ type = schema.send($2.downcase.to_sym)
69
+ raise ArgumentError unless type.is_a?(PLSQL::Type)
70
+ typecode = case type.typecode
71
+ when 'COLLECTION' then 'TABLE'
72
+ else 'OBJECT'
73
+ end
74
+ {:data_type => typecode, :data_length => nil, :sql_type_name => "#{type.schema_name}.#{type.type_name}", :in_out => 'IN/OUT'}
75
+ rescue ArgumentError
76
+ raise ArgumentError, "Package variable data type #{type_string} is not object type defined in schema"
77
+ end
78
+ when /^(\w+\.)?(\w+)%ROWTYPE$/
79
+ schema = $1 ? plsql.send($1.chop) : plsql
80
+ table = schema.send($2.downcase.to_sym)
81
+ record_metadata = {
82
+ :data_type => 'PL/SQL RECORD',
83
+ :in_out => 'IN/OUT',
84
+ :fields => {}
85
+ }
86
+ table.columns.each do |name, column|
87
+ record_metadata[:fields][name] =
88
+ {:data_type => column[:data_type], :data_length => column[:data_length], :sql_type_name => column[:sql_type_name],
89
+ :position => column[:position], :in_out => 'IN/OUT'}
90
+ end
91
+ record_metadata
92
+ else
93
+ raise ArgumentError, "Package variable data type #{type_string} is not supported"
94
+ end
95
+ end
96
+
97
+ # wrapper class to simulate Procedure class for ProcedureClass#exec
98
+ class VariableProcedure #:nodoc:
99
+ attr_reader :arguments, :argument_list, :return, :out_list, :schema
100
+
101
+ def initialize(schema, variable, operation, metadata)
102
+ @schema = schema
103
+ @variable = variable
104
+ @operation = operation
105
+ @metadata = metadata
106
+
107
+ @out_list = [[]]
108
+
109
+ case @operation
110
+ when :get
111
+ @argument_list = [[]]
112
+ @arguments = [{}]
113
+ @return = [@metadata]
114
+ when :set
115
+ @argument_list = [[:value]]
116
+ @arguments = [{:value => @metadata}]
117
+ @return = [nil]
118
+ end
119
+
120
+ end
121
+
122
+ def overloaded?
123
+ false
124
+ end
125
+
126
+ def procedure
127
+ nil
128
+ end
129
+
130
+ def call_sql(params_string)
131
+ sql = (schema_name = @variable.schema_name) ? "#{schema_name}." : ""
132
+ sql << "#{@variable.package_name}.#{@variable.variable_name}"
133
+ case @operation
134
+ when :get
135
+ # params string contains assignment to return variable
136
+ "#{params_string} #{sql};\n"
137
+ when :set
138
+ "#{sql} := #{params_string};\n"
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+
146
+ end
@@ -0,0 +1,3 @@
1
+ module PLSQL #:nodoc:
2
+ VERSION = File.read(File.dirname(__FILE__)+'/../../VERSION').chomp
3
+ end
@@ -0,0 +1,41 @@
1
+ module PLSQL
2
+
3
+ module ViewClassMethods #:nodoc:
4
+ def find(schema, view)
5
+ if schema.select_first(
6
+ "SELECT view_name FROM all_views
7
+ WHERE owner = :owner
8
+ AND view_name = :view_name",
9
+ schema.schema_name, view.to_s.upcase)
10
+ new(schema, view)
11
+ # search for synonym
12
+ elsif (row = schema.select_first(
13
+ "SELECT v.owner, v.view_name
14
+ FROM all_synonyms s, all_views v
15
+ WHERE s.owner = :owner
16
+ AND s.synonym_name = :synonym_name
17
+ AND v.owner = s.table_owner
18
+ AND v.view_name = s.table_name
19
+ UNION ALL
20
+ SELECT v.owner, v.view_name
21
+ FROM all_synonyms s, all_views v
22
+ WHERE s.owner = 'PUBLIC'
23
+ AND s.synonym_name = :synonym_name
24
+ AND v.owner = s.table_owner
25
+ AND v.view_name = s.table_name",
26
+ schema.schema_name, view.to_s.upcase, view.to_s.upcase))
27
+ new(schema, row[1], row[0])
28
+ else
29
+ nil
30
+ end
31
+ end
32
+ end
33
+
34
+ class View < Table
35
+ extend ViewClassMethods
36
+
37
+ alias :view_name :table_name #:nodoc:
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1 @@
1
+ require "ruby_plsql"
@@ -0,0 +1,18 @@
1
+ require "time"
2
+ require "date"
3
+ require "bigdecimal"
4
+
5
+ %w(connection sql_statements schema
6
+ procedure subprogram_call procedure_call
7
+ pipelined_function pipelined_function_call
8
+ package variable
9
+ table view sequence type
10
+ version helpers).each do |file|
11
+ require "plsql/#{file}"
12
+ end
13
+
14
+ if defined?(JRUBY_VERSION)
15
+ require "plsql/jdbc_connection"
16
+ else
17
+ require "plsql/oci_connection"
18
+ end
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "flash-gordons-ruby-plsql"
8
+ s.version = "0.5.0"
9
+ s.licenses = ['MIT']
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Raimonds Simanovskis"]
12
+ s.date = "2012-04-16"
13
+ s.description = "ruby-plsql gem provides simple Ruby API for calling Oracle PL/SQL procedures.\nIt could be used both for accessing Oracle PL/SQL API procedures in legacy applications\nas well as it could be used to create PL/SQL unit tests using Ruby testing libraries.\n"
14
+ s.email = "raimonds.simanovskis@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ "Gemfile",
20
+ "History.txt",
21
+ "License.txt",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/plsql/connection.rb",
26
+ "lib/plsql/helpers.rb",
27
+ "lib/plsql/jdbc_connection.rb",
28
+ "lib/plsql/oci8_patches.rb",
29
+ "lib/plsql/oci_connection.rb",
30
+ "lib/plsql/package.rb",
31
+ "lib/plsql/procedure.rb",
32
+ "lib/plsql/procedure_call.rb",
33
+ "lib/plsql/schema.rb",
34
+ "lib/plsql/sequence.rb",
35
+ "lib/plsql/sql_statements.rb",
36
+ "lib/plsql/table.rb",
37
+ "lib/plsql/type.rb",
38
+ "lib/plsql/variable.rb",
39
+ "lib/plsql/version.rb",
40
+ "lib/plsql/view.rb",
41
+ "lib/ruby-plsql.rb",
42
+ "lib/ruby_plsql.rb",
43
+ "ruby-plsql.gemspec",
44
+ "spec/plsql/connection_spec.rb",
45
+ "spec/plsql/package_spec.rb",
46
+ "spec/plsql/procedure_spec.rb",
47
+ "spec/plsql/schema_spec.rb",
48
+ "spec/plsql/sequence_spec.rb",
49
+ "spec/plsql/sql_statements_spec.rb",
50
+ "spec/plsql/table_spec.rb",
51
+ "spec/plsql/type_spec.rb",
52
+ "spec/plsql/variable_spec.rb",
53
+ "spec/plsql/version_spec.rb",
54
+ "spec/plsql/view_spec.rb",
55
+ "spec/spec.opts",
56
+ "spec/spec_helper.rb"
57
+ ]
58
+ s.homepage = "http://github.com/rsim/ruby-plsql"
59
+ s.require_paths = ["lib"]
60
+ s.rubygems_version = "1.8.11"
61
+ s.summary = "Ruby API for calling Oracle PL/SQL procedures."
62
+
63
+ if s.respond_to? :specification_version then
64
+ s.specification_version = 3
65
+
66
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
67
+ s.add_development_dependency(%q<jeweler>, ["1.8.3"])
68
+ s.add_development_dependency(%q<rspec>, ["2.9"])
69
+ s.add_development_dependency(%q<activerecord>, ["3.2.3"])
70
+ s.add_development_dependency(%q<activerecord-oracle_enhanced-adapter>, ["1.4.1"])
71
+ s.add_development_dependency(%q<ruby-oci8>, ["2.1.5"])
72
+ else
73
+ s.add_dependency(%q<jeweler>, ["1.8.3"])
74
+ s.add_dependency(%q<rspec>, ["2.9"])
75
+ s.add_dependency(%q<activerecord>, ["3.2.3"])
76
+ s.add_dependency(%q<activerecord-oracle_enhanced-adapter>, ["1.4.1"])
77
+ s.add_dependency(%q<ruby-oci8>, ["2.1.5"])
78
+ end
79
+ else
80
+ s.add_dependency(%q<jeweler>, ["1.8.3"])
81
+ s.add_dependency(%q<rspec>, ["2.9"])
82
+ s.add_dependency(%q<activerecord>, ["3.2.3"])
83
+ s.add_dependency(%q<activerecord-oracle_enhanced-adapter>, ["1.4.1"])
84
+ s.add_dependency(%q<ruby-oci8>, ["2.1.5"])
85
+ end
86
+ end
87
+
@@ -0,0 +1,495 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Connection" do
6
+
7
+ before(:all) do
8
+ @raw_conn = get_connection
9
+ @conn = PLSQL::Connection.create( @raw_conn )
10
+ @conn.set_time_zone
11
+ end
12
+
13
+ after(:all) do
14
+ unless defined?(JRuby)
15
+ @raw_conn.logoff rescue nil
16
+ else
17
+ @raw_conn.close rescue nil
18
+ end
19
+ end
20
+
21
+ describe "create and destroy" do
22
+ before(:all) do
23
+ @raw_conn1 = get_connection
24
+ end
25
+
26
+ before(:each) do
27
+ @conn1 = PLSQL::Connection.create( @raw_conn1 )
28
+ @conn1.set_time_zone
29
+ end
30
+
31
+ it "should create connection" do
32
+ @conn1.raw_connection.should == @raw_conn1
33
+ end
34
+
35
+ unless defined?(JRuby)
36
+ it "should be oci connection" do
37
+ @conn1.should be_oci
38
+ @conn1.raw_driver.should == :oci
39
+ end
40
+ else
41
+ it "should be jdbc connection" do
42
+ @conn1.should be_jdbc
43
+ @conn1.raw_driver.should == :jdbc
44
+ end
45
+ end
46
+
47
+ it "should logoff connection" do
48
+ @conn1.logoff.should be_true
49
+ end
50
+
51
+ end
52
+
53
+ # Ruby 1.8 and 1.9
54
+ unless defined?(JRuby)
55
+ describe "OCI data type conversions" do
56
+ it "should translate PL/SQL VARCHAR2 to Ruby String" do
57
+ @conn.plsql_to_ruby_data_type(:data_type => "VARCHAR2", :data_length => 100).should == [String, 100]
58
+ @conn.plsql_to_ruby_data_type(:data_type => "VARCHAR2", :data_length => nil).should == [String, 32767]
59
+ end
60
+
61
+ it "should translate PL/SQL CLOB to Ruby String" do
62
+ @conn.plsql_to_ruby_data_type(:data_type => "CLOB", :data_length => 100_000).should == [OCI8::CLOB, nil]
63
+ @conn.plsql_to_ruby_data_type(:data_type => "CLOB", :data_length => nil).should == [OCI8::CLOB, nil]
64
+ end
65
+
66
+ it "should translate PL/SQL NUMBER to Ruby OraNumber" do
67
+ @conn.plsql_to_ruby_data_type(:data_type => "NUMBER", :data_length => 15).should == [OraNumber, nil]
68
+ end
69
+
70
+ it "should translate PL/SQL DATE to Ruby DateTime" do
71
+ @conn.plsql_to_ruby_data_type(:data_type => "DATE", :data_length => nil).should == [DateTime, nil]
72
+ end
73
+
74
+ it "should translate PL/SQL TIMESTAMP to Ruby Time" do
75
+ @conn.plsql_to_ruby_data_type(:data_type => "TIMESTAMP", :data_length => nil).should == [Time, nil]
76
+ end
77
+
78
+ it "should not translate Ruby Fixnum when OraNumber type specified" do
79
+ @conn.ruby_value_to_ora_value(100, OraNumber).should eql(100)
80
+ end
81
+
82
+ it "should translate Ruby Bignum value to OraNumber when OraNumber type specified" do
83
+ ora_number = @conn.ruby_value_to_ora_value(12345678901234567890, OraNumber)
84
+ ora_number.class.should == OraNumber
85
+ ora_number.to_s.should == "12345678901234567890"
86
+ # OraNumber has more numeric comparison methods in ruby-oci8 2.0
87
+ ora_number.should == OraNumber.new("12345678901234567890") if OCI8::VERSION >= '2.0.0'
88
+ end
89
+
90
+ it "should translate Ruby String value to OCI8::CLOB when OCI8::CLOB type specified" do
91
+ large_text = "x" * 100_000
92
+ ora_value = @conn.ruby_value_to_ora_value(large_text, OCI8::CLOB)
93
+ ora_value.class.should == OCI8::CLOB
94
+ ora_value.size.should == 100_000
95
+ ora_value.rewind
96
+ ora_value.read.should == large_text
97
+ end
98
+
99
+ it "should translate Oracle OraNumber integer value to Fixnum" do
100
+ @conn.ora_value_to_ruby_value(OraNumber.new(100)).should eql(100)
101
+ end
102
+
103
+ it "should translate Oracle OraNumber float value to BigDecimal" do
104
+ @conn.ora_value_to_ruby_value(OraNumber.new(100.11)).should eql(BigDecimal("100.11"))
105
+ end
106
+
107
+ # ruby-oci8 2.0 returns DATE as Time or DateTime
108
+ if OCI8::VERSION < '2.0.0'
109
+ it "should translate Oracle OraDate value to Time" do
110
+ now = OraDate.now
111
+ @conn.ora_value_to_ruby_value(now).should eql(now.to_time)
112
+ end
113
+ end
114
+
115
+ it "should translate Oracle CLOB value to String" do
116
+ large_text = "x" * 100_000
117
+ clob = OCI8::CLOB.new(@raw_conn, large_text)
118
+ @conn.ora_value_to_ruby_value(clob).should == large_text
119
+ end
120
+
121
+ end
122
+
123
+ # JRuby
124
+ else
125
+
126
+ describe "JDBC data type conversions" do
127
+ it "should translate PL/SQL VARCHAR2 to Ruby String" do
128
+ @conn.plsql_to_ruby_data_type(:data_type => "VARCHAR2", :data_length => 100).should == [String, 100]
129
+ @conn.plsql_to_ruby_data_type(:data_type => "VARCHAR2", :data_length => nil).should == [String, 32767]
130
+ end
131
+
132
+ it "should translate PL/SQL NUMBER to Ruby BigDecimal" do
133
+ @conn.plsql_to_ruby_data_type(:data_type => "NUMBER", :data_length => 15).should == [BigDecimal, nil]
134
+ end
135
+
136
+ it "should translate PL/SQL DATE to Ruby DateTime" do
137
+ @conn.plsql_to_ruby_data_type(:data_type => "DATE", :data_length => nil).should == [DateTime, nil]
138
+ end
139
+
140
+ it "should translate PL/SQL TIMESTAMP to Ruby Time" do
141
+ @conn.plsql_to_ruby_data_type(:data_type => "TIMESTAMP", :data_length => nil).should == [Time, nil]
142
+ end
143
+
144
+ it "should not translate Ruby Fixnum when BigDecimal type specified" do
145
+ @conn.ruby_value_to_ora_value(100, BigDecimal).should == java.math.BigDecimal.new(100)
146
+ end
147
+
148
+ it "should translate Ruby Bignum value to BigDecimal when BigDecimal type specified" do
149
+ big_decimal = @conn.ruby_value_to_ora_value(12345678901234567890, BigDecimal)
150
+ big_decimal.should == java.math.BigDecimal.new("12345678901234567890")
151
+ end
152
+
153
+ it "should translate Ruby String value to Java::OracleSql::CLOB when Java::OracleSql::CLOB type specified" do
154
+ large_text = "x" * 100_000
155
+ ora_value = @conn.ruby_value_to_ora_value(large_text, Java::OracleSql::CLOB)
156
+ ora_value.class.should == Java::OracleSql::CLOB
157
+ ora_value.length.should == 100_000
158
+ ora_value.getSubString(1, ora_value.length) == large_text
159
+ ora_value.freeTemporary
160
+ end
161
+
162
+ it "should translate Ruby nil value to nil when Java::OracleSql::CLOB type specified" do
163
+ ora_value = @conn.ruby_value_to_ora_value(nil, Java::OracleSql::CLOB)
164
+ ora_value.should be_nil
165
+ end
166
+
167
+ it "should translate Oracle BigDecimal integer value to Fixnum" do
168
+ @conn.ora_value_to_ruby_value(BigDecimal("100")).should eql(100)
169
+ end
170
+
171
+ it "should translate Oracle BigDecimal float value to BigDecimal" do
172
+ @conn.ora_value_to_ruby_value(BigDecimal("100.11")).should eql(BigDecimal("100.11"))
173
+ end
174
+
175
+ it "should translate Oracle CLOB value to String" do
176
+ large_text = "āčē" * 100_000
177
+ clob = @conn.ruby_value_to_ora_value(large_text, Java::OracleSql::CLOB)
178
+ @conn.ora_value_to_ruby_value(clob).should == large_text
179
+ clob.freeTemporary
180
+ end
181
+
182
+ it "should translate empty Oracle CLOB value to nil" do
183
+ clob = @conn.ruby_value_to_ora_value(nil, Java::OracleSql::CLOB)
184
+ @conn.ora_value_to_ruby_value(clob).should be_nil
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+ describe "SQL SELECT statements" do
192
+
193
+ it "should execute SQL statement and return first result" do
194
+ @now = Time.local(2008,05,31,23,22,11)
195
+ @conn.select_first("SELECT 'abc',123,123.456,
196
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')
197
+ FROM dual").should == ["abc",123,123.456,@now]
198
+ end
199
+
200
+ it "should execute SQL statement and return first result as hash" do
201
+ @now = Time.local(2008,05,31,23,22,11)
202
+ @conn.select_hash_first("SELECT 'abc' a, 123 b, 123.456 c,
203
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}', 'YYYY-MM-DD HH24:MI:SS') d
204
+ FROM dual").should == {:a => "abc", :b => 123, :c => 123.456, :d => @now}
205
+ end
206
+
207
+ it "should execute SQL statement with bind parameters and return first result" do
208
+ @today = Date.parse("2008-05-31")
209
+ @now = Time.local(2008,05,31,23,22,11)
210
+ @conn.select_first("SELECT :1,:2,:3,:4,:5 FROM dual",
211
+ 'abc',123,123.456,@now,@today).should == ["abc",123,123.456,@now,Time.parse(@today.to_s)]
212
+ end
213
+
214
+ it "should execute SQL statement with NULL values and return first result" do
215
+ @now = Time.local(2008,05,31,23,22,11)
216
+ @conn.select_first("SELECT NULL,123,123.456,
217
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')
218
+ FROM dual").should == [nil,123,123.456,@now]
219
+ end
220
+
221
+ if defined?(JRuby)
222
+
223
+ it "should execute SQL statement with NULL values as bind parameters and return first result" do
224
+ @today = Date.parse("2008-05-31")
225
+ @now = Time.local(2008,05,31,23,22,11)
226
+ @conn.select_first("SELECT :1,:2,:3,:4,:5 FROM dual",
227
+ nil,123,123.456,@now,@today).should == [nil,123,123.456,@now,Time.parse(@today.to_s)]
228
+ end
229
+
230
+ end
231
+
232
+ it "should execute SQL statement and return all results" do
233
+ @now = Time.local(2008,05,31,23,22,11)
234
+ @conn.select_all("SELECT 'abc',123,123.456,
235
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')
236
+ FROM dual
237
+ UNION ALL SELECT 'abc',123,123.456,
238
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')
239
+ FROM dual").should == [["abc",123,123.456,@now],["abc",123,123.456,@now]]
240
+ end
241
+
242
+ it "should execute SQL statement and return all results as hash" do
243
+ @now = Time.local(2008,05,31,23,22,11)
244
+ @conn.select_hash_all("SELECT 'abc' a, 123 b, 123.456 c,
245
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS') d
246
+ FROM dual
247
+ UNION ALL SELECT 'def' a, 123 b, 123.456 c,
248
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS') d
249
+ FROM dual").should == [{:a=>"abc",:b=>123,:c=>123.456,:d=>@now},{:a=>"def",:b=>123,:c=>123.456,:d=>@now}]
250
+ end
251
+
252
+ it "should execute SQL statement with bind parameters and return all results" do
253
+ @now = Time.local(2008,05,31,23,22,11)
254
+ @conn.select_all("SELECT :1,:2,:3,:4 FROM dual UNION ALL SELECT :1,:2,:3,:4 FROM dual",
255
+ 'abc',123,123.456,@now,'abc',123,123.456,@now).should == [["abc",123,123.456,@now],["abc",123,123.456,@now]]
256
+ end
257
+
258
+ it "should execute SQL statement and yield all results in block" do
259
+ @now = Time.local(2008,05,31,23,22,11)
260
+ @conn.select_all("SELECT 'abc',123,123.456,
261
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')
262
+ FROM dual
263
+ UNION ALL SELECT 'abc',123,123.456,
264
+ TO_DATE('#{@now.strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')
265
+ FROM dual") do |r|
266
+ r.should == ["abc",123,123.456,@now]
267
+ end.should == 2
268
+ end
269
+
270
+ it "should execute SQL statement with bind parameters and yield all results in block" do
271
+ @now = Time.local(2008,05,31,23,22,11)
272
+ @conn.select_all("SELECT :1,:2,:3,:4 FROM dual UNION ALL SELECT :1,:2,:3,:4 FROM dual",
273
+ 'abc',123,123.456,@now,'abc',123,123.456,@now) do |r|
274
+ r.should == ["abc",123,123.456,@now]
275
+ end.should == 2
276
+ end
277
+
278
+ end
279
+
280
+ describe "PL/SQL procedures" do
281
+ before(:all) do
282
+ @random = rand(1000)
283
+ @now = Time.local(2008,05,31,23,22,11)
284
+ sql = <<-SQL
285
+ CREATE OR REPLACE FUNCTION test_add_random (p_number NUMBER, p_varchar IN OUT VARCHAR2, p_date IN OUT DATE)
286
+ RETURN NUMBER
287
+ IS
288
+ BEGIN
289
+ RETURN p_number + #{@random};
290
+ END test_add_random;
291
+ SQL
292
+ @conn.exec(sql).should be_true
293
+ end
294
+
295
+ after(:all) do
296
+ @conn.exec "DROP FUNCTION test_add_random"
297
+ end
298
+
299
+ it "should parse PL/SQL procedure call and bind parameters and exec and get bind parameter value" do
300
+ sql = <<-SQL
301
+ BEGIN
302
+ :result := test_add_random (:p_number, :p_varchar, :p_date);
303
+ END;
304
+ SQL
305
+ cursor = @conn.parse(sql)
306
+ cursor.bind_param(":result", nil, :data_type => 'NUMBER', :in_out => 'OUT')
307
+ cursor.bind_param(":p_number", 100, :data_type => 'NUMBER', :in_out => 'IN')
308
+ cursor.bind_param(":p_varchar", "abc", :data_type => 'VARCHAR2', :in_out => 'IN/OUT')
309
+ cursor.bind_param(":p_date", @now, :data_type => 'DATE', :in_out => 'IN/OUT')
310
+ cursor.exec
311
+ cursor[":result"].should == @random + 100
312
+ cursor[":p_varchar"].should == "abc"
313
+ cursor[":p_date"].should == @now
314
+ cursor.close.should be_nil
315
+ end
316
+
317
+ end
318
+
319
+ describe "commit and rollback" do
320
+ before(:all) do
321
+ @conn.exec("CREATE TABLE test_commit (dummy VARCHAR2(100))").should be_true
322
+ @conn.autocommit = false
323
+ @conn.should_not be_autocommit
324
+ end
325
+
326
+ after(:all) do
327
+ @conn.exec "DROP TABLE test_commit"
328
+ end
329
+
330
+ after(:each) do
331
+ @conn.exec "DELETE FROM test_commit"
332
+ @conn.commit
333
+ end
334
+
335
+ it "should do commit" do
336
+ @conn.exec("INSERT INTO test_commit VALUES ('test')")
337
+ @conn.commit
338
+ @conn.select_first("SELECT COUNT(*) FROM test_commit")[0].should == 1
339
+ end
340
+
341
+ it "should do rollback" do
342
+ @conn.exec("INSERT INTO test_commit VALUES ('test')")
343
+ @conn.rollback
344
+ @conn.select_first("SELECT COUNT(*) FROM test_commit")[0].should == 0
345
+ end
346
+
347
+ it "should do commit and rollback should not undo commited transaction" do
348
+ @conn.exec("INSERT INTO test_commit VALUES ('test')")
349
+ @conn.commit
350
+ @conn.rollback
351
+ @conn.select_first("SELECT COUNT(*) FROM test_commit")[0].should == 1
352
+ end
353
+
354
+ end
355
+
356
+ describe "prefetch rows" do
357
+ after(:each) do
358
+ @conn.prefetch_rows = 1 # set back to default
359
+ end
360
+
361
+ it "should set prefetch rows for connection" do
362
+ sql = "SELECT 1 FROM dual UNION ALL SELECT 1/0 FROM dual"
363
+ @conn.prefetch_rows = 2
364
+ lambda {
365
+ @conn.cursor_from_query(sql)
366
+ }.should raise_error(/divisor is equal to zero/)
367
+ @conn.prefetch_rows = 1
368
+ lambda {
369
+ @conn.cursor_from_query(sql)
370
+ }.should_not raise_error
371
+ end
372
+
373
+ it "should fetch just one row when using select_first" do
374
+ sql = "SELECT 1 FROM dual UNION ALL SELECT 1/0 FROM dual"
375
+ @conn.prefetch_rows = 2
376
+ lambda {
377
+ @conn.select_first(sql)
378
+ }.should_not raise_error
379
+ end
380
+
381
+ end
382
+
383
+ describe "describe synonym" do
384
+ before(:all) do
385
+ @conn.exec "CREATE SYNONYM hr.synonym_for_dual FOR sys.dual"
386
+ end
387
+
388
+ after(:all) do
389
+ @conn.exec "DROP SYNONYM hr.synonym_for_dual"
390
+ end
391
+
392
+ it "should describe local synonym" do
393
+ @conn.describe_synonym('HR','SYNONYM_FOR_DUAL').should == ['SYS', 'DUAL']
394
+ @conn.describe_synonym('hr','synonym_for_dual').should == ['SYS', 'DUAL']
395
+ @conn.describe_synonym(:hr,:synonym_for_dual).should == ['SYS', 'DUAL']
396
+ end
397
+
398
+ it "should return nil on non-existing synonym" do
399
+ @conn.describe_synonym('HR','SYNONYM_FOR_XXX').should be_nil
400
+ @conn.describe_synonym('hr','synonym_for_xxx').should be_nil
401
+ @conn.describe_synonym(:hr,:synonym_for_xxx).should be_nil
402
+ end
403
+
404
+ it "should describe public synonym" do
405
+ @conn.describe_synonym('PUBLIC','DUAL').should == ['SYS', 'DUAL']
406
+ @conn.describe_synonym('PUBLIC','dual').should == ['SYS', 'DUAL']
407
+ @conn.describe_synonym('PUBLIC',:dual).should == ['SYS', 'DUAL']
408
+ end
409
+
410
+ end
411
+
412
+ describe "session information" do
413
+ it "should get database version" do
414
+ # using Oracle version 10.2.0.4 for unit tests
415
+ @conn.database_version.should == DATABASE_VERSION.split('.').map{|n| n.to_i}
416
+ end
417
+
418
+ it "should get session ID" do
419
+ @conn.session_id.should == @conn.select_first("SELECT USERENV('SESSIONID') FROM dual")[0].to_i
420
+ end
421
+ end
422
+
423
+ describe "drop ruby temporary tables" do
424
+ after(:all) do
425
+ @conn.drop_all_ruby_temporary_tables
426
+ end
427
+
428
+ it "should drop all ruby temporary tables" do
429
+ tmp_table = "ruby_111_222_333"
430
+ @conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
431
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
432
+ @conn.drop_all_ruby_temporary_tables
433
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should raise_error(/table or view does not exist/)
434
+ end
435
+
436
+ it "should drop current session ruby temporary tables" do
437
+ tmp_table = "ruby_#{@conn.session_id}_222_333"
438
+ @conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
439
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
440
+ @conn.drop_session_ruby_temporary_tables
441
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should raise_error(/table or view does not exist/)
442
+ end
443
+
444
+ it "should not drop other session ruby temporary tables" do
445
+ tmp_table = "ruby_#{@conn.session_id+1}_222_333"
446
+ @conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
447
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
448
+ @conn.drop_session_ruby_temporary_tables
449
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
450
+ end
451
+
452
+ end
453
+
454
+ describe "logoff" do
455
+ before(:each) do
456
+ # restore connection before each test
457
+ reconnect_connection
458
+ end
459
+
460
+ after(:all) do
461
+ @conn.exec "DROP TABLE test_dummy_table" rescue nil
462
+ end
463
+
464
+ def reconnect_connection
465
+ @raw_conn = get_connection
466
+ @conn = PLSQL::Connection.create( @raw_conn )
467
+ @conn.set_time_zone
468
+ end
469
+
470
+ it "should drop current session ruby temporary tables" do
471
+ tmp_table = "ruby_#{@conn.session_id}_222_333"
472
+ @conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
473
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
474
+ @conn.logoff
475
+ reconnect_connection
476
+ lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should raise_error(/table or view does not exist/)
477
+ end
478
+
479
+ it "should rollback any uncommited transactions" do
480
+ tmp_table = "ruby_#{@conn.session_id}_222_333"
481
+ old_autocommit = @conn.autocommit?
482
+ @conn.autocommit = false
483
+ @conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
484
+ @conn.exec "CREATE TABLE test_dummy_table (dummy CHAR(1))"
485
+ @conn.exec "INSERT INTO test_dummy_table VALUES ('1')"
486
+ # logoff will drop ruby temporary tables, it should do rollback before drop table
487
+ @conn.logoff
488
+ reconnect_connection
489
+ @conn.select_first("SELECT * FROM test_dummy_table").should == nil
490
+ @conn.autocommit = old_autocommit
491
+ end
492
+
493
+ end
494
+
495
+ end