flash-gordons-ruby-plsql 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/Gemfile +14 -0
- data/History.txt +172 -0
- data/License.txt +20 -0
- data/README.md +182 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/plsql/connection.rb +233 -0
- data/lib/plsql/helpers.rb +9 -0
- data/lib/plsql/jdbc_connection.rb +542 -0
- data/lib/plsql/oci8_patches.rb +25 -0
- data/lib/plsql/oci_connection.rb +339 -0
- data/lib/plsql/package.rb +80 -0
- data/lib/plsql/procedure.rb +269 -0
- data/lib/plsql/procedure_call.rb +124 -0
- data/lib/plsql/schema.rb +309 -0
- data/lib/plsql/sequence.rb +49 -0
- data/lib/plsql/sql_statements.rb +87 -0
- data/lib/plsql/table.rb +348 -0
- data/lib/plsql/type.rb +275 -0
- data/lib/plsql/variable.rb +146 -0
- data/lib/plsql/version.rb +3 -0
- data/lib/plsql/view.rb +41 -0
- data/lib/ruby-plsql.rb +1 -0
- data/lib/ruby_plsql.rb +18 -0
- data/ruby-plsql.gemspec +87 -0
- data/spec/plsql/connection_spec.rb +495 -0
- data/spec/plsql/package_spec.rb +149 -0
- data/spec/plsql/procedure_spec.rb +2048 -0
- data/spec/plsql/schema_spec.rb +331 -0
- data/spec/plsql/sequence_spec.rb +67 -0
- data/spec/plsql/sql_statements_spec.rb +91 -0
- data/spec/plsql/table_spec.rb +371 -0
- data/spec/plsql/type_spec.rb +304 -0
- data/spec/plsql/variable_spec.rb +505 -0
- data/spec/plsql/version_spec.rb +8 -0
- data/spec/plsql/view_spec.rb +264 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +79 -0
- metadata +159 -0
@@ -0,0 +1,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
|