From 7a2ce50a9386a46387c114945354ba89c5fc9c30 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 21 Feb 2006 00:00:29 +0000 Subject: [PATCH] Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) (closes #3879) [Aggregated by schoenm@earthlink.net] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3623 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/CHANGELOG | 4 ++ .../connection_adapters/oci_adapter.rb | 71 ++++++++++++------- activerecord/lib/active_record/schema.rb | 2 +- .../test/associations_join_model_test.rb | 2 +- .../test/fixtures/db_definitions/oci.sql | 3 +- activerecord/test/synonym_test_oci.rb | 2 +- 6 files changed, 54 insertions(+), 30 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index ade3e656a8..01dd02a2f4 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,9 @@ *SVN* +* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 [Aggregated by schoenm@earthlink.net] + +* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails@atyp.de] + * Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [skaes] * Correct syntax error in mysql DDL, and make AAACreateTablesTest run first [Bob Silva] diff --git a/activerecord/lib/active_record/connection_adapters/oci_adapter.rb b/activerecord/lib/active_record/connection_adapters/oci_adapter.rb index 46937b034b..66931a8886 100644 --- a/activerecord/lib/active_record/connection_adapters/oci_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/oci_adapter.rb @@ -56,7 +56,7 @@ begin value = self[c.name] next if value.nil? || (value == '') lob = connection.select_one( - "select #{ c.name} from #{ self.class.table_name } WHERE #{ self.class.primary_key} = #{quote(id)}", + "SELECT #{ c.name} FROM #{ self.class.table_name } WHERE #{ self.class.primary_key} = #{quote(id)}", 'Writable Large Object')[c.name] lob.write value } @@ -149,6 +149,9 @@ begin # * Default values that are functions (such as "SYSDATE") are not # supported. This is a restriction of the way ActiveRecord supports # default values. + # * Support for Oracle8 is limited by Rails' use of ANSI join syntax, which + # is supported in Oracle9i and later. You will need to use #finder_sql for + # has_and_belongs_to_many associations to run against Oracle8. # # Options: # @@ -167,7 +170,7 @@ begin def native_database_types #:nodoc { - :primary_key => "NUMBER(38) NOT NULL", + :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY", :string => { :name => "VARCHAR2", :limit => 255 }, :text => { :name => "LONG" }, :integer => { :name => "NUMBER", :limit => 38 }, @@ -332,10 +335,7 @@ begin end def columns(table_name, name = nil) #:nodoc: - table_name = table_name.to_s.upcase - owner = table_name.include?('.') ? "'#{table_name.split('.').first}'" : "user" - table = "'#{table_name.split('.').last}'" - scope = (owner == "user" ? "user" : "all") + table_info = @connection.object_info(table_name) table_cols = %Q{ select column_name, data_type, data_default, nullable, @@ -343,25 +343,18 @@ begin 'VARCHAR2', data_length, null) as length, decode(data_type, 'NUMBER', data_scale, null) as scale - from #{scope}_catalog cat, #{scope}_synonyms syn, all_tab_columns col - where cat.table_name = #{table} - and syn.synonym_name (+)= cat.table_name - and col.table_name = nvl(syn.table_name, cat.table_name) - and col.owner = nvl(syn.table_owner, #{(scope == "all" ? "cat.owner" : "user")}) } - - if scope == "all" - table_cols << %Q{ - and cat.owner = #{owner} - and syn.owner (+)= cat.owner } - end + from all_tab_columns + where owner = '#{table_info.schema}' + and table_name = '#{table_info.name}' + } select_all(table_cols, name).map do |row| row['data_default'].sub!(/^'(.*)'\s*$/, '\1') if row['data_default'] OCIColumn.new( - oci_downcase(row['column_name']), + oci_downcase(row['column_name']), row['data_default'], - row['data_type'], - row['length'], + row['data_type'], + row['length'], row['scale'], row['nullable'] == 'Y' ) @@ -370,17 +363,17 @@ begin def create_table(name, options = {}) #:nodoc: super(name, options) - execute "CREATE SEQUENCE #{name}_seq" + execute "CREATE SEQUENCE #{name}_seq" unless options[:id] == false end def rename_table(name, new_name) #:nodoc: execute "RENAME #{name} TO #{new_name}" - execute "RENAME #{name}_seq TO #{new_name}_seq" + execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil end def drop_table(name) #:nodoc: super(name) - execute "DROP SEQUENCE #{name}_seq" + execute "DROP SEQUENCE #{name}_seq" rescue nil end def remove_index(table_name, options = {}) #:nodoc: @@ -492,9 +485,10 @@ begin end - # This OCI8 patch may not longer be required with the upcoming - # release of version 0.2. class OCI8 #:nodoc: + + # This OCI8 patch may not longer be required with the upcoming + # release of version 0.2. class Cursor #:nodoc: alias :define_a_column_pre_ar :define_a_column def define_a_column(i) @@ -505,6 +499,33 @@ begin end end end + + # missing constant from oci8 + OCI_PTYPE_UNK = 0 + + def object_info(name) + OraObject.new describe(name.to_s, OCI_PTYPE_UNK) + end + + def describe(name, type) + @desc ||= @@env.alloc(OCIDescribe) + @desc.describeAny(@svc, name, type) + @desc.attrGet(OCI_ATTR_PARAM) + end + + class OraObject + attr_reader :schema, :name + def initialize(info) + case info.attrGet(OCI_ATTR_PTYPE) + when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW + @schema = info.attrGet(OCI_ATTR_OBJ_SCHEMA) + @name = info.attrGet(OCI_ATTR_OBJ_NAME) + when OCI_PTYPE_SYN + @schema = info.attrGet(OCI_ATTR_SCHEMA_NAME) + @name = info.attrGet(OCI_ATTR_NAME) + end + end + end end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index c986380853..6e0579b586 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -51,7 +51,7 @@ module ActiveRecord "#{k} = #{v}" end - update "UPDATE schema_info SET #{info.join(", ")}" + update "UPDATE #{Migrator.schema_info_table_name} SET #{info.join(", ")}" end end end diff --git a/activerecord/test/associations_join_model_test.rb b/activerecord/test/associations_join_model_test.rb index 5c1510faff..f7ca3d6a84 100644 --- a/activerecord/test/associations_join_model_test.rb +++ b/activerecord/test/associations_join_model_test.rb @@ -96,7 +96,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase end def test_has_many_class_methods_called_by_method_missing - assert_equal categories(:general), authors(:david).categories.find_by_name('General') + assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first # assert_equal nil, authors(:david).categories.find_by_name('Technology') end diff --git a/activerecord/test/fixtures/db_definitions/oci.sql b/activerecord/test/fixtures/db_definitions/oci.sql index 760659d1c4..31d601b734 100644 --- a/activerecord/test/fixtures/db_definitions/oci.sql +++ b/activerecord/test/fixtures/db_definitions/oci.sql @@ -17,8 +17,7 @@ create sequence companies_nonstd_seq minvalue 10000; create table accounts ( id integer not null, firm_id integer default null references companies initially deferred disable, - credit_limit integer default null, - primary key (id) + credit_limit integer default null ); create sequence accounts_seq minvalue 10000; diff --git a/activerecord/test/synonym_test_oci.rb b/activerecord/test/synonym_test_oci.rb index 4d4776c363..fcfb2f3bd1 100644 --- a/activerecord/test/synonym_test_oci.rb +++ b/activerecord/test/synonym_test_oci.rb @@ -10,7 +10,7 @@ class TestOracleSynonym < Test::Unit::TestCase def test_oracle_synonym topic = Topic.new - subject = Subject.new + subject = Subject.new assert_equal(topic.attributes, subject.attributes) end