Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #7699 (closed defect: fixed)

Opened 3 years ago

Last modified 3 years ago

Handling of binary fields in SQL Server adapter seems to be broken

Reported by: al Assigned to: molla5
Priority: normal Milestone: 1.x
Component: ActiveRecord Version: edge
Severity: normal Keywords: binary sqlserver
Cc:

Description

I'm running into a problem related to the way binary fields are handled. Let's say I have a binary field set to the value 0x00. Rails (namely the SQL Server adapter) converts it to the string "00". But when I try to save I get an exception, because the adapter does not perform the reverse conversion.

Here is an example. If we have a table created like this in SQL Server:

 CREATE TABLE [dbo].[binary_tests] (
            [id] [int] IDENTITY (1, 1) NOT FOR REPLICATION NOT NULL ,
            [binary_field] [binary] (1) NULL ,
          ) ON [PRIMARY]; 

And we insert a record like this:

INSERT INTO binary_tests (binary_field) VALUES (0x00);

Here is what we get at the Rails level:

./script/console                       
Loading development environment.
>> record = BinaryTest.find(:first)
=> #<BinaryTest:0xb687b0d4 @attributes={"id"=>1, "binary_field"=>"00"}>
>> record.binary_field
=> "00"
>> record.save
ActiveRecord::StatementInvalid: DBI::DatabaseError:  (260)
[unixODBC][FreeTDS][SQL Server]Disallowed implicit conversion from data
type varchar to data type binary, table
'foo_development.dbo.binary_tests', column 'binary_field'. Use the
CONVERT function to run this query.: UPDATE dbo.binary_tests SET
[binary_field] = '00' WHERE id = 1
        from
./script/../config/../config/../lib/active_record/connection_adapters/abstract_adapter.rb:122:in
`log'
        from
./script/../config/../config/../lib/active_record/connection_adapters/sqlserver_adapter.rb:317:in
`execute'
        from
./script/../config/../config/../lib/active_record/connection_adapters/sqlserver_adapter.rb:300:in
`update'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/base.rb:1723:in
`update_without_lock'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/locking.rb:33:in
`update_without_callbacks'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/callbacks.rb:278:in
`update_without_timestamps'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/timestamp.rb:39:in
`update'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/base.rb:1718:in
`create_or_update_without_callbacks'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/callbacks.rb:253:in
`create_or_update'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/base.rb:1392:in
`save_without_validation'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/validations.rb:736:in
`save_without_transactions'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:126:in
`save'
        from
./script/../config/../config/../lib/active_record/connection_adapters/abstract/database_statements.rb:59:in
`transaction'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:91:in
`transaction'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:118:in
`transaction'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:126:in
`save'
        from (irb):3>>

Now let's try to insert values likely to be accepted as binary:

>> record.binary_field = 1
=> 1
>> record.binary_field
NoMethodError: private method `gsub' called for 1:Fixnum
        from
./script/../config/../config/../lib/active_record/connection_adapters/sqlserver_adapter.rb:136:in
`binary_to_string'
        from
./script/../config/../config/../lib/active_record/connection_adapters/abstract/schema_definitions.rb:64:in
`type_cast'
        from
./script/../config/../config/../lib/active_record/connection_adapters/sqlserver_adapter.rb:83:in
`type_cast'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/base.rb:1805:in
`read_attribute'
        from
./script/../config/../config/../vendor/rails/activerecord/lib/active_record/base.rb:1776:in
`method_missing'
        from (irb):3
>> record.binary_field = 0x01
=> 1
>> record.binary_field
NoMethodError: private method `gsub' called for 1:Fixnum
        from
./script/../config/../config/../lib/active_record/connection_adapters/sqlserver_adapter.rb:136:in
`binary_to_string'
        from (eval):1:in `binary_field'
        from (irb):5

Change History

04/04/07 14:11:12 changed by molla5

  • owner changed from tomafro to molla5.
  • status changed from new to assigned.

I had a similar problem with sqlserver_adapter. I modified the following methods in the sqlserver_adapter.rb as follows and things seems to work fine for me.

 def self.string_to_binary(value)
   Base64.encode64(value)
 end

 def self.binary_to_string(value)
   Base64.decode64(value)
 end

07/03/07 12:23:03 changed by tomafro

  • status changed from assigned to closed.
  • resolution set to fixed.

Fixed in patch attached to #7987