flash-gordons-ruby-plsql 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +14 -0
- data/History.txt +172 -0
- data/License.txt +20 -0
- data/README.md +182 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/plsql/connection.rb +233 -0
- data/lib/plsql/helpers.rb +9 -0
- data/lib/plsql/jdbc_connection.rb +542 -0
- data/lib/plsql/oci8_patches.rb +25 -0
- data/lib/plsql/oci_connection.rb +339 -0
- data/lib/plsql/package.rb +80 -0
- data/lib/plsql/procedure.rb +269 -0
- data/lib/plsql/procedure_call.rb +124 -0
- data/lib/plsql/schema.rb +309 -0
- data/lib/plsql/sequence.rb +49 -0
- data/lib/plsql/sql_statements.rb +87 -0
- data/lib/plsql/table.rb +348 -0
- data/lib/plsql/type.rb +275 -0
- data/lib/plsql/variable.rb +146 -0
- data/lib/plsql/version.rb +3 -0
- data/lib/plsql/view.rb +41 -0
- data/lib/ruby-plsql.rb +1 -0
- data/lib/ruby_plsql.rb +18 -0
- data/ruby-plsql.gemspec +87 -0
- data/spec/plsql/connection_spec.rb +495 -0
- data/spec/plsql/package_spec.rb +149 -0
- data/spec/plsql/procedure_spec.rb +2048 -0
- data/spec/plsql/schema_spec.rb +331 -0
- data/spec/plsql/sequence_spec.rb +67 -0
- data/spec/plsql/sql_statements_spec.rb +91 -0
- data/spec/plsql/table_spec.rb +371 -0
- data/spec/plsql/type_spec.rb +304 -0
- data/spec/plsql/variable_spec.rb +505 -0
- data/spec/plsql/version_spec.rb +8 -0
- data/spec/plsql/view_spec.rb +264 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +79 -0
- metadata +159 -0
@@ -0,0 +1,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
|
data/lib/plsql/view.rb
ADDED
@@ -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
|
data/lib/ruby-plsql.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "ruby_plsql"
|
data/lib/ruby_plsql.rb
ADDED
@@ -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
|
data/ruby-plsql.gemspec
ADDED
@@ -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
|