610 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			610 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights
 | |
| reserved.
 | |
| 
 | |
| 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-1301  USA
 | |
| */
 | |
| #include "value.h"
 | |
| #include "binlog_event.h"
 | |
| #include <boost/lexical_cast.hpp>
 | |
| #include <iomanip>
 | |
| #include <boost/format.hpp>
 | |
| #include <mysql.h>
 | |
| 
 | |
| #define DIG_PER_DEC1 9
 | |
| 
 | |
| using namespace mysql;
 | |
| using namespace mysql::system;
 | |
| namespace mysql {
 | |
| 
 | |
| static const int dig2bytes[DIG_PER_DEC1 + 1] = {0, 1, 1, 2, 2, 3, 3, 4, 4, 4};
 | |
| 
 | |
| int decimal_bin_size(int precision, int scale)
 | |
| {
 | |
|   int intg   = precision - scale;
 | |
|   int intg0  = intg / DIG_PER_DEC1;
 | |
|   int frac0  = scale / DIG_PER_DEC1;
 | |
|   int intg0x = intg - intg0 * DIG_PER_DEC1;
 | |
|   int frac0x = scale - frac0 * DIG_PER_DEC1;
 | |
| 
 | |
|   return(
 | |
|     intg0 * sizeof(boost::int32_t) + dig2bytes[intg0x] +
 | |
|     frac0 * sizeof(boost::int32_t) + dig2bytes[frac0x]
 | |
|     );
 | |
| }
 | |
| 
 | |
| int calc_field_size(unsigned char column_type, const unsigned char *field_ptr, boost::uint32_t metadata)
 | |
| {
 | |
|   boost::uint32_t length;
 | |
| 
 | |
|   switch (column_type) {
 | |
|   case MYSQL_TYPE_VAR_STRING:
 | |
|     /* This type is hijacked for result set types. */
 | |
|     length= metadata;
 | |
|     break;
 | |
|   case MYSQL_TYPE_NEWDECIMAL:
 | |
|   {
 | |
|     int precision = (metadata & 0xff);
 | |
|     int scale = metadata >> 8;
 | |
|     length = decimal_bin_size(precision, scale);
 | |
|     break;
 | |
|   }
 | |
|   case MYSQL_TYPE_DECIMAL:
 | |
|   case MYSQL_TYPE_FLOAT:
 | |
|   case MYSQL_TYPE_DOUBLE:
 | |
|     length= metadata;
 | |
|     break;
 | |
|   /*
 | |
|     The cases for SET and ENUM are include for completeness, however
 | |
|     both are mapped to type MYSQL_TYPE_STRING and their real types
 | |
|     are encoded in the field metadata.
 | |
|   */
 | |
|   case MYSQL_TYPE_SET:
 | |
|   case MYSQL_TYPE_ENUM:
 | |
|   case MYSQL_TYPE_STRING:
 | |
|   {
 | |
|     //unsigned char type= metadata >> 8U;
 | |
|     unsigned char type = metadata & 0xff;
 | |
|     if ((type == MYSQL_TYPE_SET) || (type == MYSQL_TYPE_ENUM))
 | |
|     {
 | |
|       //length= metadata & 0x00ff;
 | |
|       length = (metadata & 0xff00) >> 8;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       /*
 | |
|         We are reading the actual size from the master_data record
 | |
|         because this field has the actual lengh stored in the first
 | |
|         byte.
 | |
|       */
 | |
|       length= (unsigned int) *field_ptr+1;
 | |
|       //DBUG_ASSERT(length != 0);
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   case MYSQL_TYPE_YEAR:
 | |
|   case MYSQL_TYPE_TINY:
 | |
|     length= 1;
 | |
|     break;
 | |
|   case MYSQL_TYPE_SHORT:
 | |
|     length= 2;
 | |
|     break;
 | |
|   case MYSQL_TYPE_INT24:
 | |
|     length= 3;
 | |
|     break;
 | |
|   case MYSQL_TYPE_LONG:
 | |
|     length= 4;
 | |
|     break;
 | |
|   case MYSQL_TYPE_LONGLONG:
 | |
|     length= 8;
 | |
|     break;
 | |
|   case MYSQL_TYPE_NULL:
 | |
|     length= 0;
 | |
|     break;
 | |
|   case MYSQL_TYPE_NEWDATE:
 | |
|     length= 3;
 | |
|     break;
 | |
|   case MYSQL_TYPE_DATE:
 | |
|   case MYSQL_TYPE_TIME:
 | |
|     length= 3;
 | |
|     break;
 | |
|   case MYSQL_TYPE_TIMESTAMP:
 | |
|     length= 4;
 | |
|     break;
 | |
|   case MYSQL_TYPE_DATETIME:
 | |
|     length= 8;
 | |
|     break;
 | |
|   case MYSQL_TYPE_BIT:
 | |
|   {
 | |
|     /*
 | |
|       Decode the size of the bit field from the master.
 | |
|         from_len is the length in bytes from the master
 | |
|         from_bit_len is the number of extra bits stored in the master record
 | |
|       If from_bit_len is not 0, add 1 to the length to account for accurate
 | |
|       number of bytes needed.
 | |
|     */
 | |
| 	boost::uint32_t from_len= (metadata >> 8U) & 0x00ff;
 | |
| 	boost::uint32_t from_bit_len= metadata & 0x00ff;
 | |
|     //DBUG_ASSERT(from_bit_len <= 7);
 | |
|     length= from_len + ((from_bit_len > 0) ? 1 : 0);
 | |
|     break;
 | |
|   }
 | |
|   case MYSQL_TYPE_VARCHAR:
 | |
|   {
 | |
|     length= metadata > 255 ? 2 : 1;
 | |
|     length+= length == 1 ? (boost::uint32_t) *field_ptr : *((boost::uint16_t *)field_ptr);
 | |
|     break;
 | |
|   }
 | |
|   case MYSQL_TYPE_TINY_BLOB:
 | |
|   case MYSQL_TYPE_MEDIUM_BLOB:
 | |
|   case MYSQL_TYPE_LONG_BLOB:
 | |
|   case MYSQL_TYPE_BLOB:
 | |
|   case MYSQL_TYPE_GEOMETRY:
 | |
|   {
 | |
|      switch (metadata)
 | |
|     {
 | |
|       case 1:
 | |
|         length= 1+ (boost::uint32_t) field_ptr[0];
 | |
|         break;
 | |
|       case 2:
 | |
|         length= 2+ (boost::uint32_t) (*(boost::uint16_t *)(field_ptr) & 0xFFFF);
 | |
|         break;
 | |
|       case 3:
 | |
|         // TODO make platform indep.
 | |
|         length= 3+ (boost::uint32_t) (long) (*((boost::uint32_t *) (field_ptr)) & 0xFFFFFF);
 | |
|         break;
 | |
|       case 4:
 | |
|         // TODO make platform indep.
 | |
|         length= 4+ (boost::uint32_t) (long) *((boost::uint32_t *) (field_ptr));
 | |
|         break;
 | |
|       default:
 | |
|         length= 0;
 | |
|         break;
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   default:
 | |
|     length= ~(boost::uint32_t) 0;
 | |
|   }
 | |
|   return length;
 | |
| }
 | |
| 
 | |
| /*
 | |
| Value::Value(Value &val)
 | |
| {
 | |
|   m_size= val.length();
 | |
|   m_storage= val.storage();
 | |
|   m_type= val.type();
 | |
|   m_metadata= val.metadata();
 | |
|   m_is_null= val.is_null();
 | |
| }
 | |
| */
 | |
| 
 | |
| Value::Value(const Value& val)
 | |
| {
 | |
|   m_size= val.m_size;
 | |
|   m_storage= val.m_storage;
 | |
|   m_type= val.m_type;
 | |
|   m_metadata= val.m_metadata;
 | |
|   m_is_null= val.m_is_null;
 | |
| }
 | |
| 
 | |
| Value &Value::operator=(const Value &val)
 | |
| {
 | |
|   m_size= val.m_size;
 | |
|   m_storage= val.m_storage;
 | |
|   m_type= val.m_type;
 | |
|   m_metadata= val.m_metadata;
 | |
|   m_is_null= val.m_is_null;
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| bool Value::operator==(const Value &val) const
 | |
| {
 | |
|   return (m_size == val.m_size) &&
 | |
|          (m_storage == val.m_storage) &&
 | |
|          (m_type == val.m_type) &&
 | |
|          (m_metadata == val.m_metadata);
 | |
| }
 | |
| 
 | |
| bool Value::operator!=(const Value &val) const
 | |
| {
 | |
|   return !operator==(val);
 | |
| }
 | |
| 
 | |
| char *Value::as_c_str(unsigned long &size) const
 | |
| {
 | |
|   if (m_is_null || m_size == 0)
 | |
|   {
 | |
|     size= 0;
 | |
|     return 0;
 | |
|   }
 | |
|   /*
 | |
|    Length encoded; First byte is length of string.
 | |
|   */
 | |
|   int metadata_length= m_size > 251 ? 2: 1;
 | |
|   /*
 | |
|    Size is length of the character string; not of the entire storage
 | |
|   */
 | |
|   size= m_size - metadata_length;
 | |
| 
 | |
|   char *str = const_cast<char *>(m_storage + metadata_length);
 | |
| 
 | |
|   if (m_type == MYSQL_TYPE_VARCHAR && m_metadata > 255) {
 | |
|     str++;
 | |
|     size--;
 | |
|   }
 | |
| 
 | |
|   return str;
 | |
| }
 | |
| 
 | |
| unsigned char *Value::as_blob(unsigned long &size) const
 | |
| {
 | |
|   if (m_is_null || m_size == 0)
 | |
|   {
 | |
|     size= 0;
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    Size was calculated during construction of the object and only inludes the
 | |
|    size of the blob data, not the metadata part which also is stored in the
 | |
|    storage. For blobs this part can be between 1-4 bytes long.
 | |
|   */
 | |
|   size= m_size - m_metadata;
 | |
| 
 | |
|   /*
 | |
|    Adjust the storage pointer with the size of the metadata.
 | |
|   */
 | |
|   return (unsigned char *)(m_storage + m_metadata);
 | |
| }
 | |
| 
 | |
| boost::int32_t Value::as_int32() const
 | |
| {
 | |
|   if (m_is_null)
 | |
|   {
 | |
|     return 0;
 | |
|   }
 | |
|   boost::uint32_t to_int;
 | |
|   Protocol_chunk<boost::uint32_t> prot_integer(to_int);
 | |
| 
 | |
|   buffer_source buff(m_storage, m_size);
 | |
|   buff >> prot_integer;
 | |
|   return to_int;
 | |
| }
 | |
| 
 | |
| boost::int8_t Value::as_int8() const
 | |
| {
 | |
|   if (m_is_null)
 | |
|   {
 | |
|     return 0;
 | |
|   }
 | |
|   boost::int8_t to_int;
 | |
|   Protocol_chunk<boost::int8_t> prot_integer(to_int);
 | |
| 
 | |
|   buffer_source buff(m_storage, m_size);
 | |
|   buff >> prot_integer;
 | |
|   return to_int;
 | |
| }
 | |
| 
 | |
| boost::int16_t Value::as_int16() const
 | |
| {
 | |
|   if (m_is_null)
 | |
|   {
 | |
|     return 0;
 | |
|   }
 | |
|   boost::int16_t to_int;
 | |
|   Protocol_chunk<boost::int16_t> prot_integer(to_int);
 | |
| 
 | |
|   buffer_source buff(m_storage, m_size);
 | |
|   buff >> prot_integer;
 | |
|   return to_int;
 | |
| }
 | |
| 
 | |
| boost::int64_t Value::as_int64() const
 | |
| {
 | |
|   if (m_is_null)
 | |
|   {
 | |
|     return 0;
 | |
|   }
 | |
|   boost::int64_t to_int;
 | |
|   Protocol_chunk<boost::int64_t> prot_integer(to_int);
 | |
| 
 | |
|   buffer_source buff(m_storage, m_size);
 | |
|   buff >> prot_integer;
 | |
|   return to_int;
 | |
| }
 | |
| 
 | |
| float Value::as_float() const
 | |
| {
 | |
|   // TODO
 | |
|   return *((const float *)storage());
 | |
| }
 | |
| 
 | |
| double Value::as_double() const
 | |
| {
 | |
|   // TODO
 | |
|   return *((const double *)storage());
 | |
| }
 | |
| 
 | |
| void Converter::to(std::string &str, const Value &val) const
 | |
| {
 | |
|   if (val.is_null())
 | |
|   {
 | |
|     str= "(NULL)";
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   switch(val.type())
 | |
|   {
 | |
|     case MYSQL_TYPE_DECIMAL:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_TINY:
 | |
|       str= boost::lexical_cast<std::string>(static_cast<int>(val.as_int8()));
 | |
|       break;
 | |
|     case MYSQL_TYPE_SHORT:
 | |
|       str= boost::lexical_cast<std::string>(val.as_int16());
 | |
|       break;
 | |
|     case MYSQL_TYPE_LONG:
 | |
|       str= boost::lexical_cast<std::string>(val.as_int32());
 | |
|       break;
 | |
|     case MYSQL_TYPE_FLOAT:
 | |
|     {
 | |
|       str= boost::str(boost::format("%d") % val.as_float());
 | |
|     }
 | |
|       break;
 | |
|     case MYSQL_TYPE_DOUBLE:
 | |
|       str= boost::str(boost::format("%d") % val.as_double());
 | |
|       break;
 | |
|     case MYSQL_TYPE_NULL:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_TIMESTAMP:
 | |
|       str= boost::lexical_cast<std::string>((boost::uint32_t)val.as_int32());
 | |
|       break;
 | |
| 
 | |
|     case MYSQL_TYPE_LONGLONG:
 | |
|       str= boost::lexical_cast<std::string>(val.as_int64());
 | |
|       break;
 | |
|     case MYSQL_TYPE_INT24:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_DATE:
 | |
|     {
 | |
|       const char* val_storage = val.storage();
 | |
|       unsigned int date_val = (val_storage[0] & 0xff) + ((val_storage[1] & 0xff) << 8) + ((val_storage[2] & 0xff) << 16);
 | |
|       unsigned int date_year = date_val >> 9;
 | |
|       date_val -= (date_year << 9);
 | |
|       unsigned int date_month = date_val >> 5;
 | |
|       unsigned int date_day = date_val - (date_month << 5);
 | |
|       str = boost::str(boost::format("%04d-%02d-%02d") % date_year % date_month % date_day);
 | |
|       break;
 | |
|     }
 | |
|     case MYSQL_TYPE_DATETIME:
 | |
|     {
 | |
|       boost::uint64_t timestamp= val.as_int64();
 | |
|       unsigned long d= timestamp / 1000000;
 | |
|       unsigned long t= timestamp % 1000000;
 | |
|       std::ostringstream os;
 | |
| 
 | |
|       os << std::setfill('0') << std::setw(4) << d / 10000
 | |
|          << std::setw(1) << '-'
 | |
|          << std::setw(2) << (d % 10000) / 100
 | |
|          << std::setw(1) << '-'
 | |
|          << std::setw(2) << d % 100
 | |
|          << std::setw(1) << ' '
 | |
|          << std::setw(2) << t / 10000
 | |
|          << std::setw(1) << ':'
 | |
|          << std::setw(2) << (t % 10000) / 100
 | |
|          << std::setw(1) << ':'
 | |
|          << std::setw(2) << t % 100;
 | |
| 
 | |
|       str= os.str();
 | |
|     }
 | |
|       break;
 | |
|     case MYSQL_TYPE_TIME:
 | |
|     {
 | |
|       const char* val_storage = val.storage();
 | |
|       unsigned int time_val = (val_storage[0] & 0xff) + ((val_storage[1] & 0xff) << 8) + ((val_storage[2] & 0xff) << 16);
 | |
|       unsigned int time_sec = time_val % 100;
 | |
|       time_val -= time_sec;
 | |
|       unsigned int time_min = (time_val % 10000) / 100;
 | |
|       unsigned int time_hour = (time_val - time_min) / 10000;
 | |
|       str = boost::str(boost::format("%02d:%02d:%02d") % time_hour % time_min % time_sec);
 | |
|       break;
 | |
|     }
 | |
|     case MYSQL_TYPE_YEAR:
 | |
|     {
 | |
|       const char* val_storage = val.storage();
 | |
|       unsigned int year_val = (val_storage[0] & 0xff);
 | |
|       year_val = year_val > 0 ? (year_val + 1900) : 0;
 | |
|       str = boost::str(boost::format("%04d") % year_val);
 | |
|       break;
 | |
|     }
 | |
|     case MYSQL_TYPE_NEWDATE:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_VARCHAR:
 | |
|     {
 | |
|       unsigned long size;
 | |
|       char *ptr= val.as_c_str(size);
 | |
|       str.append(ptr, size);
 | |
|     }
 | |
|       break;
 | |
|     case MYSQL_TYPE_VAR_STRING:
 | |
|     {
 | |
|       str.append(val.storage(), val.length());
 | |
|     }
 | |
|     break;
 | |
|     case MYSQL_TYPE_STRING:
 | |
|     {
 | |
|       unsigned char str_type = 0;
 | |
| 
 | |
|       if (val.metadata()) {
 | |
|         str_type = val.metadata() & 0xff;
 | |
|       }
 | |
| 
 | |
|       if (str_type == MYSQL_TYPE_SET) {
 | |
|         str = "not implemented";
 | |
|         break;
 | |
|       } else if (str_type == MYSQL_TYPE_ENUM) {
 | |
|         unsigned int val_storage = static_cast<unsigned int>(*val.storage());
 | |
|         str = boost::str(boost::format("%u") % val_storage);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       unsigned long size;
 | |
|       char *ptr= val.as_c_str(size);
 | |
|       str.append(ptr, size);
 | |
|     }
 | |
|       break;
 | |
|     case MYSQL_TYPE_BIT:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_NEWDECIMAL:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_ENUM:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_SET:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     case MYSQL_TYPE_TINY_BLOB:
 | |
|     case MYSQL_TYPE_MEDIUM_BLOB:
 | |
|     case MYSQL_TYPE_LONG_BLOB:
 | |
|     case MYSQL_TYPE_BLOB:
 | |
|     {
 | |
|       unsigned long size;
 | |
|       unsigned char *ptr= val.as_blob(size);
 | |
|       str.append((const char *)ptr, size);
 | |
|     }
 | |
|       break;
 | |
|     case MYSQL_TYPE_GEOMETRY:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|     default:
 | |
|       str= "not implemented";
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Converter::to(float &out, const Value &val) const
 | |
| {
 | |
|   switch(val.type())
 | |
|   {
 | |
|   case MYSQL_TYPE_FLOAT:
 | |
|     out= val.as_float();
 | |
|     break;
 | |
|   default:
 | |
|     out= 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Converter::to(long &out, const Value &val) const
 | |
| {
 | |
|   switch(val.type())
 | |
|   {
 | |
|     case MYSQL_TYPE_DECIMAL:
 | |
|       // TODO
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_TINY:
 | |
|       out= val.as_int8();
 | |
|       break;
 | |
|     case MYSQL_TYPE_SHORT:
 | |
|       out= val.as_int16();
 | |
|       break;;
 | |
|     case MYSQL_TYPE_LONG:
 | |
|       out= (long)val.as_int32();
 | |
|       break;
 | |
|     case MYSQL_TYPE_FLOAT:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_DOUBLE:
 | |
|       out= (long)val.as_double();
 | |
|     case MYSQL_TYPE_NULL:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_TIMESTAMP:
 | |
|       out=(boost::uint32_t)val.as_int32();
 | |
|       break;
 | |
| 
 | |
|     case MYSQL_TYPE_LONGLONG:
 | |
|       out= (long)val.as_int64();
 | |
|       break;
 | |
|     case MYSQL_TYPE_INT24:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_DATE:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_TIME:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_DATETIME:
 | |
|       out= (long)val.as_int64();
 | |
|       break;
 | |
|     case MYSQL_TYPE_YEAR:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_NEWDATE:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_VARCHAR:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_BIT:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_NEWDECIMAL:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_ENUM:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_SET:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_TINY_BLOB:
 | |
|     case MYSQL_TYPE_MEDIUM_BLOB:
 | |
|     case MYSQL_TYPE_LONG_BLOB:
 | |
|     case MYSQL_TYPE_BLOB:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_VAR_STRING:
 | |
|     {
 | |
|       std::string str;
 | |
|       str.append(val.storage(), val.length());
 | |
|       out= boost::lexical_cast<long>(str.c_str());
 | |
|     }
 | |
|       break;
 | |
|     case MYSQL_TYPE_STRING:
 | |
|       out= 0;
 | |
|       break;
 | |
|     case MYSQL_TYPE_GEOMETRY:
 | |
|       out= 0;
 | |
|       break;
 | |
|     default:
 | |
|       out= 0;
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| } // end namespace mysql
 | 
