ray_apps_blog

April 21, 2009

ruby-plsql new version – Ruby 1.9.1 support and more

Filed under: jruby,oracle,pl/sql,rails,ruby — Raimonds Simanovskis @ 1:20 pm

I have released ruby-plsql gem (Ruby API for Oracle PL/SQL procedure calls) new version 0.3.0 which includes several new features.

Ruby 1.9.1

Probably the most important is support for Ruby 1.9.1 – now you can use both Oracle enhanced adapter and ruby-plsql gem on all three major Ruby plaforms:

  • MRI 1.8.6 with ruby-oci8 1.0.x library or gem
  • Ruby/YARV 1.9.1 with ruby-oci8 2.0 library or gem (currently just trunk version of ruby-oci8 2.0 contains the last bug fixes for Ruby 1.9.1)
  • JRuby (so far tested with 1.1.6) with JDBC Oracle driver

ActiveRecord connection

In addition usage of ruby-plsql gem in Ruby on Rails project is simplified. Now you can include in environment.rb or some initializer file just:

plsql.activerecord_class = ActiveRecord::Base

and you don’t need to specify plsql.connection anymore – it will always use current ActiveRecord connection. This is also useful when ActiveRecord reestablishes connection to database as you don’t need to reestablish plsql connection in this case.

In addition if you use several different connections to Oracle database then you can assign to plsql.activerecord_class also different class that inherits from ActiveRecord::Base and has connection to different database.

Database time zone

Also you can also specify in which timezone DATE values are stored in database:

plsql.default_timezone = :local

or

plsql.default_timezone = :utc

This will affect how DATE values (without timezone) will be converted to Time or DateTime values (with timezone), default selection is :local timezone. If you have set plsql.activerecord_class then the value will be taken from ActiveRecord::Base.default_timezone.

BLOB support

You can now use BLOB data type for input and output parameters and function return values.
I remind you that also NUMBER, VARCHAR2, DATE, TIMESTAMP and CLOB data types are supported,

Synonym support

Now you can also use private and public database synonyms to functions or procedures or packages.
E.g. if ORA_LOGIN_USER is public database synonym to SYS.LOGIN_USER function then instead of

plsql.sys.login_user

you can use

plsql.ora_login_user

Installation

To install the gem as always do

sudo gem install ruby-plsql

or call the correct gem command version for JRuby or Ruby 1.9.1.

Source code of ruby-plsql is located at GitHub where you can find usage examples in RSpec tests.


10 Comments »

  1. I’ve enjoyed reading your blog. Most are full of garbage, but this is really interesting – and useful too!

    Comment by bank register — May 22, 2009 @ 10:51 pm | Reply

  2. They say that when buying a house the three key factors are location, location, location. Well, when looking for a useful website the factors are quality,quality,quality. The content here fits the bill exactly. Thanks a bunch. Great stuff!

    Comment by leather checkbook — May 23, 2009 @ 1:03 pm | Reply

  3. Hi Raimonds, thankyou – that plsql gem is AWESOME and makes calling oracle stored procs really easily. Could you please let me know if it supports refcursors.
    I am trying to call a procedure that returns a refcursor and cant get the call syntax correct (using jruby)

    Note: the stored proc is like…

    procedure pkg.test ( p_cursor out sys_refcursor)
    is
    begin
    open p_cursor for
    select
    col1 ,
    col2
    from pkg.table1;
    end test;

    Comment by Marcus — July 23, 2009 @ 7:03 am | Reply

  4. Currently I do not support Oracle cursors as IN or OUT parameters.
    It is a little bit tricky as there is no standard Ruby type that I could map returned SYS_REFCURSOR to.
    If I could think of some good Ruby syntax for this case then I could try to implement it in future ruby-plsql versions.

    How would you like to use returned cursor in Ruby? Would you like to fetch all rows from it or fetch one by one?

    Comment by Raimonds Simanovskis — July 23, 2009 @ 10:21 am | Reply

    • perhaps something like
      psql.pkg.test {|row| row.each {|col| }

      I guess an enhanced version could support batch fetching similar to find_in_batches.
      I have managed to call using a ref-cursor using the following jdbc code – not a generic solution and only for JRuby but found no references on google so i will paste it here….

      def test
      conn = ActiveRecord::Base.connection.raw_connection
      cstmt = conn.prepareCall(“begin test(?); end;”)
      cstmt.registerOutParameter(1, -10) # -10 == oracle.jdbc.driver.OracleTypes.CURSOR
      cstmt.execute
      rs = cstmt.getObject(1)
      rsm = rs.getMetaData
      columnCount = rsm.getColumnCount
      rows = []
      while (rs.next())
      row = []
      for j in (1..columnCount)
      row << rs.getObject(j)
      end
      rows << row
      end
      return rows
      ensure
      rs.close if rs
      cstmt.close if cstmt
      end

      Comment by Marcus Baguley — July 24, 2009 @ 12:30 am | Reply

  5. hi Raimonds,

    I am very happy user of your gem.

    I encountered today an obstacle when handling 2 connections in one script.
    I have 2 databases emeadb11 and mktgcd

    chris@chris-ub:~$ irb
    irb(main):001:0> require ‘rubygems’
    => true
    irb(main):002:0> require ‘ruby_plsql’
    => true
    irb(main):003:0> con1 = OCI8.new(‘chris’,'*****’,'emeadb11′)
    => #
    irb(main):004:0> con2 = OCI8.new(‘kcierpisz’,'*****’,'mktgcd’)
    => #
    irb(main):005:0> plsql.connection = con1
    => #
    irb(main):006:0> plsql
    => #<PLSQL::Schema:0x8cd9f94 @schema_name=nil, @procedures={}, @connection=#<PLSQL::OCIConnection:0x8cd95a8 @activerecord_class=nil, @raw_connection=#, @raw_driver=:oci>, @first=true, @schemas={}, @packages={}>
    irb(main):007:0> plsql.emeadb_phone_pkg.prepare_phones_in(‘EMEA_PHONES’)
    => nil
    irb(main):008:0> plsql
    => #<PLSQL::Schema:0x8cd9f94 @schema_name="CHRIS", @procedures={}, @connection=#<PLSQL::OCIConnection:0x8cd95a8 @activerecord_class=nil, @raw_connection=#, @raw_driver=:oci>, @first=true, @schemas={}, @packages={:emeadb_phone_pkg=>##[]}, @schema=#, @arguments={0=>{:emea_phones_table=>{:data_precision=>nil, :data_type=>”VARCHAR2″, :data_scale=>nil, :in_out=>”IN”, :data_length=>nil, :position=>1}}}, @argument_list={0=>[:emea_phones_table]}, @overloaded=false, @package=”EMEADB_PHONE_PKG”, @return={0=>nil}, @overloads=[0], @procedure=”PREPARE_PHONES_IN”>}, @override_schema_name=nil, @schema=#, @package=”EMEADB_PHONE_PKG”>}>
    irb(main):009:0> plsql.connection = con2
    => #
    irb(main):010:0> plsql
    => #<PLSQL::Schema:0x8cd9f94 @schema_name="CHRIS", @procedures={}, @connection=#<PLSQL::OCIConnection:0x97554c8 @activerecord_class=nil, @raw_connection=#, @raw_driver=:oci>, @first=true, @schemas={}, @packages={}>
    irb(main):011:0> plsql.phone_pkg.create_table(‘EMEA_PHONES_TMP’)
    ArgumentError: No PL/SQL procedure found
    from /home/chris/.gem/ruby/1.8/gems/ruby-plsql-0.3.1/lib/plsql/schema.rb:127:in `method_missing’
    from (irb):11
    from :0
    irb(main):012:0>

    as you can see at irb(main):006 schema_name is nil (although it should already here have CHRIS I guess)
    at irb(main):008 schema_name has value ‘CHRIS’ (probably because at irb(main):007 I ran a procedure)
    at irb(main):009 I am assigning con2 (KCIERPISZ schema)
    plsql though sees still CHRIS under schema_name
    and that’s why irb(main):011 fails.

    When I run only one connection per script everything is fine:
    irb(main):001:0> require ‘rubygems’
    => true
    irb(main):002:0> require ‘ruby_plsql’
    => true
    irb(main):003:0>
    irb(main):004:0* con2 = OCI8.new(“kcierpisz”,”*****”,”mktgcd”)
    => #
    irb(main):005:0> plsql.connection = con2
    => #
    irb(main):006:0> plsql.phone_pkg.create_table(‘EMEA_PHONES_TMP’)
    => nil
    irb(main):007:0> plsql
    => #<PLSQL::Schema:0x8f33984 @schema_name="KCIERPISZ", @procedures={}, @connection=#<PLSQL::OCIConnection:0x8f338e4 @activerecord_class=nil, @raw_connection=#, @raw_driver=:oci>, @first=true, @schemas={}, @packages={:phone_pkg=>##[]}, @schema=#, @arguments={0=>{:emea_phones_tmp_table=>{:data_precision=>nil, :data_type=>”VARCHAR2″, :data_scale=>nil, :in_out=>”IN”, :data_length=>nil, :position=>1}}}, @argument_list={0=>[:emea_phones_tmp_table]}, @overloaded=false, @package=”PHONE_PKG”, @return={0=>nil}, @overloads=[0], @procedure=”CREATE_TABLE”>}, @override_schema_name=nil, @schema=#, @package=”PHONE_PKG”>}>
    irb(main):008:0>

    Is there a way I could overwrite that schema_name (if that causes the issue)?

    thanks a lot for your help,
    chris

    Comment by chris — October 22, 2009 @ 9:06 pm | Reply

    • Fixed the issue – I was not clearing cached schema name after resetting connection. Thanks for reporting :)

      I pushed the patch to github – if this is critical for you then please clone it from github.com/rsim/ruby-plsql and run “rake install_gem”. Or you can wait until I publish new version of gem.

      Btw if you need to operate with two different connections then you can use them in parallel like:

      plsql(:one).connection = OCI8.new(’chris’,’*****’,’emeadb11′)
      plsql(:two).connection = OCI8.new(’kcierpisz’,’*****’,’mktgcd’)
      plsql(:one).emeadb_phone_pkg.prepare_phones_in(’EMEA_PHONES’)
      plsql(:two).phone_pkg.create_table(’EMEA_PHONES_TMP’)

      And also if you have any further issues with ruby-plsql then please use discussion group http://groups.google.com/group/oracle-enhanced – it is primarily used for oracle_enhanced adapter but ruby-plsql questions might be interesting for oracle_enhanced users as well.

      Comment by Raimonds Simanovskis — October 23, 2009 @ 2:41 pm | Reply

      • I cloned git repo, but rake install_gem did not work giving me following:
        chris@chris-ub:~/src/ruby-plsql$ rake install_gem
        (in /home/chris/src/ruby-plsql)
        rm -r doc
        rm -r pkg
        rake aborted!
        Don’t know how to build task ‘lib/ruby_plsql/version.rb’

        I tried manually just add the line @schema_name = nil to schema.rb but it did not change the behaviour (maybe in between there were other changes, I see the directory structure is changed slightly inside of lib/)

        I will wait for the gem. And anyway it’s not critical since the parallel connections option works great.

        thanks for your great work

        PS. from next issue on will post to the discussion group :)

        Comment by chris — October 23, 2009 @ 8:45 pm

      • I didn’t update Manifest.txt file to reflect file name changes in gem – now rake install_gem should work.

        Comment by Raimonds Simanovskis — October 26, 2009 @ 2:30 pm

  6. and it does indeed, thanks :)

    Comment by chris — October 26, 2009 @ 9:58 pm | Reply


RSS feed for comments on this post.

Leave a comment

Blog at WordPress.com.