Firebird .NET Data Provider reads Guids incorrectly on little-endian systems
----------------------------------------------------------------------------

                 Key: DNET-509
                 URL: http://tracker.firebirdsql.org/browse/DNET-509
             Project: .NET Data provider
          Issue Type: Bug
          Components: ADO.NET Provider
    Affects Versions: 3.0.2.0
            Reporter: Herman Schoenfeld
            Assignee: Jiri Cincura


On a little-endian systems Guids values are not being correctly hydrated by the 
Firebird ADO.NET provider.

For example, a user may insert a Guid via a query of the form 

  ..." INSERT INTO MyTable VALUES (UUID_TO_CHAR(" + guid.ToString() + " ))"

But when that user loads the Guid back via the provider (on a little endian 
system), the value is different than the correct value stored on the database 
value.

The problem is that Uuid & Guid vary in storage format but not it presentation 
format.The  The CHAR(16) octets contain the Uuid bytes in big endian layout. 
When hydrating these bytes into a Guid, the provider does not compensate for 
the Guid's storage of certain parts as the native endian. The correct way to 
parse the uuid into a guid is:

                        var rfc4122bytes =  ... uuid byte array returned from 
firebird ..
                        if (BitConverter.IsLittleEndian) {
                                Array.Reverse(rfc4122bytes, 0, 4);
                                Array.Reverse(rfc4122bytes, 4, 2);
                                Array.Reverse(rfc4122bytes, 6, 2);
                        }
                        var guid = new Guid(rfc4122bytes);

Suspected location of bug is in class 'DbValue' method 'GetGuid'.

As a work-around, I've written a wrapper for the FbDataReader which will 
correct the invalid guid value.

        public class FirebirdCorrectingReader : IDataReader {
                private readonly IDataReader _decoratedReader;

                public FirebirdCorrectingReader(IDataReader decoratedReader) {
                        _decoratedReader = decoratedReader;     
                }

                #region IDataReader Impl

                public void Dispose() {
                        _decoratedReader.Dispose();
                }

                public string GetName(int i) {
                        return _decoratedReader.GetName(i);
                }

                public string GetDataTypeName(int i) {
                        return _decoratedReader.GetDataTypeName(i);
                }

                public Type GetFieldType(int i) {
                        return _decoratedReader.GetFieldType(i);
                }

                public object GetValue(int i) {
                        var result = _decoratedReader.GetValue(i);
                        if (result is Guid) {
                                result = CorrectGuid((Guid)result);
                        }
                        return result;
                }

                public int GetValues(object[] values) {
                        return _decoratedReader.GetValues(values);
                }

                public int GetOrdinal(string name) {
                        return _decoratedReader.GetOrdinal(name);
                }

                public bool GetBoolean(int i) {
                        return _decoratedReader.GetBoolean(i);
                }

                public byte GetByte(int i) {
                        return _decoratedReader.GetByte(i);
                }

                public long GetBytes(int i, long fieldOffset, byte[] buffer, 
int bufferoffset, int length) {
                        return _decoratedReader.GetBytes(i, fieldOffset, 
buffer, bufferoffset, length);
                }

                public char GetChar(int i) {
                        return _decoratedReader.GetChar(i);
                }

                public long GetChars(int i, long fieldoffset, char[] buffer, 
int bufferoffset, int length) {
                        return _decoratedReader.GetChars(i, fieldoffset, 
buffer, bufferoffset, length);
                }

                public Guid GetGuid(int i) {
                        return CorrectGuid(_decoratedReader.GetGuid(i));
                }

                public short GetInt16(int i) {
                        return _decoratedReader.GetInt16(i);
                }

                public int GetInt32(int i) {
                        return _decoratedReader.GetInt32(i);
                }

                public long GetInt64(int i) {
                        return _decoratedReader.GetInt64(i);
                }

                public float GetFloat(int i) {
                        return _decoratedReader.GetFloat(i);
                }

                public double GetDouble(int i) {
                        return _decoratedReader.GetDouble(i);
                }

                public string GetString(int i) {
                        return _decoratedReader.GetString(i);
                }

                public decimal GetDecimal(int i) {
                        return _decoratedReader.GetDecimal(i);
                }

                public DateTime GetDateTime(int i) {
                        return _decoratedReader.GetDateTime(i);
                }

                public IDataReader GetData(int i) {
                        return _decoratedReader.GetData(i);
                }

                public bool IsDBNull(int i) {
                        return _decoratedReader.IsDBNull(i);
                }

                public int FieldCount { get { return 
_decoratedReader.FieldCount; } }

                object IDataRecord.this[int i] {
                        get { return _decoratedReader[i]; }
                }

                object IDataRecord.this[string name] {
                        get {return _decoratedReader[name]; }
                }

                public void Close() {
                        _decoratedReader.Close();
                }

                public DataTable GetSchemaTable() {
                        return _decoratedReader.GetSchemaTable();
                }

                public bool NextResult() {
                        return _decoratedReader.NextResult();
                }

                public bool Read() {
                        return _decoratedReader.Read();
                }

                public int Depth { get { return _decoratedReader.Depth; } }
                public bool IsClosed { get { return _decoratedReader.IsClosed; 
} }
                public int RecordsAffected { get { return 
_decoratedReader.RecordsAffected; } }

                #endregion

                public static Guid CorrectGuid(Guid badlyParsedGuid) {
                        var rfc4122bytes = badlyParsedGuid.ToByteArray();
                        if (BitConverter.IsLittleEndian) {
                                Array.Reverse(rfc4122bytes, 0, 4);
                                Array.Reverse(rfc4122bytes, 4, 2);
                                Array.Reverse(rfc4122bytes, 6, 2);
                        }
                        return new Guid(rfc4122bytes);
                }
        }


-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: 
http://tracker.firebirdsql.org/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

------------------------------------------------------------------------------
This SF.net email is sponsored by Windows:

Build for Windows Store.

http://p.sf.net/sfu/windows-dev2dev
_______________________________________________
Firebird-net-provider mailing list
Firebird-net-provider@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/firebird-net-provider

Reply via email to