From d22f9c948859827f170b9a93820bf06aa720743c Mon Sep 17 00:00:00 2001 From: Marcel Molina Date: Sun, 21 May 2006 15:13:28 +0000 Subject: Fix Oracle boolean support and tests. Closes #5139. [schoenm@earthlink.net] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4351 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/CHANGELOG | 2 + activerecord/lib/active_record/associations.rb | 6 +-- .../abstract/schema_definitions.rb | 10 +++-- .../connection_adapters/oracle_adapter.rb | 52 +++++++++++----------- .../test/fixtures/db_definitions/oracle.sql | 4 +- activerecord/test/migration_test.rb | 16 +++---- 6 files changed, 48 insertions(+), 42 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 1e351913b1..09960b6650 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Fix Oracle boolean support and tests. Closes #5139. [schoenm@earthlink.net] + * create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper] * Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com] diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 717483cf3e..00ab226db0 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1183,7 +1183,7 @@ module ActiveRecord end end return false unless conditions.any? - conditions.join(' ').scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name| + conditions.join(' ').scan(/([\.\w]+)\.\w+/).flatten.any? do |condition_table_name| condition_table_name != table_name end end @@ -1192,7 +1192,7 @@ module ActiveRecord def include_eager_order?(options) order = options[:order] return false unless order - order.scan(/(\w+)\.\w+/).flatten.any? do |order_table_name| + order.scan(/([\.\w]+)\.\w+/).flatten.any? do |order_table_name| order_table_name != table_name end end @@ -1391,7 +1391,7 @@ module ActiveRecord @parent = parent @reflection = reflection @aliased_prefix = "t#{ join_dependency.joins.size }" - @aliased_table_name = table_name # start with the table name + @aliased_table_name = table_name #.tr('.', '_') # start with the table name, sub out any .'s @parent_table_name = parent.active_record.table_name if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{aliased_table_name.downcase}\son} diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 6e80707528..44fab82425 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -14,11 +14,12 @@ module ActiveRecord # +sql_type+ is only used to extract the column's length, if necessary. For example, company_name varchar(60). # +null+ determines if this column allows +NULL+ values. def initialize(name, default, sql_type = nil, null = true) - @name, @type, @null = name, simplified_type(sql_type), null - @sql_type = sql_type - # have to do this one separately because type_cast depends on #type + @name, @sql_type, @null, @limit = name, sql_type, null, extract_limit(sql_type) + + # simplified_type may depend on #limit, type_cast depends on #type + @type = simplified_type(sql_type) @default = type_cast(default) - @limit = extract_limit(sql_type) unless sql_type.nil? + @primary = nil @text = [:string, :text].include? @type @number = [:float, :integer].include? @type @@ -133,6 +134,7 @@ module ActiveRecord private def extract_limit(sql_type) + return unless sql_type $1.to_i if sql_type =~ /\((.*)\)/ end diff --git a/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb b/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb index a8c7656e78..fdfa887905 100644 --- a/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb @@ -75,19 +75,13 @@ begin module ConnectionAdapters #:nodoc: class OracleColumn < Column #:nodoc: - attr_reader :sql_type + alias_method :super_initialize, :initialize # overridden to add the concept of scale, required to differentiate # between integer and float fields - def initialize(name, default, sql_type, limit, scale, null) - @name, @limit, @sql_type, @scale, @null = name, limit, sql_type, scale, null - - @type = simplified_type(sql_type) - @default = type_cast(default) - - @primary = nil - @text = [:string, :text].include? @type - @number = [:float, :integer].include? @type + def initialize(name, default, sql_type = nil, null = true, scale = nil) + @scale = scale + super_initialize(name, default, sql_type, null) end def type_cast(value) @@ -95,6 +89,7 @@ begin case type when :string then value when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0) + when :boolean then cast_to_boolean(value) when :float then value.to_f when :datetime then cast_to_date_or_time(value) when :time then cast_to_time(value) @@ -104,19 +99,25 @@ begin private def simplified_type(field_type) + return :boolean if (OracleAdapter.emulate_booleans && field_type =~ /num/i && @limit == 1) case field_type when /char/i : :string when /num|float|double|dec|real|int/i : @scale == 0 ? :integer : :float when /date|time/i : @name =~ /_at$/ ? :time : :datetime - when /clob/i : :text - when /blob/i : :binary + when /clob/i : :text + when /blob/i : :binary end end + def cast_to_boolean(value) + return value if value.is_a? TrueClass or value.is_a? FalseClass + value.to_i == 0 ? false : true + end + def cast_to_date_or_time(value) return value if value.is_a? Date return nil if value.blank? - guess_date_or_time (value.is_a? Time) ? value : cast_to_time(value) + guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value)) end def cast_to_time(value) @@ -167,6 +168,9 @@ begin # * :database class OracleAdapter < AbstractAdapter + @@emulate_booleans = true + cattr_accessor :emulate_booleans + def adapter_name #:nodoc: 'Oracle' end @@ -277,8 +281,8 @@ begin elsif id_value # Pre-assigned id log(sql, name) { @connection.exec sql } else # Assume the sql contains a bind-variable for the id - id_value = select_one("select #{sequence_name}.nextval id from dual")['id'] - log(sql, name) { @connection.exec sql, id_value } + id_value = select_one("select #{sequence_name}.nextval id from dual")['id'].to_i + log(sql.sub(/\B:id\b/, id_value.to_s), name) { @connection.exec sql, id_value } end id_value @@ -361,10 +365,10 @@ begin (owner, table_name) = @connection.describe(table_name) table_cols = %Q{ - select column_name, data_type, data_default, nullable, + select column_name as name, data_type as sql_type, data_default, nullable, decode(data_type, 'NUMBER', data_precision, 'VARCHAR2', data_length, - null) as length, + null) as limit, decode(data_type, 'NUMBER', data_scale, null) as scale from all_tab_columns where owner = '#{owner}' @@ -373,18 +377,16 @@ begin } select_all(table_cols, name).map do |row| + row['sql_type'] += "(#{row['limit'].to_i})" if row['limit'] if row['data_default'] row['data_default'].sub!(/^(.*?)\s*$/, '\1') row['data_default'].sub!(/^'(.*)'$/, '\1') end - OracleColumn.new( - oracle_downcase(row['column_name']), - row['data_default'], - row['data_type'], - (l = row['length']).nil? ? nil : l.to_i, - (s = row['scale']).nil? ? nil : s.to_i, - row['nullable'] == 'Y' - ) + OracleColumn.new(oracle_downcase(row['name']), + row['data_default'], + row['sql_type'], + row['nullable'] == 'Y', + (s = row['scale']).nil? ? nil : s.to_i) end end diff --git a/activerecord/test/fixtures/db_definitions/oracle.sql b/activerecord/test/fixtures/db_definitions/oracle.sql index 39099b1747..48d6bdf073 100644 --- a/activerecord/test/fixtures/db_definitions/oracle.sql +++ b/activerecord/test/fixtures/db_definitions/oracle.sql @@ -37,7 +37,7 @@ create table topics ( bonus_time timestamp default null, last_read timestamp default null, content varchar(4000), - approved integer default 1, + approved number(1) default 1, replies_count integer default 0, parent_id integer references topics initially deferred disable, type varchar(50) default null, @@ -53,7 +53,7 @@ create table topics ( bonus_time date default null, last_read date default null, content varchar(4000), - approved integer default 1, + approved number(1) default 1, replies_count integer default 0, parent_id integer references topics initially deferred disable, type varchar(50) default null, diff --git a/activerecord/test/migration_test.rb b/activerecord/test/migration_test.rb index e71790ed1f..0f837841e1 100644 --- a/activerecord/test/migration_test.rb +++ b/activerecord/test/migration_test.rb @@ -106,14 +106,8 @@ if ActiveRecord::Base.connection.supports_migrations? four = columns.detect { |c| c.name == "four" } assert_equal "hello", one.default - if current_adapter?(:OracleAdapter) - # Oracle doesn't support native booleans - assert_equal true, two.default == 1 - assert_equal false, three.default != 0 - else - assert_equal true, two.default - assert_equal false, three.default - end + assert_equal true, two.default + assert_equal false, three.default assert_equal 1, four.default ensure @@ -147,6 +141,11 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal 'smallint', one.sql_type assert_equal 'integer', four.sql_type assert_equal 'bigint', eight.sql_type + elsif current_adapter?(:OracleAdapter) + assert_equal 'NUMBER(38)', default.sql_type + assert_equal 'NUMBER(1)', one.sql_type + assert_equal 'NUMBER(4)', four.sql_type + assert_equal 'NUMBER(8)', eight.sql_type end ensure Person.connection.drop_table :testings rescue nil @@ -328,6 +327,7 @@ if ActiveRecord::Base.connection.supports_migrations? new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns") assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true } assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false } + assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true } end def test_change_column_with_new_default -- cgit v1.2.3