List:Falcon Storage Engine« Previous MessageNext Message »
From:Olav Sandstaa Date:January 12 2009 11:52am
Subject:Re: Falcon and timestamps on big-endian machines
View as plain text  
Ann and all,

I have started to work on bug #37281 "Time stamps in Falcon on 
big-endian machine give wrong results" and this email contains some 
responses to an email Ann wrote several months ago when I first reported 
the error with timestamps.

A short summary of bug #37281: The code for special handling of 
TIMESTAMPs on big-endian machines in StorageInterface::decodeRecord() 
was removed since it was believed this did not have any function 
(everything within the following #ifdef was removed):


                case MYSQL_TYPE_TIMESTAMP:
                    {
                    int value = (int) (dataStream->value.integer64 / 1000);
#ifdef _BIG_ENDIAN
                    if (table->s->db_low_byte_first)
                        int4store(field->ptr, value);
                    else
#endif
                    longstore(field->ptr, value);
                    }
                    break;

This lead to timestamps no longer working correctly on big-endian machines.


Ann W. Harrison wrote:
>   I still worry about reintroducing the code that was removed without
> understanding exactly why the timestamp should be inverted only on 
> decode.  MySQL can TIMESTAMP fields have different behaviors - some
> are automatically updated.  I think the code that was remove fixes
> an endian problem on automatically updated timestamps, but probably
> messes it up for others.

I agree with you that we should know why we the timestamp should be 
inverted only on decode before we reintroduces the the removed code - 
and I think I now understand why it is needed (see comments further down).

>
>   When a timestamp field with the TIMESTAMP_AUTO_SET_ON_INSERT flag
> set is inserted, ha_falcon.cpp appears to call back into the item
> to get a timestamp value.  That value is little endian, I think.
> Similarly, when a timestamp field update, ha_falcon checks the
> state of the auto update flag, and makes the same call.

You are right about this. In both these cases Falcon calls 
Field_timestamp::set_time() which again calls 
Field_timestamp::store_timestamp() with the following implementation:

  inline void store_timestamp(my_time_t timestamp)
  {
#ifdef WORDS_BIGENDIAN
    if (table && table->s->db_low_byte_first)
    {
      int4store(ptr,timestamp);
    }
    else
#endif
      longstore(ptr,(uint32) timestamp);
  }

This code will store the generated timestamps in little endian format 
both on little endian and big endian machines.

So initially both timetamps sent down from the server and timestamps 
generated by Falcon (when TIMESTAMP_AUTO_SET_ON_INSERT and 
TIMESTAMP_AUTO_SET_ON_UPDATE) are on little-endian format.

>
>   I think that the code that was removed rearranged the automatically
> generated values.  If I'm right, then it should not be used on
> timestamp fields that are not automatically set.  However, a better
> solution, if that is the case, would be to invert the values when
> they are set, so Falcon actually stores all values in big endian
> format on a big endian machine.

I think this is actually the case already (if I understood your 
suggestion correctly) since I believe Falcon stores the timestamps in 
big endian format on big endian machines. For both timestamps sent down 
by the server and the timestamps generated by Falcon, Falcon calls 
StorageInterface::encodeRecord() which has the following code for 
timestamps:

  case MYSQL_TYPE_TIMESTAMP:
                    {
                    my_bool nullValue;
                    int64 value = ((Field_timestamp*) 
field)->get_timestamp(&nullValue);
                    dataStream->encodeDate(value * 1000);
                    }

and the code for the Field_timestamp::get_timestamp() has the following 
implementation:

     inline long get_timestamp(my_bool *null_value)
  {
    if ((*null_value= is_null()))
      return 0;
#ifdef WORDS_BIGENDIAN
    if (table && table->s->db_low_byte_first)
      return sint4korr(ptr);
#endif
    long tmp;
    longget(tmp,ptr);
    return tmp;
  }

So if I have understood the code correctly, when Falcon has finished 
StorageInterface::encodeRecord() the timestamp value is stored in little 
endian format on little endian machines and big endian format on big 
endian machines. And as a further comment to your question about that we 
should understand by Falcon only does the endian conversion on 
::decodeRecord it seems like this is not the case. The same conversion 
takes place also in StorageInterface::encodeRecord() but is less visible 
since it is done in the call-back to Field_timestamp::get_timestamp().

The server wants to have timestamp values returned on a little-endian 
format. So it seems like we need to do the conversion from big-endian to 
little-endian in StorageInterface::decodeRecord() when running on a big 
endian machine. And thus the removed code is actually both needed and 
correct (as far as I can see).

Still, re-introducing the removed code might not be best solution. I 
think a better solution is to use a call-back to 
Field_timestamp::store_timestamp() (see implemention of it above). Thus 
the code in StorageInterface::decodeRecord() could look a like this:

  case MYSQL_TYPE_TIMESTAMP:
    int value = (int) (dataStream->value.integer64 / 1000);
    ((Field_timestamp*)field)->store_timestamp(value);
    break;

This would make the code for decode similar to what we do in 
StorageInterface::encodeRecord(), the endian convertion would be hidden 
in the Field_timestamp;:store_timestamp() implementation and overall 
make the code simpler.

I plan to submit a patch for this shortly which seems to solve all the 
failing test cases related to timestamps on big-endian machines.

Please let me know if anyone have comments or questions to the above or 
to the patch.

Best regards,
Olav



Thread
Re: Falcon and timestamps on big-endian machinesOlav Sandstaa12 Jan