mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			465 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			465 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2004, 2012, Oracle and/or its affiliates.
 | 
						|
   Copyright (c) 2013, MariaDB Foundation.
 | 
						|
 | 
						|
 This program is free software; you can redistribute it and/or modify
 | 
						|
 it under the terms of the GNU General Public License as published by
 | 
						|
 the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
 This program is distributed in the hope that it will be useful,
 | 
						|
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 GNU General Public License for more details.
 | 
						|
 | 
						|
 You should have received a copy of the GNU General Public License
 | 
						|
 along with this program; if not, write to the Free Software
 | 
						|
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | 
						|
 | 
						|
#include "mariadb.h"
 | 
						|
#include "compat56.h"
 | 
						|
#include "myisampack.h"
 | 
						|
#include "my_time.h"
 | 
						|
 | 
						|
 | 
						|
static const int my_max_usec_value[7]
 | 
						|
{
 | 
						|
  0,
 | 
						|
  900000,
 | 
						|
  990000,
 | 
						|
  999000,
 | 
						|
  999900,
 | 
						|
  999990,
 | 
						|
  999999
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/*** MySQL56 TIME low-level memory and disk representation routines ***/
 | 
						|
 | 
						|
/*
 | 
						|
  In-memory format:
 | 
						|
 | 
						|
   1  bit sign          (Used for sign, when on disk)
 | 
						|
   1  bit unused        (Reserved for wider hour range, e.g. for intervals)
 | 
						|
   10 bit hour          (0-836)
 | 
						|
   6  bit minute        (0-59)
 | 
						|
   6  bit second        (0-59)
 | 
						|
  24  bits microseconds (0-999999)
 | 
						|
 | 
						|
 Total: 48 bits = 6 bytes
 | 
						|
   Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert time value to MySQL56 numeric packed representation.
 | 
						|
  
 | 
						|
  @param    ltime   The value to convert.
 | 
						|
  @return           Numeric packed representation.
 | 
						|
*/
 | 
						|
longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(ltime->year == 0);
 | 
						|
  DBUG_ASSERT(ltime->month == 0);
 | 
						|
  // Mix days with hours: "1 00:10:10" -> "24:10:10"
 | 
						|
  long hms= ((ltime->day * 24 + ltime->hour) << 12) |
 | 
						|
            (ltime->minute << 6) | ltime->second;
 | 
						|
  longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
 | 
						|
  return ltime->neg ? -tmp : tmp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert MySQL56 time packed numeric representation to time.
 | 
						|
 | 
						|
  @param  OUT ltime  The MYSQL_TIME variable to set.
 | 
						|
  @param      tmp    The packed numeric representation.
 | 
						|
*/
 | 
						|
void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
 | 
						|
{
 | 
						|
  long hms;
 | 
						|
  if ((ltime->neg= (tmp < 0)))
 | 
						|
    tmp= -tmp;
 | 
						|
  hms= (long) MY_PACKED_TIME_GET_INT_PART(tmp);
 | 
						|
  ltime->year=   (uint) 0;
 | 
						|
  ltime->month=  (uint) 0;
 | 
						|
  ltime->day=    (uint) 0;
 | 
						|
  ltime->hour=   (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
 | 
						|
  ltime->minute= (uint) (hms >> 6)  % (1 << 6);  /* 6 bits starting at 6th   */
 | 
						|
  ltime->second= (uint)  hms        % (1 << 6);  /* 6 bits starting at 0th   */
 | 
						|
  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
 | 
						|
  ltime->time_type= MYSQL_TIMESTAMP_TIME;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate binary size of MySQL56 packed numeric time representation.
 | 
						|
  
 | 
						|
  @param   dec   Precision.
 | 
						|
*/
 | 
						|
uint my_time_binary_length(uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  return 3 + (dec + 1) / 2;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  On disk we convert from signed representation to unsigned
 | 
						|
  representation using TIMEF_OFS, so all values become binary comparable.
 | 
						|
*/
 | 
						|
#define TIMEF_OFS 0x800000000000LL
 | 
						|
#define TIMEF_INT_OFS 0x800000LL
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert MySQL56 in-memory numeric time representation to on-disk representation
 | 
						|
  
 | 
						|
  @param       nr   Value in packed numeric time format.
 | 
						|
  @param   OUT ptr  The buffer to put value at.
 | 
						|
  @param       dec  Precision.
 | 
						|
*/
 | 
						|
void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  /* Make sure the stored value was previously properly rounded or truncated */
 | 
						|
  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) % 
 | 
						|
              (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
 | 
						|
 | 
						|
  switch (dec)
 | 
						|
  {
 | 
						|
  case 0:
 | 
						|
  default:
 | 
						|
    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
 | 
						|
    break;
 | 
						|
 | 
						|
  case 1:
 | 
						|
  case 2:
 | 
						|
    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
 | 
						|
    ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
 | 
						|
    break;
 | 
						|
 | 
						|
  case 4:
 | 
						|
  case 3:
 | 
						|
    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
 | 
						|
    mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
 | 
						|
    break;
 | 
						|
 | 
						|
  case 5:
 | 
						|
  case 6:
 | 
						|
    mi_int6store(ptr, nr + TIMEF_OFS);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert MySQL56 on-disk time representation to in-memory packed numeric 
 | 
						|
  representation.
 | 
						|
  
 | 
						|
  @param   ptr  The pointer to read the value at.
 | 
						|
  @param   dec  Precision.
 | 
						|
  @return       Packed numeric time representation.
 | 
						|
*/
 | 
						|
longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
 | 
						|
  switch (dec)
 | 
						|
  {
 | 
						|
  case 0:
 | 
						|
  default:
 | 
						|
    {
 | 
						|
      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
 | 
						|
      return MY_PACKED_TIME_MAKE_INT(intpart);
 | 
						|
    }
 | 
						|
  case 1:
 | 
						|
  case 2:
 | 
						|
    {
 | 
						|
      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
 | 
						|
      int frac= (uint) ptr[3];
 | 
						|
      if (intpart < 0 && frac)
 | 
						|
      {
 | 
						|
        /*
 | 
						|
          Negative values are stored with reverse fractional part order,
 | 
						|
          for binary sort compatibility.
 | 
						|
 | 
						|
            Disk value  intpart frac   Time value   Memory value
 | 
						|
            800000.00    0      0      00:00:00.00  0000000000.000000
 | 
						|
            7FFFFF.FF   -1      255   -00:00:00.01  FFFFFFFFFF.FFD8F0
 | 
						|
            7FFFFF.9D   -1      99    -00:00:00.99  FFFFFFFFFF.F0E4D0
 | 
						|
            7FFFFF.00   -1      0     -00:00:01.00  FFFFFFFFFF.000000
 | 
						|
            7FFFFE.FF   -1      255   -00:00:01.01  FFFFFFFFFE.FFD8F0
 | 
						|
            7FFFFE.F6   -2      246   -00:00:01.10  FFFFFFFFFE.FE7960
 | 
						|
 | 
						|
            Formula to convert fractional part from disk format
 | 
						|
            (now stored in "frac" variable) to absolute value: "0x100 - frac".
 | 
						|
            To reconstruct in-memory value, we shift
 | 
						|
            to the next integer value and then substruct fractional part.
 | 
						|
        */
 | 
						|
        intpart++;    /* Shift to the next integer value */
 | 
						|
        frac-= 0x100; /* -(0x100 - frac) */
 | 
						|
      }
 | 
						|
      return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
 | 
						|
    }
 | 
						|
 | 
						|
  case 3:
 | 
						|
  case 4:
 | 
						|
    {
 | 
						|
      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
 | 
						|
      int frac= mi_uint2korr(ptr + 3);
 | 
						|
      if (intpart < 0 && frac)
 | 
						|
      {
 | 
						|
        /*
 | 
						|
          Fix reverse fractional part order: "0x10000 - frac".
 | 
						|
          See comments for FSP=1 and FSP=2 above.
 | 
						|
        */
 | 
						|
        intpart++;      /* Shift to the next integer value */
 | 
						|
        frac-= 0x10000; /* -(0x10000-frac) */
 | 
						|
      }
 | 
						|
      return MY_PACKED_TIME_MAKE(intpart, frac * 100);
 | 
						|
    }
 | 
						|
 | 
						|
  case 5:
 | 
						|
  case 6:
 | 
						|
    return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*** MySQL56 DATETIME low-level memory and disk representation routines ***/
 | 
						|
 | 
						|
/*
 | 
						|
    1 bit  sign            (used when on disk)
 | 
						|
   17 bits year*13+month   (year 0-9999, month 0-12)
 | 
						|
    5 bits day             (0-31)
 | 
						|
    5 bits hour            (0-23)
 | 
						|
    6 bits minute          (0-59)
 | 
						|
    6 bits second          (0-59)
 | 
						|
   24 bits microseconds    (0-999999)
 | 
						|
 | 
						|
   Total: 64 bits = 8 bytes
 | 
						|
 | 
						|
   SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
 | 
						|
*/
 | 
						|
 | 
						|
/**
 | 
						|
  Convert datetime to MySQL56 packed numeric datetime representation.
 | 
						|
  @param ltime  The value to convert.
 | 
						|
  @return       Packed numeric representation of ltime.
 | 
						|
*/
 | 
						|
longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
 | 
						|
{
 | 
						|
  longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
 | 
						|
  longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
 | 
						|
  longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
 | 
						|
  DBUG_ASSERT(!check_datetime_range(ltime)); /* Make sure no overflow */
 | 
						|
  return ltime->neg ? -tmp : tmp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert MySQL56 packed numeric datetime representation to MYSQL_TIME.
 | 
						|
  @param OUT  ltime The datetime variable to convert to.
 | 
						|
  @param      tmp   The packed numeric datetime value.
 | 
						|
*/
 | 
						|
void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
 | 
						|
{
 | 
						|
  longlong ymd, hms;
 | 
						|
  longlong ymdhms, ym;
 | 
						|
 | 
						|
  DBUG_ASSERT(tmp != LONGLONG_MIN);
 | 
						|
 | 
						|
  if ((ltime->neg= (tmp < 0)))
 | 
						|
    tmp= -tmp;
 | 
						|
 | 
						|
  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
 | 
						|
  ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
 | 
						|
 | 
						|
  ymd= ymdhms >> 17;
 | 
						|
  ym= ymd >> 5;
 | 
						|
  hms= ymdhms % (1 << 17);
 | 
						|
 | 
						|
  ltime->day= ymd % (1 << 5);
 | 
						|
  ltime->month= ym % 13;
 | 
						|
  ltime->year= (uint) (ym / 13);
 | 
						|
 | 
						|
  ltime->second= hms % (1 << 6);
 | 
						|
  ltime->minute= (hms >> 6) % (1 << 6);
 | 
						|
  ltime->hour= (uint) (hms >> 12);
 | 
						|
  
 | 
						|
  ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate binary size of MySQL56 packed datetime representation.
 | 
						|
  @param dec  Precision.
 | 
						|
*/
 | 
						|
uint my_datetime_binary_length(uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  return 5 + (dec + 1) / 2;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
 | 
						|
  for HA_KETYPE_BINARY compatibility purposes.
 | 
						|
*/
 | 
						|
#define DATETIMEF_INT_OFS 0x8000000000LL
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert MySQL56 on-disk datetime representation
 | 
						|
  to in-memory packed numeric representation.
 | 
						|
 | 
						|
  @param ptr   The pointer to read value at.
 | 
						|
  @param dec   Precision.
 | 
						|
  @return      In-memory packed numeric datetime representation.
 | 
						|
*/
 | 
						|
longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
 | 
						|
{
 | 
						|
  longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
 | 
						|
  int frac;
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  switch (dec)
 | 
						|
  {
 | 
						|
  case 0:
 | 
						|
  default:
 | 
						|
    return MY_PACKED_TIME_MAKE_INT(intpart);
 | 
						|
  case 1:
 | 
						|
  case 2:
 | 
						|
    frac= ((int) (signed char) ptr[5]) * 10000;
 | 
						|
    break;
 | 
						|
  case 3:
 | 
						|
  case 4:
 | 
						|
    frac= mi_sint2korr(ptr + 5) * 100;
 | 
						|
    break;
 | 
						|
  case 5:
 | 
						|
  case 6:
 | 
						|
    frac= mi_sint3korr(ptr + 5);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return MY_PACKED_TIME_MAKE(intpart, frac);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Store MySQL56 in-memory numeric packed datetime representation to disk.
 | 
						|
 | 
						|
  @param      nr  In-memory numeric packed datetime representation.
 | 
						|
  @param OUT  ptr The pointer to store at.
 | 
						|
  @param      dec Precision, 1-6.
 | 
						|
*/
 | 
						|
void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  /* The value being stored must have been properly rounded or truncated */
 | 
						|
  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
 | 
						|
              (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
 | 
						|
 | 
						|
  mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
 | 
						|
  switch (dec)
 | 
						|
  {
 | 
						|
  case 0:
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  case 1:
 | 
						|
  case 2:
 | 
						|
    ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
 | 
						|
    break;
 | 
						|
  case 3:
 | 
						|
  case 4:
 | 
						|
    mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
 | 
						|
    break;
 | 
						|
  case 5:
 | 
						|
  case 6:
 | 
						|
    mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*** MySQL56 TIMESTAMP low-level memory and disk representation routines ***/
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate on-disk size of a timestamp value.
 | 
						|
 | 
						|
  @param  dec  Precision.
 | 
						|
*/
 | 
						|
uint my_timestamp_binary_length(uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  return 4 + (dec + 1) / 2;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert MySQL56 binary timestamp representation to in-memory representation.
 | 
						|
 | 
						|
  @param  OUT tm  The variable to convert to.
 | 
						|
  @param      ptr The pointer to read the value from.
 | 
						|
  @param      dec Precision.
 | 
						|
*/
 | 
						|
void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  tm->tv_sec= mi_uint4korr(ptr);
 | 
						|
  switch (dec)
 | 
						|
  {
 | 
						|
    case 0:
 | 
						|
    default:
 | 
						|
      tm->tv_usec= 0;
 | 
						|
      return;
 | 
						|
    case 1:
 | 
						|
    case 2:
 | 
						|
      tm->tv_usec= ((int) ptr[4]) * 10000;
 | 
						|
      break;
 | 
						|
    case 3:
 | 
						|
    case 4:
 | 
						|
      tm->tv_usec= (uint) mi_uint2korr(ptr + 4) * 100;
 | 
						|
      break;
 | 
						|
    case 5:
 | 
						|
    case 6:
 | 
						|
      tm->tv_usec= (uint) mi_uint3korr(ptr + 4);
 | 
						|
  }
 | 
						|
  // The binary data my be corrupt. Cut fractional seconds to the valid range.
 | 
						|
  set_if_smaller(tm->tv_usec, my_max_usec_value[dec]);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert MySQL56 in-memory timestamp representation to on-disk representation.
 | 
						|
 | 
						|
  @param        tm   The value to convert.
 | 
						|
  @param  OUT   ptr  The pointer to store the value to.
 | 
						|
  @param        dec  Precision.
 | 
						|
*/
 | 
						|
void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
 | 
						|
  /* Stored value must have been previously properly rounded or truncated */
 | 
						|
  DBUG_ASSERT((tm->tv_usec %
 | 
						|
               (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
 | 
						|
  mi_int4store(ptr, tm->tv_sec);
 | 
						|
  switch (dec)
 | 
						|
  {
 | 
						|
    case 0:
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
    case 1:
 | 
						|
    case 2:
 | 
						|
      ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
 | 
						|
      break;
 | 
						|
    case 3:
 | 
						|
    case 4:
 | 
						|
      mi_int2store(ptr + 4, tm->tv_usec / 100);
 | 
						|
      break;
 | 
						|
      /* Impossible second precision. Fall through */
 | 
						|
    case 5:
 | 
						|
    case 6:
 | 
						|
      mi_int3store(ptr + 4, tm->tv_usec);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/****************************************/
 |