[ruby-oci8-commit] [546] trunk/ruby-oci8: fix OCI8. describe_table not to follow synonyms until stack overflow.

nobody at rubyforge.org nobody at rubyforge.org
Tue Dec 18 12:12:11 UTC 2012


Revision: 546
Author:   kubo
Date:     2012-12-18 12:12:11 +0000 (Tue, 18 Dec 2012)
Log Message:
-----------
fix OCI8.describe_table not to follow synonyms until stack overflow.
Now the recursive level is limited to 20.
(github issue #26 reported by Brian Henderson)

Modified Paths:
--------------
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/error.c
    trunk/ruby-oci8/lib/oci8/metadata.rb
    trunk/ruby-oci8/lib/oci8/oci8.rb

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2012-12-16 02:55:19 UTC (rev 545)
+++ trunk/ruby-oci8/ChangeLog	2012-12-18 12:12:11 UTC (rev 546)
@@ -1,3 +1,11 @@
+2012-12-18  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/error.c, lib/oci8/oci8.rb: change the OCIError constructor to
+	    accept an Oracle error code as the first parameter and create a message
+	    which depends on NLS_LANG.
+	* lib/oci8/metadata.rb: fix OCI8.describe_table not to follow synonyms
+	    until stack overflow. Now the recursive level is limited to 20.
+	    (github issue #26 reported by Brian Henderson)
+
 2012-12-16  KUBO Takehiro  <kubo at jiubao.org>
 	* ext/oci8/extconf.rb, lib/oci8.rb.in: use RUBY_VERSION instead of
 	   RbConfig::CONFIG['ruby_version'] to know the ruby ABI version.

Modified: trunk/ruby-oci8/ext/oci8/error.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/error.c	2012-12-16 02:55:19 UTC (rev 545)
+++ trunk/ruby-oci8/ext/oci8/error.c	2012-12-18 12:12:11 UTC (rev 546)
@@ -160,31 +160,6 @@
     return exc;
 }
 
-/*
- * call-seq:
- *    initialize(message, code = nil, sql = nil, parse_error_offset = nil)
- *
- * Creates a new OCIError object.
- *
- * @example
- *   OCIError.new("ORA-00001: unique constraint (%s.%s) violated", 1)
- *   # => #<OCIError: ORA-00001: unique constraint (%s.%s) violated>
- */
-static VALUE oci8_error_initialize(int argc, VALUE *argv, VALUE self)
-{
-    VALUE msg;
-    VALUE code;
-    VALUE sql;
-    VALUE parse_error_offset;
-
-    rb_scan_args(argc, argv, "04", &msg, &code, &sql, &parse_error_offset);
-    rb_call_super(argc > 1 ? 1 : argc, argv);
-    rb_ivar_set(self, oci8_id_at_code, code);
-    rb_ivar_set(self, oci8_id_at_sql, sql);
-    rb_ivar_set(self, oci8_id_at_parse_error_offset, parse_error_offset);
-    return Qnil;
-}
-
 sb4 oci8_get_error_code(OCIError *errhp)
 {
     sb4 errcode = -1;
@@ -214,8 +189,6 @@
     eOCIContinue = rb_define_class("OCIContinue", eOCIException);
     eOCISuccessWithInfo = rb_define_class("OCISuccessWithInfo", eOCIError);
 
-    rb_define_method(eOCIError, "initialize", oci8_error_initialize, -1);
-
     /*
      * @attr_reader [Integer] code  error code
      */

Modified: trunk/ruby-oci8/lib/oci8/metadata.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/metadata.rb	2012-12-16 02:55:19 UTC (rev 545)
+++ trunk/ruby-oci8/lib/oci8/metadata.rb	2012-12-18 12:12:11 UTC (rev 546)
@@ -2007,15 +2007,19 @@
       __describe(table_name, OCI8::Metadata::Table, false)
     else
       # check tables, views, synonyms and public synonyms.
-      metadata = __describe(table_name, OCI8::Metadata::Unknown, true)
-      case metadata
-      when OCI8::Metadata::Table, OCI8::Metadata::View
-        metadata
-      when OCI8::Metadata::Synonym
-        describe_table(metadata.translated_name)
-      else
-        raise OCIError.new("ORA-04043: object #{table_name} does not exist", 4043)
+      recursive_level = 20
+      recursive_level.times do
+        metadata = __describe(table_name, OCI8::Metadata::Unknown, true)
+        case metadata
+        when OCI8::Metadata::Table, OCI8::Metadata::View
+          return metadata
+        when OCI8::Metadata::Synonym
+          table_name = metadata.translated_name
+        else
+          raise OCIError.new(4043, table_name) # ORA-04043: object %s does not exist
+        end
       end
+      raise OCIError.new(36, recursive_level) # ORA-00036: maximum number of recursive SQL levels (%s) exceeded
     end
   end
   # returns a OCI8::Metadata::View in the current schema.

Modified: trunk/ruby-oci8/lib/oci8/oci8.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/oci8.rb	2012-12-16 02:55:19 UTC (rev 545)
+++ trunk/ruby-oci8/lib/oci8/oci8.rb	2012-12-18 12:12:11 UTC (rev 546)
@@ -382,6 +382,63 @@
   end
 end
 
+class OCIError
+
+  # @overload initialize(message, error_code = nil, sql_stmt = nil, parse_error_offset = nil)
+  #   Creates a new OCIError object with specified parameters.
+  #
+  #   @param [String]   message    error message
+  #   @param [Integer]  error_code Oracle error code
+  #   @param [String]   sql_stmt   SQL statement
+  #   @param [Integer]  parse_error_offset
+  #
+  #   @example
+  #     OCIError.new("ORA-00001: unique constraint (%s.%s) violated", 1, 'insert into table_name values (1)', )
+  #     # => #<OCIError: ORA-00001: unique constraint (%s.%s) violated>
+  #     #<OCIError: ORA-00923: FROM keyword not found where expected>
+  #     "select sysdate"
+  #     923
+  #     14
+  #
+  # @overload initialize(error_code, *params)
+  #   Creates a new OCIError object with the error message which corresponds to the specified
+  #   Oracle error code.
+  #
+  #   @param [Integer]  error_code  Oracle error code
+  #   @param [String, ...] params   parameters which replace '%s'
+  #
+  #   @example
+  #     # without parameters
+  #     OCIError.new(4043)
+  #     # When NLS_LANG=american_america.AL32UTF8
+  #     # => #<OCIError: ORA-04043: object %s does not exist>
+  #     # When NLS_LANG=german_germany.AL32UTF8
+  #     # => #<OCIError: ORA-04043: Objekt %s ist nicht vorhanden>
+  #     
+  #     # with one parameter
+  #     OCIError.new(4043, 'table_name')
+  #     # When NLS_LANG=american_america.AL32UTF8
+  #     # => #<OCIError: ORA-04043: object table_name does not exist>
+  #     # When NLS_LANG=german_germany.AL32UTF8
+  #     # => #<OCIError: ORA-04043: Objekt table_name ist nicht vorhanden>
+  #
+  def initialize(*args)
+    if args.length > 0
+      if args[0].is_a? Fixnum
+        @code = args.shift
+        super(OCI8.error_message(@code).gsub('%s') {|s| args.empty? ? '%s' : args.shift})
+        @sql = nil
+        @parse_error_offset = nil
+      else
+        msg, @code, @sql, @parse_error_offset = args
+        super(msg)
+      end
+    else
+      super()
+    end
+  end
+end
+
 class OraDate
 
   # Returns a Time object which denotes self.



More information about the ruby-oci8-commit mailing list