Index: test/models/data_types.rb =================================================================== --- test/models/data_types.rb (revision 1130) +++ test/models/data_types.rb (working copy) @@ -5,7 +5,10 @@ def self.up create_table "db_types", :force => true do |t| t.column :sample_timestamp, :timestamp - t.column :sample_decimal, :decimal, :precision=> 15, :scale => 0 + t.column :sample_decimal, :decimal, :precision=> 15, :scale => 0 + t.column :date, :date + t.column :datetime, :datetime + t.column :binary, :binary end end Index: test/db/hsqldb.rb =================================================================== --- test/db/hsqldb.rb (revision 1130) +++ test/db/hsqldb.rb (working copy) @@ -9,7 +9,7 @@ ActiveRecord::Base.logger = logger at_exit { - # Clean up hsqldb when done + # Clean up hsqldb when done Dir['test.db*'].each {|f| File.delete(f)} - File.delete('hsqldb-testdb.log') + File.delete('hsqldb-testdb.log') rescue nil #can't delete on windows } Index: test/db/mysql.rb =================================================================== --- test/db/mysql.rb (revision 1130) +++ test/db/mysql.rb (working copy) @@ -7,3 +7,8 @@ } ActiveRecord::Base.establish_connection(config) + +logger = Logger.new 'mysql-testdb.log' +logger.level = Logger::DEBUG +ActiveRecord::Base.logger = logger + Index: test/simple.rb =================================================================== --- test/simple.rb (revision 1130) +++ test/simple.rb (working copy) @@ -1,14 +1,20 @@ -ActiveRecord::Schema.verbose = false +ActiveRecord::Schema.verbose = false + +ActiveRecord::Base.time_zone_aware_attributes = true +ActiveRecord::Base.default_timezone = :utc +Time.zone = 'Moscow' #just a random zone, unlikely to be local, and not utc module MigrationSetup def setup + DbTypeMigration.up CreateEntries.up CreateAutoIds.up @connection = ActiveRecord::Base.connection end - def teardown + def teardown + #DbTypeMigration.down CreateEntries.down CreateAutoIds.down ActiveRecord::Base.clear_active_connections! @@ -23,7 +29,8 @@ @content = "Hello from JRuby on Rails!" @new_title = "First post updated title" @rating = 205.76 - Entry.create :title => @title, :content => @content, :rating => @rating + Entry.create :title => @title, :content => @content, :rating => @rating + DbType.create end end @@ -75,6 +82,62 @@ post.destroy assert_equal prev_count - 1, Entry.count + end + + def test_save_time + t = Time.now + #precision will only be expected to the second. + time = Time.local(t.year, t.month, t.day, t.hour, t.min, t.sec) + e = DbType.find(:first) + e.datetime = time + e.save! + e = DbType.find(:first) + assert_equal time.in_time_zone, e.datetime + end + + def test_save_date_time + t = Time.now + #precision will only be expected to the second. + time = Time.local(t.year, t.month, t.day, t.hour, t.min, t.sec) + datetime = time.to_datetime + e = DbType.find(:first) + e.datetime = datetime + e.save! + e = DbType.find(:first) + assert_equal time, e.datetime.localtime + end + + def test_save_time_with_zone + t = Time.now + #precision will only be expected to the second. + original_time = Time.local(t.year, t.month, t.day, t.hour, t.min, t.sec) + time = original_time.in_time_zone + e = DbType.find(:first) + e.datetime = time + e.save! + e = DbType.find(:first) + assert_equal time, e.datetime + end + + + def test_save_date + date = Date.new(2007) + e = DbType.find(:first) + e.date = date + e.save! + e = DbType.find(:first) + assert_equal date, e.date + end + + def test_save_binary + #string is 60_000 bytes + binary_string = "\000ABCDEFGHIJKLMNOPQRSTUVWXYZ'\001\003"*1#2_000 + e = DbType.find(:first) + e.binary = binary_string + e.send(:write_attribute, :binary, binary_string) + e.save! + e = DbType.find(:first) + assert_equal binary_string, e.binary end def test_indexes Index: test/jdbc_common.rb =================================================================== --- test/jdbc_common.rb (revision 1130) +++ test/jdbc_common.rb (working copy) @@ -4,6 +4,7 @@ require 'jdbc_adapter' require 'models/auto_id' require 'models/entry' +require 'models/data_types' require 'models/add_not_null_column_to_table' require 'simple' require 'has_many_through' Index: lib/jdbc_adapter/jdbc_derby.rb =================================================================== --- lib/jdbc_adapter/jdbc_derby.rb (revision 1130) +++ lib/jdbc_adapter/jdbc_derby.rb (working copy) @@ -48,28 +48,6 @@ end module Column - def value_to_binary(value) - value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*") - 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)) - end - - def cast_to_time(value) - return value if value.is_a? Time - time_array = ParseDate.parsedate value - time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1; - Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil - end - - def guess_date_or_time(value) - (value.hour == 0 and value.min == 0 and value.sec == 0) ? - Date.new(value.year, value.month, value.day) : value - end - def simplified_type(field_type) return :boolean if field_type =~ /smallint/i return :float if field_type =~ /real/i @@ -382,3 +360,4 @@ end end + Index: lib/jdbc_adapter/jdbc_hsqldb.rb =================================================================== --- lib/jdbc_adapter/jdbc_hsqldb.rb (revision 1130) +++ lib/jdbc_adapter/jdbc_hsqldb.rb (working copy) @@ -23,39 +23,7 @@ end module Column - def type_cast(value) - return nil if value.nil? || value =~ /^\s*null\s*$/i - case type - when :string then value - when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0) - when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0) - when :float then value.to_f - when :datetime then cast_to_date_or_time(value) - when :timestamp then cast_to_time(value) - when :binary then value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*") - when :time then cast_to_time(value) - else value - end - 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)) - end - def cast_to_time(value) - return value if value.is_a? Time - time_array = ParseDate.parsedate value - time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1; - Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil - end - - def guess_date_or_time(value) - (value.hour == 0 and value.min == 0 and value.sec == 0) ? - Date.new(value.year, value.month, value.day) : value - end - - private def simplified_type(field_type) case field_type @@ -86,21 +54,21 @@ tp[:string][:limit] = 255 tp[:datetime] = { :name => "DATETIME" } tp[:timestamp] = { :name => "DATETIME" } - tp[:time] = { :name => "DATETIME" } - tp[:date] = { :name => "DATETIME" } + tp[:time] = { :name => "TIME" } + tp[:date] = { :name => "DATE" } tp end - def quote(value, column = nil) # :nodoc: + def quote(value, column = nil) # :nodoc: return value.quoted_id if value.respond_to?(:quoted_id) case value when String if column && column.type == :binary - "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'" + "'#{value.unpack("H*")}'" else "'#{quote_string(value)}'" - end + end else super end end Index: src/java/jdbc_adapter/JdbcDerbySpec.java =================================================================== --- src/java/jdbc_adapter/JdbcDerbySpec.java (revision 1130) +++ src/java/jdbc_adapter/JdbcDerbySpec.java (working copy) @@ -71,8 +71,10 @@ case 't': //text, timestamp, time if (type.equals("text")) { return value; - } else { - return rubyApi.callMethod(recv, "cast_to_time", value); + } else if (type.equals("timestamp")) { + return rubyApi.callMethod(recv.getMetaClass(), "string_to_time", value); + } else { //time + return rubyApi.callMethod(recv.getMetaClass(), "string_to_dummy_time", value); } case 'i': //integer case 'p': //primary key @@ -83,7 +85,7 @@ } case 'd': //decimal, datetime, date if (type.equals("datetime")) { - return rubyApi.callMethod(recv, "cast_to_date_or_time", value); + return rubyApi.callMethod(recv.getMetaClass(), "string_to_time", value); } else if (type.equals("date")) { return rubyApi.callMethod(recv.getMetaClass(), "string_to_date", value); } else { @@ -93,7 +95,7 @@ return rubyApi.callMethod(value, "to_f"); case 'b': //binary, boolean if (type.equals("binary")) { - return rubyApi.callMethod(recv, "value_to_binary", value); + return rubyApi.callMethod(recv.getMetaClass(), "binary_to_string", value); } else { return rubyApi.callMethod(recv.getMetaClass(), "value_to_boolean", value); } @@ -115,7 +117,7 @@ } else if (type == runtime.newSymbol("text")) { return quote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS CLOB)"); } else if (type == runtime.newSymbol("binary")) { - return hexquote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS BLOB)"); + return hexquote_string_with_surround(runtime, "CAST(X'", (RubyString)value, "' AS BLOB)"); } else { // column type :integer or other numeric or date version if (only_digits((RubyString)value)) { @@ -164,9 +166,7 @@ return RubyString.objAsString(context, value); } else if(value instanceof RubyBigDecimal) { return rubyApi.callMethod(value, "to_s", runtime.newString("F")); - } else if (rubyApi.isKindOf(value, runtime.getModule("Date"))) { - return quote_string_with_surround(runtime, "'", RubyString.objAsString(context, value), "'"); - } else if (rubyApi.isKindOf(value, runtime.getModule("Time")) || rubyApi.isKindOf(value, runtime.getModule("DateTime"))) { + } else if (rubyApi.callMethod(value, "acts_like?", runtime.newString("date")).isTrue() || rubyApi.callMethod(value, "acts_like?", runtime.newString("time")).isTrue()) { return quote_string_with_surround(runtime, "'", (RubyString)(rubyApi.callMethod(recv, "quoted_date", value)), "'"); } else { return quote_string_with_surround(runtime, "'", (RubyString)(rubyApi.callMethod(value, "to_yaml")), "'"); @@ -199,16 +199,18 @@ private static IRubyObject hexquote_string_with_surround(Ruby runtime, String before, RubyString string, String after) { ByteList input = string.getByteList(); ByteList output = new ByteList(before.getBytes()); + int written = 0; for(int i = input.begin; i< input.begin + input.realSize; i++) { byte b1 = input.bytes[i]; byte higher = HEX[(((char)b1)>>4)%16]; byte lower = HEX[((char)b1)%16]; - if(b1 == '\'') { - output.append(higher); - output.append(lower); - } output.append(higher); output.append(lower); + written += 2; + if(written >= 16334) { // max hex length = 16334 + output.append("'||X'".getBytes()); + written = 0; + } } output.append(after.getBytes());