ruby-plsql 0.4.4 → 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.
data/Gemfile CHANGED
@@ -1,14 +1,14 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- gem 'jeweler'
4
- gem 'rspec', "~> 1.3.0"
3
+ group :development do
4
+ gem 'jeweler', '~> 1.8.3'
5
+ gem 'rspec', '~> 2.9'
5
6
 
6
- unless ENV['NO_ACTIVERECORD']
7
- # avoid loading activerecord 3.0 beta
8
- gem 'activerecord', '=2.3.8'
9
- gem 'activerecord-oracle_enhanced-adapter', '=1.3.1'
10
- end
7
+ unless ENV['NO_ACTIVERECORD']
8
+ gem 'activerecord', '~> 3.2.3'
9
+ gem 'activerecord-oracle_enhanced-adapter', '~> 1.4.1'
10
+ end
11
11
 
12
- if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
13
- gem 'ruby-oci8', '>=2.0.4'
12
+ # gem 'ruby-oci8', '~> 2.1.0'
13
+ gem 'ruby-oci8', :git => 'git://github.com/kubo/ruby-oci8.git', :platforms => :mri
14
14
  end
data/History.txt CHANGED
@@ -1,3 +1,17 @@
1
+ == 0.5.0 2012-04-16
2
+
3
+ * Improvements
4
+ * Support for ruby-oci8 2.1.0 and ActiveRecord 3.2
5
+ * Tested with Ruby 1.9.3
6
+ * Tests migrated to RSpec 2
7
+ * Use ojdbc6.jar or ojdbc5.jar JDBC drivers when using JRuby
8
+ * Bug fixes
9
+ * Fixed clearing of global temp tables before procedure calls
10
+ * Fixed passing NULL value for CLOB type arguments
11
+ * Fixed procedure call that returns array of records
12
+ * Fixed support for NCHAR and NVARCHAR argument types
13
+ * Accept any ActiveRecord inherited model class for activerecord_class= method
14
+
1
15
  == 0.4.4 2010-10-06
2
16
 
3
17
  * Improvements
data/License.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008-2010 Raimonds Simanovskis
1
+ Copyright (c) 2008-2012 Raimonds Simanovskis
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,182 @@
1
+ ruby-plsql
2
+ ==========
3
+
4
+ Ruby API for calling Oracle PL/SQL procedures.
5
+
6
+ DESCRIPTION
7
+ -----------
8
+
9
+ ruby-plsql gem provides simple Ruby API for calling Oracle PL/SQL procedures. It could be used both for accessing Oracle PL/SQL API procedures in legacy applications as well as it could be used to create PL/SQL unit tests using Ruby testing libraries.
10
+
11
+ NUMBER, BINARY_INTEGER, PLS_INTEGER, VARCHAR2, NVARCHAR2, CHAR, NCHAR, DATE, TIMESTAMP, CLOB, BLOB, BOOLEAN, PL/SQL RECORD, TABLE, VARRAY, OBJECT and CURSOR types are supported for input and output parameters and return values of PL/SQL procedures and functions.
12
+
13
+ ruby-plsql supports Ruby 1.8.7, 1.9.3 and JRuby 1.6.7 implementations.
14
+
15
+ USAGE
16
+ -----
17
+
18
+ ### Calling PL/SQL functions and procedures:
19
+
20
+ ```ruby
21
+ require "rubygems"
22
+ require "ruby-plsql"
23
+
24
+ plsql.connection = OCI8.new("hr","hr","xe")
25
+
26
+ plsql.test_uppercase('xxx') # => "XXX"
27
+ plsql.test_uppercase(:p_string => 'xxx') # => "XXX"
28
+ plsql.test_copy("abc", nil, nil) # => { :p_to => "abc", :p_to_double => "abcabc" }
29
+ plsql.test_copy(:p_from => "abc", :p_to => nil, :p_to_double => nil)
30
+ # => { :p_to => "abc", :p_to_double => "abcabc" }
31
+ plsql.hr.test_uppercase('xxx') # => "XXX"
32
+ plsql.test_package.test_uppercase('xxx') # => 'XXX'
33
+
34
+ # PL/SQL records or object type parameters should be passed as Hash
35
+ p_employee = { :employee_id => 1, :first_name => 'First', :last_name => 'Last', :hire_date => Time.local(2000,01,31) }
36
+ plsql.test_full_name(p_employee)
37
+
38
+ # TABLE or VARRAY parameters should be passed as Array
39
+ plsql.test_sum([1,2,3,4])
40
+
41
+ # Nested objects or arrays are also supported
42
+ p_employee = { :employee_id => 1, :first_name => 'First', :last_name => 'Last', :hire_date => Time.local(2000,01,31),
43
+ :address => {:street => 'Street', :city => 'City', :country => 'Country'},
44
+ :phones => [{:type => 'mobile', :phone_number => '123456'}, {:type => 'fixed', :phone_number => '654321'}]}
45
+ plsql.test_store_employee(p_employee)
46
+
47
+ # Returned cursor can be fetched
48
+ plsql.test_cursor do |cursor|
49
+ cursor.fetch # => one row from cursor
50
+ cursor.fetch_all # => all rows from cursor
51
+ end
52
+
53
+ plsql.connection.autocommit = false
54
+ plsql.commit
55
+ plsql.rollback
56
+
57
+ plsql.logoff
58
+ ```
59
+
60
+ Look at RSpec tests under spec directory for more usage examples.
61
+
62
+
63
+ ### Table operations:
64
+
65
+ ruby-plsql also provides simple API for select/insert/update/delete table operations (with Sequel-like syntax). This could be useful if ruby-plsql is used without ActiveRecord (e.g. for writing PL/SQL unit tests):
66
+
67
+ ```ruby
68
+ # insert record in table
69
+ employee = { :employee_id => 1, :first_name => 'First', :last_name => 'Last', :hire_date => Time.local(2000,01,31) }
70
+ plsql.employees.insert employee # INSERT INTO employees VALUES (1, 'First', 'Last', ...)
71
+
72
+ # insert many records
73
+ employees = [employee1, employee2, ... ] # array of many Hashes
74
+ plsql.employees.insert employees
75
+
76
+ # insert many records as list of values
77
+ plsql.employees.insert_values [:employee_id, :first_name, :last_name],
78
+ [1, 'First 1', 'Last 1'],
79
+ [2, 'First 2', 'Last 2']
80
+
81
+ # select one record
82
+ plsql.employees.first # SELECT * FROM employees
83
+ # fetch first row => {:employee_id => ..., :first_name => '...', ...}
84
+ plsql.employees.first(:employee_id => 1) # SELECT * FROM employees WHERE employee_id = 1
85
+ plsql.employees.first("WHERE employee_id = 1")
86
+ plsql.employees.first("WHERE employee_id = :employee_id", 1)
87
+
88
+ # select many records
89
+ plsql.employees.all # => [{...}, {...}, ...]
90
+ plsql.employees.all(:order_by => :employee_id)
91
+ plsql.employees.all("WHERE employee_id > :employee_id", 5)
92
+
93
+ # count records
94
+ plsql.employees.count # SELECT COUNT(*) FROM employees
95
+ plsql.employees.count("WHERE employee_id > :employee_id", 5)
96
+
97
+ # update records
98
+ plsql.employees.update(:first_name => 'Second', :where => {:employee_id => 1})
99
+ # UPDATE employees SET first_name = 'Second' WHERE employee_id = 1
100
+
101
+ # delete records
102
+ plsql.employees.delete(:employee_id => 1) # DELETE FROM employees WHERE employee_id = 1
103
+
104
+ # select from sequences
105
+ plsql.employees_seq.nextval # SELECT employees_seq.NEXTVAL FROM dual
106
+ plsql.employees_seq.currval # SELECT employees_seq.CURRVAL FROM dual
107
+
108
+
109
+ ### Usage with Rails:
110
+
111
+ If using with Rails then include in initializer file:
112
+
113
+ ```ruby
114
+ plsql.activerecord_class = ActiveRecord::Base
115
+ ```
116
+
117
+ and then you do not need to specify plsql.connection (this is also safer when ActiveRecord reestablishes connection to database).
118
+
119
+ INSTALLATION
120
+ ------------
121
+
122
+ Install as gem with
123
+
124
+ gem install ruby-plsql
125
+
126
+ or include gem in Gemfile if using bundler.
127
+
128
+ In addition install either ruby-oci8 (for MRI/YARV) or copy Oracle JDBC driver to $JRUBY_HOME/lib (for JRuby).
129
+
130
+ If you are using MRI 1.8 or 1.9 Ruby implementation then you need to install ruby-oci8 gem (version 2.0.x or 2.1.x)
131
+ as well as Oracle client, e.g. [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html).
132
+
133
+ If you are using JRuby then you need to download latest [Oracle JDBC driver](http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html) - either ojdbc6.jar for Java 6 or ojdbc5.jar for Java 5. And copy this file to one of these locations:
134
+
135
+ * in `./lib` directory of Rails application and require it manually
136
+ * in some directory which is in `PATH`
137
+ * in `JRUBY_HOME/lib` directory
138
+ * or include path to JDBC driver jar file in Java `CLASSPATH`
139
+
140
+
141
+ LINKS
142
+ -----
143
+
144
+ * Source code: http://github.com/rsim/ruby-plsql
145
+ * Bug reports / Feature requests: http://github.com/rsim/ruby-plsql/issues
146
+ * Discuss at oracle_enhanced adapter group: http://groups.google.com/group/oracle-enhanced
147
+
148
+ CONTRIBUTORS
149
+ ------------
150
+
151
+ * Raimonds Simanovskis
152
+ * Edgars Beigarts
153
+ * Oleh Mykytyuk
154
+ * Wiehann Matthysen
155
+ * Dayle Larson
156
+ * Yasuo Honda
157
+
158
+ LICENSE
159
+ -------
160
+
161
+ (The MIT License)
162
+
163
+ Copyright (c) 2008-2012 Raimonds Simanovskis
164
+
165
+ Permission is hereby granted, free of charge, to any person obtaining
166
+ a copy of this software and associated documentation files (the
167
+ 'Software'), to deal in the Software without restriction, including
168
+ without limitation the rights to use, copy, modify, merge, publish,
169
+ distribute, sublicense, and/or sell copies of the Software, and to
170
+ permit persons to whom the Software is furnished to do so, subject to
171
+ the following conditions:
172
+
173
+ The above copyright notice and this permission notice shall be
174
+ included in all copies or substantial portions of the Software.
175
+
176
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
177
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
178
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
179
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
180
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
181
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
182
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,47 +1,42 @@
1
1
  require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
2
11
  require 'rake'
3
12
 
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "ruby-plsql"
8
- gem.summary = "Ruby API for calling Oracle PL/SQL procedures."
9
- gem.description = <<-EOS
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ gem.name = "ruby-plsql"
16
+ gem.summary = "Ruby API for calling Oracle PL/SQL procedures."
17
+ gem.description = <<-EOS
10
18
  ruby-plsql gem provides simple Ruby API for calling Oracle PL/SQL procedures.
11
19
  It could be used both for accessing Oracle PL/SQL API procedures in legacy applications
12
20
  as well as it could be used to create PL/SQL unit tests using Ruby testing libraries.
13
21
  EOS
14
- gem.email = "raimonds.simanovskis@gmail.com"
15
- gem.homepage = "http://github.com/rsim/ruby-plsql"
16
- gem.authors = ["Raimonds Simanovskis"]
17
- gem.add_development_dependency "rspec", "~> 1.3.0"
18
- gem.add_development_dependency "activerecord", "= 2.3.8"
19
- gem.add_development_dependency "activerecord-oracle_enhanced-adapter", "~> 1.3.1"
20
- gem.extra_rdoc_files = ['README.rdoc']
21
- end
22
- Jeweler::GemcutterTasks.new
23
- rescue LoadError
24
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ gem.email = "raimonds.simanovskis@gmail.com"
23
+ gem.homepage = "http://github.com/rsim/ruby-plsql"
24
+ gem.authors = ["Raimonds Simanovskis"]
25
+ gem.extra_rdoc_files = ['README.md']
25
26
  end
27
+ Jeweler::RubygemsDotOrgTasks.new
26
28
 
27
- require 'spec/rake/spectask'
28
- Spec::Rake::SpecTask.new(:spec) do |spec|
29
- spec.libs << 'lib' << 'spec'
30
- spec.spec_files = FileList['spec/**/*_spec.rb']
31
- end
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec)
32
31
 
33
- Spec::Rake::SpecTask.new(:rcov) do |spec|
34
- spec.libs << 'lib' << 'spec'
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
37
- spec.rcov_opts = ['--exclude', '/Library,spec/']
32
+ RSpec::Core::RakeTask.new(:rcov) do |t|
33
+ t.rcov = true
34
+ t.rcov_opts = ['--exclude', '/Library,spec/']
38
35
  end
39
36
 
40
- task :spec => :check_dependencies
41
-
42
37
  task :default => :spec
43
38
 
44
- require 'rake/rdoctask'
39
+ require 'rdoc/task'
45
40
  Rake::RDocTask.new do |rdoc|
46
41
  version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
42
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.4
1
+ 0.5.0
@@ -10,7 +10,7 @@ module PLSQL
10
10
  end
11
11
 
12
12
  def self.create(raw_conn, ar_class = nil) #:nodoc:
13
- if ar_class && !(defined?(::ActiveRecord) && [ar_class, ar_class.superclass].include?(::ActiveRecord::Base))
13
+ if ar_class && !(defined?(::ActiveRecord) && ar_class.ancestors.include?(::ActiveRecord::Base))
14
14
  raise ArgumentError, "Wrong ActiveRecord class"
15
15
  end
16
16
  case driver_type
@@ -2,19 +2,28 @@ begin
2
2
  require "java"
3
3
  require "jruby"
4
4
 
5
- # ojdbc14.jar file should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
6
-
7
- ojdbc_jar = "ojdbc14.jar"
5
+ # ojdbc6.jar or ojdbc5.jar file should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
6
+
7
+ java_version = java.lang.System.getProperty("java.version")
8
+ ojdbc_jar = if java_version =~ /^1.5/
9
+ "ojdbc5.jar"
10
+ elsif java_version >= '1.6'
11
+ "ojdbc6.jar"
12
+ else
13
+ nil
14
+ end
8
15
 
9
16
  unless ENV_JAVA['java.class.path'] =~ Regexp.new(ojdbc_jar)
10
17
  # On Unix environment variable should be PATH, on Windows it is sometimes Path
11
- env_path = ENV["PATH"] || ENV["Path"] || ''
12
- if ojdbc_jar_path = env_path.split(/[:;]/).concat($LOAD_PATH).find{|d| File.exists?(File.join(d,ojdbc_jar))}
18
+ env_path = (ENV["PATH"] || ENV["Path"] || '').split(/[:;]/)
19
+ # Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
20
+ # then in Ruby load path and finally in environment PATH
21
+ if ojdbc_jar_path = ['./lib'].concat($LOAD_PATH).concat(env_path).find{|d| File.exists?(File.join(d,ojdbc_jar))}
13
22
  require File.join(ojdbc_jar_path,ojdbc_jar)
14
23
  end
15
24
  end
16
25
 
17
- java.sql.DriverManager.registerDriver Java::oracle.jdbc.driver.OracleDriver.new
26
+ java.sql.DriverManager.registerDriver Java::oracle.jdbc.OracleDriver.new
18
27
 
19
28
  # set tns_admin property from TNS_ADMIN environment variable
20
29
  if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
@@ -23,10 +32,7 @@ begin
23
32
 
24
33
  rescue LoadError, NameError
25
34
  # JDBC driver is unavailable.
26
- error_message = "ERROR: ruby-plsql could not load Oracle JDBC driver. "+
27
- "Please install ojdbc14.jar library."
28
- STDERR.puts error_message
29
- raise LoadError
35
+ raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. Please install #{ojdbc_jar || "Oracle JDBC"} library."
30
36
  end
31
37
 
32
38
  module PLSQL
@@ -228,7 +234,9 @@ module PLSQL
228
234
 
229
235
  SQL_TYPE_TO_RUBY_CLASS = {
230
236
  java.sql.Types::CHAR => String,
237
+ java.sql.Types::NCHAR => String,
231
238
  java.sql.Types::VARCHAR => String,
239
+ java.sql.Types::NVARCHAR => String,
232
240
  java.sql.Types::LONGVARCHAR => String,
233
241
  java.sql.Types::NUMERIC => BigDecimal,
234
242
  java.sql.Types::INTEGER => Fixnum,
@@ -389,7 +397,7 @@ module PLSQL
389
397
  clob.setString(1, value)
390
398
  clob
391
399
  else
392
- Java::OracleSql::CLOB.getEmptyCLOB
400
+ nil
393
401
  end
394
402
  when :'Java::OracleSql::BLOB'
395
403
  if value
@@ -397,7 +405,7 @@ module PLSQL
397
405
  blob.setBytes(1, value.to_java_bytes)
398
406
  blob
399
407
  else
400
- Java::OracleSql::BLOB.getEmptyBLOB
408
+ nil
401
409
  end
402
410
  when :'Java::OracleSql::ARRAY'
403
411
  if value
@@ -194,8 +194,7 @@ module PLSQL
194
194
  end
195
195
  when :"OCI8::CLOB", :"OCI8::BLOB"
196
196
  # ruby-oci8 cannot create CLOB/BLOB from ''
197
- value = nil if value == ''
198
- type.new(raw_oci_connection, value)
197
+ value.to_s.length > 0 ? type.new(raw_oci_connection, value) : nil
199
198
  when :"OCI8::Cursor"
200
199
  value && value.raw_cursor
201
200
  else
@@ -52,12 +52,17 @@ module PLSQL
52
52
  when 'NUMBER'
53
53
  precision, scale = metadata[:data_precision], metadata[:data_scale]
54
54
  "NUMBER#{precision ? "(#{precision}#{scale ? ",#{scale}": ""})" : ""}"
55
- when 'VARCHAR2', 'CHAR', 'NVARCHAR2', 'NCHAR'
56
- length = metadata[:data_length]
57
- if length && (char_used = metadata[:char_used])
58
- length = "#{length} #{char_used == 'C' ? 'CHAR' : 'BYTE'}"
55
+ when 'VARCHAR2', 'CHAR'
56
+ length = case metadata[:char_used]
57
+ when 'C' then "#{metadata[:char_length]} CHAR"
58
+ when 'B' then "#{metadata[:data_length]} BYTE"
59
+ else
60
+ metadata[:data_length]
59
61
  end
60
- "#{metadata[:data_type]}#{length ? "(#{length})": ""}"
62
+ "#{metadata[:data_type]}#{length && "(#{length})"}"
63
+ when 'NVARCHAR2', 'NCHAR'
64
+ length = metadata[:char_length]
65
+ "#{metadata[:data_type]}#{length && "(#{length})"}"
61
66
  when 'PL/SQL TABLE', 'TABLE', 'VARRAY', 'OBJECT'
62
67
  metadata[:sql_type_name]
63
68
  else
@@ -87,7 +92,7 @@ module PLSQL
87
92
  @schema.select_all(
88
93
  "SELECT #{subprogram_id_column}, object_name, TO_NUMBER(overload), argument_name, position, data_level,
89
94
  data_type, in_out, data_length, data_precision, data_scale, char_used,
90
- type_owner, type_name, type_subname
95
+ char_length, type_owner, type_name, type_subname
91
96
  FROM all_arguments
92
97
  WHERE object_id = :object_id
93
98
  AND owner = :owner
@@ -98,7 +103,7 @@ module PLSQL
98
103
 
99
104
  subprogram_id, object_name, overload, argument_name, position, data_level,
100
105
  data_type, in_out, data_length, data_precision, data_scale, char_used,
101
- type_owner, type_name, type_subname = r
106
+ char_length, type_owner, type_name, type_subname = r
102
107
 
103
108
  @overloaded ||= !overload.nil?
104
109
  # if not overloaded then store arguments at key 0
@@ -134,6 +139,7 @@ module PLSQL
134
139
  :data_precision => data_precision && data_precision.to_i,
135
140
  :data_scale => data_scale && data_scale.to_i,
136
141
  :char_used => char_used,
142
+ :char_length => char_length && char_length.to_i,
137
143
  :type_owner => type_owner,
138
144
  :type_name => type_name,
139
145
  :type_subname => type_subname,