From 4e25fa0ed7fb7adad32b3e80f784e45b49c2975b Mon Sep 17 00:00:00 2001 From: Mordred Date: Sat, 23 Sep 2017 23:26:23 +0200 Subject: [PATCH] Implemented prepared statmements --- .../Server_Common/Database/Connection.cpp | 14 + .../Server_Common/Database/Connection.h | 3 +- src/servers/Server_Common/Database/DataType.h | 44 ++ .../Database/PreparedResultSet.cpp | 623 ++++++++++++++++ .../Database/PreparedResultSet.h | 115 +++ .../Database/PreparedStatement.cpp | 696 ++++++++++++++++++ .../Database/PreparedStatement.h | 95 +++ .../Server_Common/Database/ResultBind.cpp | 157 ++++ .../Server_Common/Database/ResultBind.h | 37 + .../Server_Common/Database/ResultSet.h | 54 +- .../Server_Common/Database/Statement.h | 17 +- .../Server_Common/Database/mysql_util.cpp | 373 ++++++++++ .../Server_Common/Database/mysql_util.h | 21 +- src/servers/Server_Zone/ServerZone.cpp | 27 + 14 files changed, 2237 insertions(+), 39 deletions(-) create mode 100644 src/servers/Server_Common/Database/DataType.h create mode 100644 src/servers/Server_Common/Database/PreparedResultSet.cpp create mode 100644 src/servers/Server_Common/Database/PreparedResultSet.h create mode 100644 src/servers/Server_Common/Database/PreparedStatement.cpp create mode 100644 src/servers/Server_Common/Database/PreparedStatement.h create mode 100644 src/servers/Server_Common/Database/ResultBind.cpp create mode 100644 src/servers/Server_Common/Database/ResultBind.h diff --git a/src/servers/Server_Common/Database/Connection.cpp b/src/servers/Server_Common/Database/Connection.cpp index 32d15e6f..1f9f085c 100644 --- a/src/servers/Server_Common/Database/Connection.cpp +++ b/src/servers/Server_Common/Database/Connection.cpp @@ -1,6 +1,7 @@ #include "Connection.h" #include "MySqlBase.h" #include "Statement.h" +#include "PreparedStatement.h" #include #include @@ -228,3 +229,16 @@ std::string Core::Db::Connection::getError() return ""; } +Core::Db::PreparedStatement* Core::Db::Connection::prepareStatement( const std::string &sql ) +{ + MYSQL_STMT* stmt = mysql_stmt_init( getRawCon() ); + + if( !stmt ) + throw std::runtime_error( "Could not init prepared statement: " + this->getError() ); + + if( mysql_stmt_prepare( stmt, sql.c_str(), sql.size() ) ) + throw std::runtime_error( "Could not prepare statement: " + this->getError() ); + + return new PreparedStatement( stmt, this ); +} + diff --git a/src/servers/Server_Common/Database/Connection.h b/src/servers/Server_Common/Database/Connection.h index 4f78476c..14a2caf7 100644 --- a/src/servers/Server_Common/Database/Connection.h +++ b/src/servers/Server_Common/Database/Connection.h @@ -15,6 +15,7 @@ namespace Db typedef std::map< enum mysql_option, std::string > optionMap; class MySqlBase; class Statement; + class PreparedStatement; class Connection { @@ -69,7 +70,7 @@ namespace Db bool reconnect(); - //sql::PreparedStatement * prepareStatement(const sql::SQLString& sql); + PreparedStatement* prepareStatement( const std::string& sql ); //sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int autoGeneratedKeys); diff --git a/src/servers/Server_Common/Database/DataType.h b/src/servers/Server_Common/Database/DataType.h new file mode 100644 index 00000000..b2ce27a6 --- /dev/null +++ b/src/servers/Server_Common/Database/DataType.h @@ -0,0 +1,44 @@ +#ifndef SAPPHIRE_DATATYPE_H +#define SAPPHIRE_DATATYPE_H +namespace Core +{ +namespace Db +{ + class DataType + { + DataType(); + + public: + enum + { + UNKNOWN = 0, + BIT, + TINYINT, + SMALLINT, + MEDIUMINT, + INTEGER, + BIGINT, + REAL, + DOUBLE, + DECIMAL, + NUMERIC, + CHAR, + BINARY, + VARCHAR, + VARBINARY, + LONGVARCHAR, + LONGVARBINARY, + TIMESTAMP, + DATE, + TIME, + YEAR, + GEOMETRY, + ENUM, + SET, + SQLNULL, + JSON + }; + }; +} +} +#endif //SAPPHIRE_DATATYPE_H diff --git a/src/servers/Server_Common/Database/PreparedResultSet.cpp b/src/servers/Server_Common/Database/PreparedResultSet.cpp new file mode 100644 index 00000000..fa1a0fce --- /dev/null +++ b/src/servers/Server_Common/Database/PreparedResultSet.cpp @@ -0,0 +1,623 @@ +#include "PreparedResultSet.h" +#include "ResultBind.h" +#include "DataType.h" +#include +#include +#include + +namespace +{ + static inline char * my_l_to_a(char * buf, size_t buf_size, int64_t a) + { + snprintf(buf, buf_size, "%lld", (long long) a); + return buf; + } + + static inline char * my_ul_to_a(char * buf, size_t buf_size, uint64_t a) + { + snprintf(buf, buf_size, "%llu", (unsigned long long) a); + return buf; + } + + static inline char * my_f_to_a(char * buf, size_t buf_size, double a) + { + snprintf(buf, buf_size, "%f", a); + return buf; + } +} + +uint32_t Core::Db::PreparedResultSet::findColumn( const std::string &columnLabel ) const +{ + std::string searchColumn = columnLabel; + + std::transform( searchColumn.begin(), searchColumn.end(), searchColumn.begin(), + [](unsigned char c){ return std::toupper( c ); } ); + + auto iter = m_fieldNameToIndex.find( searchColumn ); + if( iter == m_fieldNameToIndex.end() ) + return 0; + + return iter->second + 1; +} + +Core::Db::PreparedResultSet::PreparedResultSet( boost::shared_ptr< ResultBind >& pBind, + Core::Db::PreparedStatement* par ) : + ResultSet( nullptr, par ), + m_pResultBind( pBind ) +{ + +} + +bool Core::Db::PreparedResultSet::isBeforeFirstOrAfterLast() const +{ + return ( m_rowPosition == 0 ); +} + +Core::Db::PreparedResultSet::~PreparedResultSet() +{ + +} + +uint32_t Core::Db::PreparedResultSet::getUInt( const uint32_t columnIndex ) const +{ + + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getUInt: can't fetch because not on result set" ); + + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::getUInt: invalid value of 'columnIndex'" ); + + m_lastQueriedColumn = columnIndex; + + if (*m_pResultBind->m_pBind[columnIndex - 1].is_null) { + return 0; + } + return static_cast< uint32_t >( getUInt64_intern( columnIndex, true ) ); +} + +uint32_t Core::Db::PreparedResultSet::getUInt( const std::string& columnLabel ) const +{ + return getUInt( findColumn( columnLabel ) ); +} + +int64_t Core::Db::PreparedResultSet::getInt64( const uint32_t columnIndex ) const +{ + + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getInt64: can't fetch because not on result set" ); + + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::getInt64: invalid value of 'columnIndex'" ); + + m_lastQueriedColumn = columnIndex; + + if( *m_pResultBind->m_pBind[columnIndex - 1].is_null ) + return 0; + + return getInt64_intern( columnIndex, true ); +} + +int64_t Core::Db::PreparedResultSet::getInt64( const std::string& columnLabel ) const +{ + return getInt64( findColumn( columnLabel ) ); +} + +uint64_t Core::Db::PreparedResultSet::getUInt64_intern( const uint32_t columnIndex, bool ) const +{ + + MYSQL_RES* res = mysql_stmt_result_metadata( m_pStmt->getRawStmt() ); + MYSQL_FIELD* field = mysql_fetch_field_direct( res, columnIndex ); + + switch( Util::mysql_type_to_datatype( field ) ) + { + case DataType::REAL: + case DataType::DOUBLE: + return static_cast< uint64_t >( getDouble( columnIndex ) ); + case DataType::NUMERIC: + case DataType::DECIMAL: + case DataType::TIMESTAMP: + case DataType::DATE: + case DataType::TIME: + case DataType::CHAR: + case DataType::BINARY: + case DataType::VARCHAR: + case DataType::VARBINARY: + case DataType::LONGVARCHAR: + case DataType::LONGVARBINARY: + case DataType::SET: + case DataType::ENUM: + case DataType::JSON: + return strtoull( getString(columnIndex).c_str(), nullptr, 10 ); + case DataType::BIT: + { + uint64_t uval = 0; + /* This length is in bytes, on the contrary to what can be seen in mysql_resultset.cpp where the Meta is used */ + switch( *m_pResultBind->m_pBind[columnIndex - 1].length ) + { + case 8:uval = (uint64_t) bit_uint8korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 7:uval = (uint64_t) bit_uint7korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 6:uval = (uint64_t) bit_uint6korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 5:uval = (uint64_t) bit_uint5korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 4:uval = (uint64_t) bit_uint4korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 3:uval = (uint64_t) bit_uint3korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 2:uval = (uint64_t) bit_uint2korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 1:uval = (uint64_t) bit_uint1korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + } + return uval; + } + case DataType::YEAR: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::MEDIUMINT: + case DataType::INTEGER: + case DataType::BIGINT: + { + + uint64_t ret; + bool is_it_null = *m_pResultBind->m_pBind[columnIndex - 1].is_null != 0; + bool is_it_unsigned = m_pResultBind->m_pBind[columnIndex - 1].is_unsigned != 0; + + switch( m_pResultBind->m_pBind[columnIndex - 1].buffer_length ) + { + case 1: + if( is_it_unsigned ) + ret = !is_it_null ? *reinterpret_cast< uint8_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int8_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + break; + case 2: + if(is_it_unsigned) + ret = !is_it_null ? *reinterpret_cast< uint16_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int16_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + break; + case 4: + if( is_it_unsigned ) + ret = !is_it_null ? *reinterpret_cast< uint32_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int32_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + break; + case 8: + if( is_it_unsigned ) + ret = !is_it_null ? *reinterpret_cast< uint64_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int64_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + break; + default: + throw std::runtime_error( "PreparedResultSet::getInt64_intern: invalid type" ); + } + return ret; + } + default: + break; + } + throw std::runtime_error( "MySQL_Prepared_ResultSet::getUInt64_intern: unhandled type. Please, report" ); + return 0; +} + +int64_t Core::Db::PreparedResultSet::getInt64_intern( const uint32_t columnIndex, bool ) const +{ + + MYSQL_RES* res = mysql_stmt_result_metadata( m_pStmt->getRawStmt() ); + MYSQL_FIELD* field = mysql_fetch_field_direct( res, columnIndex ); + + switch( Util::mysql_type_to_datatype( field ) ) + { + case DataType::REAL: + case DataType::DOUBLE: + return static_cast< int64_t >( getDouble( columnIndex ) ); + case DataType::NUMERIC: + case DataType::DECIMAL: + case DataType::TIMESTAMP: + case DataType::DATE: + case DataType::TIME: + case DataType::CHAR: + case DataType::BINARY: + case DataType::VARCHAR: + case DataType::VARBINARY: + case DataType::LONGVARCHAR: + case DataType::LONGVARBINARY: + case DataType::SET: + case DataType::ENUM: + case DataType::JSON: + return strtoll( getString( columnIndex ).c_str(), nullptr, 10 ); + case DataType::BIT: + { + int64_t uval = 0; + switch( *m_pResultBind->m_pBind[columnIndex - 1].length ) + { + case 8:uval = ( int64_t ) bit_uint8korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 7:uval = ( int64_t ) bit_uint7korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 6:uval = ( int64_t ) bit_uint6korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 5:uval = ( int64_t ) bit_uint5korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 4:uval = ( int64_t ) bit_uint4korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 3:uval = ( int64_t ) bit_uint3korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 2:uval = ( int64_t ) bit_uint2korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + case 1:uval = ( int64_t ) bit_uint1korr( m_pResultBind->m_pBind[columnIndex - 1].buffer );break; + } + return uval; + } + case DataType::YEAR: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::MEDIUMINT: + case DataType::INTEGER: + case DataType::BIGINT: + { + int64_t ret; + bool is_it_null = *m_pResultBind->m_pBind[columnIndex - 1].is_null != 0; + bool is_it_unsigned = m_pResultBind->m_pBind[columnIndex - 1].is_unsigned != 0; + + switch( m_pResultBind->m_pBind[columnIndex - 1].buffer_length ) + { + case 1: + if( is_it_unsigned ) + ret = !is_it_null ? *reinterpret_cast< uint8_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int8_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + break; + case 2: + if( is_it_unsigned ) + ret = !is_it_null ? *reinterpret_cast< uint16_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int16_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + break; + case 4: + if( is_it_unsigned ) + ret = !is_it_null ? *reinterpret_cast< uint32_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int32_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer) : 0; + break; + case 8: + if( is_it_unsigned ) + ret = !is_it_null ? *reinterpret_cast< uint64_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + else + ret = !is_it_null ? *reinterpret_cast< int64_t* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0; + break; + default: + throw std::runtime_error( "PreparedResultSet::getInt64_intern: invalid type" ); + } + return ret; + } + default: + break; + + } + throw std::runtime_error( "PreparedResultSet::getInt64_intern: unhandled type. Please, report" ); + return 0; +} + +uint64_t Core::Db::PreparedResultSet::getUInt64( const uint32_t columnIndex ) const +{ + + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getUInt64: can't fetch because not on result set" ); + + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::getUInt64: invalid value of 'columnIndex'" ); + + m_lastQueriedColumn = columnIndex; + + if( *m_pResultBind->m_pBind[columnIndex - 1].is_null ) + return 0; + return getUInt64_intern( columnIndex, true ); +} + +uint64_t Core::Db::PreparedResultSet::getUInt64( const std::string& columnLabel ) const +{ + return getUInt64( findColumn( columnLabel ) ); +} + +std::string Core::Db::PreparedResultSet::getString( const uint32_t columnIndex ) const +{ + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getString: can't fetch because not on result set" ); + + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::getString: invalid 'columnIndex'" ); + + m_lastQueriedColumn = columnIndex; + if( *m_pResultBind->m_pBind[columnIndex - 1].is_null ) + return std::string(""); + + MYSQL_RES* res = mysql_stmt_result_metadata( m_pStmt->getRawStmt() ); + MYSQL_FIELD* field = mysql_fetch_field_direct( res, columnIndex ); + + switch( Util::mysql_type_to_datatype( field ) ) + { + case DataType::TIMESTAMP: + { + char buf[28]; + MYSQL_TIME * t = static_cast< MYSQL_TIME* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ); + if( t->second_part ) + snprintf( buf, sizeof(buf) - 1, "%04d-%02d-%02d %02d:%02d:%02d.%06lu", + t->year, t->month, t->day, t->hour, t->minute, t->second, t->second_part ); + else + snprintf( buf, sizeof(buf) - 1, "%04d-%02d-%02d %02d:%02d:%02d", + t->year, t->month, t->day, t->hour, t->minute, t->second ); + + return std::string( buf ); + } + case DataType::DATE: + { + char buf[12]; + MYSQL_TIME* t = static_cast< MYSQL_TIME* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ); + snprintf( buf, sizeof( buf ) - 1, "%02d-%02d-%02d", t->year, t->month, t->day ); + + return std::string( buf ); + } + case DataType::TIME: + { + char buf[18]; + MYSQL_TIME* t = static_cast( m_pResultBind->m_pBind[columnIndex - 1].buffer ); + if( t->second_part ) + snprintf( buf, sizeof( buf ), "%s%02d:%02d:%02d.%06lu", t->neg ? "-" : "", t->hour, t->minute, t->second, t->second_part ); + else + snprintf( buf, sizeof( buf ), "%s%02d:%02d:%02d", t->neg ? "-" : "", t->hour, t->minute, t->second ); + + return std::string( buf ); + } + case DataType::BIT: + case DataType::YEAR: // fetched as a SMALLINT + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::MEDIUMINT: + case DataType::INTEGER: + case DataType::BIGINT: + { + char buf[30]; + + if( m_pResultBind->m_pBind[columnIndex - 1].is_unsigned ) + my_ul_to_a( buf, sizeof( buf ) - 1, getUInt64_intern( columnIndex, false ) ); + else + my_l_to_a( buf, sizeof( buf ) - 1, getInt64_intern( columnIndex, false ) ); + + return std::string( buf ); + } + case DataType::REAL: + case DataType::DOUBLE: + { + char buf[50]; + my_f_to_a( buf, sizeof( buf ) - 1, getDouble( columnIndex ) ); + return std::string( buf ); + } + case DataType::NUMERIC: + case DataType::DECIMAL: + case DataType::CHAR: + case DataType::BINARY: + case DataType::VARCHAR: + case DataType::VARBINARY: + case DataType::LONGVARCHAR: + case DataType::LONGVARBINARY: + case DataType::SET: + case DataType::ENUM: + case DataType::JSON: + return std::string( static_cast< char* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ), + *m_pResultBind->m_pBind[columnIndex - 1].length ); + default: + break; + } + + throw std::runtime_error( " PreparedResultSet::getString: unhandled type. Please, report" ); + return 0; +} + +std::string Core::Db::PreparedResultSet::getString( const std::string& columnLabel) const +{ + return getString( findColumn( columnLabel ) ); +} + +int32_t Core::Db::PreparedResultSet::getInt( uint32_t columnIndex ) const +{ + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getInt: can't fetch because not on result set" ); + + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::getInt: invalid value of 'columnIndex'" ); + + m_lastQueriedColumn = columnIndex; + + if( *m_pResultBind->m_pBind[columnIndex - 1].is_null ) + return 0; + + return static_cast< int32_t >( getInt64_intern( columnIndex, true ) ); +} + +int32_t Core::Db::PreparedResultSet::getInt( const std::string& columnLabel ) const +{ + return getInt( findColumn( columnLabel ) ); +} + +long double Core::Db::PreparedResultSet::getDouble(const uint32_t columnIndex) const +{ + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getDouble: can't fetch because not on result set" ); + + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::getDouble: invalid 'columnIndex'" ); + + + m_lastQueriedColumn = columnIndex; + + if( *m_pResultBind->m_pBind[columnIndex - 1].is_null) + return 0.0; + + MYSQL_RES* res = mysql_stmt_result_metadata( m_pStmt->getRawStmt() ); + MYSQL_FIELD* field = mysql_fetch_field_direct( res, columnIndex ); + + switch( Util::mysql_type_to_datatype( field ) ) + { + case DataType::BIT: + case DataType::YEAR: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::MEDIUMINT: + case DataType::INTEGER: + case DataType::BIGINT: + { + long double ret; + bool is_it_unsigned = m_pResultBind->m_pBind[columnIndex - 1].is_unsigned != 0; + + if( is_it_unsigned ) + { + uint64_t ival = getUInt64_intern( columnIndex, false ); + ret = static_cast< long double >( ival ); + } + else + { + int64_t ival = getInt64_intern( columnIndex, false ); + ret = static_cast< long double >( ival ); + } + return ret; + } + case DataType::NUMERIC: + case DataType::DECIMAL: + case DataType::TIMESTAMP: + case DataType::DATE: + case DataType::TIME: + case DataType::CHAR: + case DataType::BINARY: + case DataType::VARCHAR: + case DataType::VARBINARY: + case DataType::LONGVARCHAR: + case DataType::LONGVARBINARY: + case DataType::SET: + case DataType::ENUM: + case DataType::JSON: + { + long double ret = Util::strtonum( getString( columnIndex ).c_str() ); + return ret; + } + case DataType::REAL: + { + long double ret = !*m_pResultBind->m_pBind[columnIndex - 1].is_null ? + *reinterpret_cast< float* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0.; + return ret; + } + case DataType::DOUBLE: + { + long double ret = !*m_pResultBind->m_pBind[columnIndex - 1].is_null ? + *reinterpret_cast< double* >( m_pResultBind->m_pBind[columnIndex - 1].buffer ) : 0.; + return ret; + } + } + + throw std::runtime_error("PreparedResultSet::getDouble: unhandled type. Please, report"); + return .0; +} + +long double Core::Db::PreparedResultSet::getDouble( const std::string& columnLabel ) const +{ + return getDouble( findColumn( columnLabel ) ); +} + +size_t Core::Db::PreparedResultSet::getRow() const +{ + return static_cast< size_t >( m_rowPosition ); +} + +size_t Core::Db::PreparedResultSet::rowsCount() const +{ + return static_cast< uint32_t >( m_numRows ); +} + +const Core::Db::Statement* Core::Db::PreparedResultSet::getStatement() const +{ + return m_pStmt; +} + +std::istream* Core::Db::PreparedResultSet::getBlob( const uint32_t columnIndex ) const +{ + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getBlob: can't fetch because not on result set" ); + + return new std::istringstream( getString( columnIndex ) ); +} + +std::istream* Core::Db::PreparedResultSet::getBlob( const std::string& columnLabel ) const +{ + return new std::istringstream( getString( columnLabel ) ); +} + +std::vector< char > Core::Db::PreparedResultSet::getBlobVector( uint32_t columnIndex ) const +{ + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::getBlobVector: invalid value of 'columnIndex'" ); + + boost::scoped_ptr< std::istream > inStr( getBlob( columnIndex ) ); + char buff[4196]; + std::vector< char > data; + inStr->read( buff, sizeof( buff ) ); + if( inStr->gcount() ) + { + data.resize( static_cast< uint32_t >( inStr->gcount() ) ); + memcpy( data.data(), buff, static_cast< size_t >( inStr->gcount() ) ); + } + return data; +} + +std::vector< char > Core::Db::PreparedResultSet::getBlobVector( const std::string& columnLabel ) const +{ + return getBlobVector( findColumn( columnLabel ) ); +} + +bool Core::Db::PreparedResultSet::getBoolean( const uint32_t columnIndex ) const +{ + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::getBoolean: can't fetch because not on result set" ); + return getInt(columnIndex ) != 0; +} + +bool Core::Db::PreparedResultSet::getBoolean( const std::string& columnLabel ) const +{ + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error("PreparedResultSet::getBoolean: can't fetch because not on result set"); + return getInt(columnLabel ) != 0; +} + +bool Core::Db::PreparedResultSet::isLast() const +{ + return ( m_rowPosition == m_numRows ); +} + +bool Core::Db::PreparedResultSet::isFirst() const +{ + return ( m_rowPosition == 1 ); +} + +bool Core::Db::PreparedResultSet::isNull( const uint32_t columnIndex ) const +{ + if( columnIndex == 0 || columnIndex > m_numFields ) + throw std::runtime_error( "PreparedResultSet::isNull: invalid value of 'columnIndex'" ); + + if( isBeforeFirstOrAfterLast() ) + throw std::runtime_error( "PreparedResultSet::isNull: can't fetch because not on result set" ); + return *m_pResultBind->m_pBind[columnIndex - 1].is_null != 0; +} + +bool Core::Db::PreparedResultSet::isNull( const std::string& columnLabel ) const +{ + uint32_t index = findColumn( columnLabel ); + if( index == 0 ) + throw std::runtime_error( "PreparedResultSet::isNull: invalid value of 'columnLabel'" ); + return isNull( index ); +} + +bool Core::Db::PreparedResultSet::next() +{ + bool ret = false; + + // reset last_queried_column + // m_lastQueriedColumn = std::numeric_limits< uint32_t >::max(); + + int result = mysql_stmt_fetch( m_pStmt->getRawStmt() ); + if( !result || result == MYSQL_DATA_TRUNCATED ) + ret = true; + if( result == MYSQL_NO_DATA ) + ret = false; + if( result == 1 ) + throw std::runtime_error( "PreparedResultSet:next: error getting next result" ); + ++m_rowPosition; + + return ret; +} \ No newline at end of file diff --git a/src/servers/Server_Common/Database/PreparedResultSet.h b/src/servers/Server_Common/Database/PreparedResultSet.h new file mode 100644 index 00000000..63f65c78 --- /dev/null +++ b/src/servers/Server_Common/Database/PreparedResultSet.h @@ -0,0 +1,115 @@ +#ifndef SAPPHIRE_PREPAREDRESULTSET_H +#define SAPPHIRE_PREPAREDRESULTSET_H + +#include +#include +#include "ResultSet.h" + +namespace Core +{ + namespace Db + { + class PreparedStatement; +// class PreparedResultSetMetaData; + class ResultBind; + + class PreparedResultSet : public ResultSet + { + private: + mutable uint32_t m_lastQueriedColumn; // this is updated by calls to getInt(int), getString(int), etc... + + uint32_t m_numFields; + uint64_t m_numRows; + uint64_t m_rowPosition; + + typedef std::map< std::string, uint32_t > FieldNameIndexMap; + + FieldNameIndexMap m_fieldNameToIndex; + + PreparedStatement * m_pStmt; + + bool is_valid; + +// boost::scoped_ptr< MySQL_PreparedResultSetMetaData > rs_meta; + + boost::shared_ptr< ResultBind > m_pResultBind; + + protected: + void checkValid() const; + void closeIntern(); + bool isBeforeFirstOrAfterLast() const; + void seek(); + + int64_t getInt64_intern(const uint32_t columnIndex, bool cutTooBig) const; + uint64_t getUInt64_intern(const uint32_t columnIndex, bool cutTooBig) const; + + public: + PreparedResultSet( boost::shared_ptr< ResultBind >& pBind, PreparedStatement* par ); + + virtual ~PreparedResultSet(); + + void clearWarnings(); + + void close(); + + uint32_t findColumn( const std::string& columnLabel ) const; + + std::istream * getBlob(uint32_t columnIndex) const; + std::istream * getBlob(const std::string& columnLabel) const; + + std::vector< char > getBlobVector( uint32_t columnIndex ) const; + std::vector< char > getBlobVector( const std::string& columnLabel ) const; + + bool getBoolean(uint32_t columnIndex) const; + bool getBoolean(const std::string& columnLabel) const; + + long double getDouble(uint32_t columnIndex) const; + long double getDouble(const std::string& columnLabel) const; + + int32_t getInt(uint32_t columnIndex) const; + int32_t getInt(const std::string& columnLabel) const; + + uint32_t getUInt(uint32_t columnIndex) const; + uint32_t getUInt(const std::string& columnLabel) const; + + int64_t getInt64(uint32_t columnIndex) const; + int64_t getInt64(const std::string& columnLabel) const; + + uint64_t getUInt64(uint32_t columnIndex) const; + uint64_t getUInt64(const std::string& columnLabel) const; + + //sql::ResultSetMetaData * getMetaData() const; + + size_t getRow() const; + + const Statement * getStatement() const; + + std::string getString(uint32_t columnIndex) const; + std::string getString(const std::string& columnLabel) const; + + void getWarnings(); + + bool isClosed() const; + + void insertRow(); + + bool isFirst() const; + + bool isLast() const; + + bool isNull(uint32_t columnIndex) const; + + bool isNull(const std::string& columnLabel) const; + + bool next(); + + size_t rowsCount() const; + + }; + + } +} + + + +#endif //SAPPHIRE_PREPAREDRESULTSET_H diff --git a/src/servers/Server_Common/Database/PreparedStatement.cpp b/src/servers/Server_Common/Database/PreparedStatement.cpp new file mode 100644 index 00000000..87f8c411 --- /dev/null +++ b/src/servers/Server_Common/Database/PreparedStatement.cpp @@ -0,0 +1,696 @@ +#include "PreparedStatement.h" +#include "PreparedResultSet.h" +#include "Connection.h" +#include "ResultBind.h" +#include +#include +#include + +static const unsigned int MAX_SEND_LONGDATA_BUFFER = 1 << 18; //1<<18=256k (for istream) +static const unsigned int MAX_SEND_LONGDATA_CHUNK = 1 << 18; //1<<19=512k (for string) + +namespace Core +{ +namespace Db +{ + +// Visitor class to send long data contained in blob_bind +class LongDataSender : public boost::static_visitor< bool > +{ + unsigned position; + MYSQL_STMT* m_pStmt; + LongDataSender() + {} + +public: + + LongDataSender(MYSQL_STMT* pStmt, unsigned int i) + : position( i ) + , m_pStmt( pStmt ) + { + } + + bool operator()(std::istream * my_blob) const + { + if (my_blob == NULL) + return false; + + //char buf[MAX_SEND_LONGDATA_BUFFER]; + boost::scoped_array< char > buf( new char[MAX_SEND_LONGDATA_BUFFER] ); + + do + { + if( my_blob->eof() ) + break; + my_blob->read( buf.get(), MAX_SEND_LONGDATA_BUFFER ); + + if( my_blob->bad() ) + throw std::runtime_error( "Error while reading from blob (bad)" ); + else if( my_blob->fail() ) + { + if( !my_blob->eof() ) + throw std::runtime_error( "Error while reading from blob (fail)" ); + } + if( mysql_stmt_send_long_data( m_pStmt, position, buf.get(), static_cast< unsigned long >( my_blob->gcount() ) ) ) + { + switch( mysql_stmt_errno( m_pStmt ) ) + { + case CR_OUT_OF_MEMORY: + throw std::bad_alloc(); + case CR_INVALID_BUFFER_USE: + throw std::runtime_error("PreparedStatement::setBlob: can't set blob value on that column"); + case CR_SERVER_GONE_ERROR: + case CR_COMMANDS_OUT_OF_SYNC: + default: + throw std::runtime_error("PreparedStatement:: Default error"); + } + } + } while( true ); + + return true; + } + + bool operator()( std::string* str ) const + { + if ( str == nullptr ) + return false; + + uint32_t sent = 0, chunkSize; + + while( sent < str->length() ) + { + chunkSize = ( sent + MAX_SEND_LONGDATA_CHUNK > str->length() + ? str->length() - sent + : MAX_SEND_LONGDATA_CHUNK ); + + if( mysql_stmt_send_long_data( m_pStmt, position, str->c_str() + sent, chunkSize ) ) + { + + switch( mysql_stmt_errno( m_pStmt ) ) + { + case CR_OUT_OF_MEMORY: + throw std::bad_alloc(); + case CR_INVALID_BUFFER_USE: + throw std::runtime_error( "PreparedStatement::setBlob: can't set blob value on that column" ); + case CR_SERVER_GONE_ERROR: + case CR_COMMANDS_OUT_OF_SYNC: + default: + throw std::runtime_error( "PreparedStatement:: Default error" ); + } + } + + sent+= chunkSize; + } + + return true; + } +}; + + +class BlobBindDeleter : public boost::static_visitor<> +{ +public: + + void operator()( std::string*& str ) const + { + if( str != nullptr ) + { + delete str; + str= nullptr; + } + } + + void operator()( std::istream*& my_blob ) const + { + if( my_blob!= nullptr ) + { + delete my_blob; + my_blob= nullptr; + } + } +}; + +class BlobIsNull : public boost::static_visitor< bool > +{ +public: + + bool operator()( std::string*& str ) const + { + return str == nullptr; + } + + bool operator()( std::istream*& my_blob ) const + { + return my_blob == nullptr; + } +}; + + +void resetBlobBind(MYSQL_BIND & param) +{ + delete [] static_cast(param.buffer); + + param.buffer_type = MYSQL_TYPE_LONG_BLOB; + param.buffer = nullptr; + param.buffer_length = 0; + param.is_null_value = 0; + + delete param.length; + param.length = new unsigned long(0); +} + + +class ParamBind +{ +public: + + typedef boost::variant< std::istream*, std::string* > Blob_t; + +private: + + unsigned int m_paramCount; + boost::scoped_array< MYSQL_BIND > bind; + boost::scoped_array< bool > value_set; + boost::scoped_array< bool > delete_blob_after_execute; + + typedef std::map Blobs; + + Blobs blob_bind; + +public: + + ParamBind(unsigned int paramCount) + : m_paramCount( paramCount ), + bind( nullptr ), + value_set( nullptr ), + delete_blob_after_execute( nullptr ) + { + if (m_paramCount) + { + bind.reset(new MYSQL_BIND[paramCount]); + memset(bind.get(), 0, sizeof(MYSQL_BIND) * paramCount); + + value_set.reset(new bool[paramCount]); + delete_blob_after_execute.reset(new bool[paramCount]); + for (unsigned int i = 0; i < paramCount; ++i) + { + bind[i].is_null_value = 1; + value_set[i] = false; + delete_blob_after_execute[i] = false; + } + } + } + + virtual ~ParamBind() + { + clearParameters(); + + for( Blobs::iterator it = blob_bind.begin(); it != blob_bind.end(); ++it ) + { + if (delete_blob_after_execute[it->first]) + { + delete_blob_after_execute[it->first] = false; + boost::apply_visitor( BlobBindDeleter(), it->second ); + } + } + } + + void set( unsigned int position ) + { + value_set[position] = true; + } + + void unset( unsigned int position ) + { + value_set[position] = false; + if( delete_blob_after_execute[position] ) + { + delete_blob_after_execute[position] = false; + boost::apply_visitor( BlobBindDeleter(),blob_bind[position] ); + blob_bind.erase( position ); + } + } + + + void setBlob( unsigned int position, Blob_t & blob, bool delete_after_execute ) + { + set( position ); + + resetBlobBind( bind[position] ); + + Blobs::iterator it = blob_bind.find( position ); + if( it != blob_bind.end() && delete_blob_after_execute[position] ) + boost::apply_visitor( BlobBindDeleter(), it->second ); + + if( boost::apply_visitor( BlobIsNull(), blob ) ) + { + if( it != blob_bind.end() ) + blob_bind.erase(it); + + delete_blob_after_execute[position] = false; + } + else + { + blob_bind[position] = blob; + delete_blob_after_execute[position] = delete_after_execute; + } + } + + + bool isAllSet() + { + for( uint32_t i = 0; i < m_paramCount; ++i ) + { + if (!value_set[i]) + return false; + } + return true; + } + + + void clearParameters() + { + for( uint32_t i = 0; i < m_paramCount; ++i ) + { + delete ( char* ) bind[i].length; + bind[i].length = nullptr; + delete[] ( char* ) bind[i].buffer; + bind[i].buffer = nullptr; + if (value_set[i]) + { + Blobs::iterator it = blob_bind.find( i ); + if( it != blob_bind.end() && delete_blob_after_execute[i] ) + { + boost::apply_visitor( BlobBindDeleter(), it->second ); + blob_bind.erase( it ); + } + blob_bind[i] = Blob_t(); + value_set[i] = false; + } + } + } + + MYSQL_BIND * getBindObject() + { + return bind.get(); + } + + + boost::variant< std::istream*, std::string* > getBlobObject( uint32_t position ) + { + Blobs::iterator it= blob_bind.find( position ); + + if( it != blob_bind.end() ) + return it->second; + + return Blob_t(); + } + +}; + +} +} + +Core::Db::PreparedStatement::PreparedStatement( MYSQL_STMT* pStmt, Core::Db::Connection* pConn ) + : Statement( pConn ) +{ + m_pStmt = pStmt; + m_pConnection = pConn; + m_paramCount = mysql_stmt_param_count( m_pStmt ); + m_pParamBind.reset( new ParamBind( m_paramCount ) ); + m_pResultBind.reset( new ResultBind( pStmt ) ); +} + +uint32_t Core::Db::PreparedStatement::errNo() +{ + return mysql_stmt_errno( m_pStmt ); +} + +Core::Db::Connection *Core::Db::PreparedStatement::getConnection() +{ + return m_pConnection; +} + +Core::Db::PreparedStatement::~PreparedStatement() +{ + if( m_pStmt ) + closeIntern(); +} + +uint32_t Core::Db::PreparedStatement::getWarningCount() +{ + return mysql_warning_count( m_pConnection->getRawCon() ); +} + +uint64_t Core::Db::PreparedStatement::getUpdateCount() +{ + throw std::runtime_error("PreparedStatement::getUpdateCount() Not implemented"); + return 0; +} + +bool Core::Db::PreparedStatement::sendLongDataBeforeParamBind() +{ + MYSQL_BIND* bind = m_pParamBind->getBindObject(); + + for( unsigned int i = 0; i < m_paramCount; ++i ) + { + if( bind[i].buffer_type == MYSQL_TYPE_LONG_BLOB ) + { + LongDataSender lv( m_pStmt, i ); + ParamBind::Blob_t dummy( m_pParamBind->getBlobObject( i ) ); + boost::apply_visitor( lv, dummy ); + } + } + return true; +} + +void Core::Db::PreparedStatement::doQuery() +{ + if( m_paramCount && !m_pParamBind->isAllSet() ) + throw std::runtime_error( "Value not set for all parameters" ); + + if( mysql_stmt_bind_param( m_pStmt, m_pParamBind->getBindObject() ) ) + throw std::runtime_error("Couldn't bind : " + std::to_string( errNo() ) ); + + if( !sendLongDataBeforeParamBind() || mysql_stmt_execute( m_pStmt ) ) + throw std::runtime_error( "Couldn't execute : " + std::to_string( errNo() ) + ": " + m_pConnection->getError() ); + + warningsCount = getWarningCount(); +} + +void Core::Db::PreparedStatement::closeIntern() +{ + if( m_pStmt ) + mysql_stmt_close( m_pStmt ); + clearParameters(); +} + +void Core::Db::PreparedStatement::clearParameters() +{ + m_pParamBind->clearParameters(); +} + +bool Core::Db::PreparedStatement::execute() +{ + doQuery(); + return mysql_stmt_field_count( m_pStmt ) > 0; +} + +bool Core::Db::PreparedStatement::execute( const std::string &sql ) +{ + throw std::runtime_error("PreparedStatement::execute( const std::string &sql ) Not implemented"); + return false; +} + +Core::Db::ResultSet* Core::Db::PreparedStatement::executeQuery( const std::string &sql ) +{ + return nullptr; +} + +Core::Db::ResultSet* Core::Db::PreparedStatement::executeQuery() +{ + doQuery(); + + my_bool bool_tmp = 1; + mysql_stmt_attr_set( m_pStmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp ); + + ResultSet* tmp = new PreparedResultSet( m_pResultBind, this ); + + return tmp; +} + +Core::Db::ResultSet* Core::Db::PreparedStatement::getResultSet() +{ + my_bool bool_tmp = 1; + mysql_stmt_attr_set( m_pStmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp ); + + ResultSet* tmp = new PreparedResultSet( m_pResultBind, this ); + + return tmp; +} + +MYSQL_STMT* Core::Db::PreparedStatement::getRawStmt() +{ + return m_pStmt; +} + +typedef std::pair< char*, size_t > BufferSizePair; + +static BufferSizePair allocate_buffer_for_type(enum_field_types t) +{ + switch( t ) + { + case MYSQL_TYPE_LONG: + return BufferSizePair( new char[4], 4 ); + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_LONGLONG: + return BufferSizePair( new char[8], 8 ); + case MYSQL_TYPE_STRING: + return BufferSizePair( NULLCSTR, 0 ); + case MYSQL_TYPE_NULL: + return BufferSizePair( NULLCSTR, 0 ); + default: + throw std::runtime_error( "allocate_buffer_for_type: invalid result_bind data type" ); + } +} + +void Core::Db::PreparedStatement::setInt( uint32_t parameterIndex, int32_t value ) +{ + + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + throw std::runtime_error( "PreparedStatement::setInt: invalid 'parameterIndex'" ); + --parameterIndex; + + { + ParamBind::Blob_t dummy; + m_pParamBind->setBlob( parameterIndex, dummy, false ); + m_pParamBind->unset( parameterIndex ); + } + + enum_field_types t = MYSQL_TYPE_LONG; + + BufferSizePair p = allocate_buffer_for_type(t); + + m_pParamBind->set(parameterIndex); + MYSQL_BIND* param = &m_pParamBind->getBindObject()[parameterIndex]; + + param->buffer_type = t; + delete [] static_cast< char* >( param->buffer ); + param->buffer = p.first; + param->buffer_length = 0; + param->is_null_value = 0; + delete param->length; + param->length = nullptr; + + memcpy( param->buffer, &value, p.second ); + +} + +void Core::Db::PreparedStatement::setUInt( uint32_t parameterIndex, uint32_t value ) +{ + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + throw std::runtime_error( "PreparedStatement::setInt: invalid 'parameterIndex'" ); + --parameterIndex; + + { + ParamBind::Blob_t dummy; + m_pParamBind->setBlob( parameterIndex, dummy, false ); + m_pParamBind->unset( parameterIndex ); + } + + enum_field_types t = MYSQL_TYPE_LONG; + + BufferSizePair p = allocate_buffer_for_type(t); + + m_pParamBind->set( parameterIndex ); + MYSQL_BIND* param = &m_pParamBind->getBindObject()[parameterIndex]; + + param->buffer_type = t; + delete [] static_cast< char* >( param->buffer ); + param->buffer = p.first; + param->buffer_length = 0; + param->is_null_value = 0; + param->is_unsigned = 1; + delete param->length; + param->length = nullptr; + + memcpy( param->buffer, &value, p.second ); +} + +void Core::Db::PreparedStatement::setInt64( uint32_t parameterIndex, int64_t value ) +{ + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + throw std::runtime_error( "PreparedStatement::setInt64: invalid 'parameterIndex'" ); + --parameterIndex; + + { + ParamBind::Blob_t dummy; + m_pParamBind->setBlob( parameterIndex, dummy, false ); + m_pParamBind->unset( parameterIndex ); + } + + enum_field_types t = MYSQL_TYPE_LONGLONG; + + BufferSizePair p = allocate_buffer_for_type(t); + + m_pParamBind->set( parameterIndex ); + MYSQL_BIND* param = &m_pParamBind->getBindObject()[parameterIndex]; + + param->buffer_type = t; + delete [] static_cast< char* >( param->buffer ); + param->buffer = p.first; + param->buffer_length = 0; + param->is_null_value = 0; + delete param->length; + param->length = nullptr; + + memcpy( param->buffer, &value, p.second ); +} + +void Core::Db::PreparedStatement::setUInt64( uint32_t parameterIndex, uint64_t value ) +{ + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + throw std::runtime_error( "PreparedStatement::setInt64: invalid 'parameterIndex'" ); + --parameterIndex; + + { + ParamBind::Blob_t dummy; + m_pParamBind->setBlob( parameterIndex, dummy, false ); + m_pParamBind->unset( parameterIndex ); + } + + enum_field_types t = MYSQL_TYPE_LONGLONG; + + BufferSizePair p = allocate_buffer_for_type(t); + + m_pParamBind->set( parameterIndex ); + MYSQL_BIND* param = &m_pParamBind->getBindObject()[parameterIndex]; + + param->buffer_type = t; + delete [] static_cast< char* >( param->buffer ); + param->buffer = p.first; + param->buffer_length = 0; + param->is_null_value = 0; + param->is_unsigned = 1; + delete param->length; + param->length = nullptr; + + memcpy( param->buffer, &value, p.second ); +} + +void Core::Db::PreparedStatement::setNull( uint32_t parameterIndex, int ) +{ + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + throw std::runtime_error( "PreparedStatement::setNull: invalid 'parameterIndex'" ); + --parameterIndex; + + { + ParamBind::Blob_t dummy; + m_pParamBind->setBlob( parameterIndex, dummy, false ); + m_pParamBind->unset( parameterIndex ); + } + + enum_field_types t = MYSQL_TYPE_NULL; + + m_pParamBind->set( parameterIndex ); + MYSQL_BIND* param = &m_pParamBind->getBindObject()[parameterIndex]; + + param->buffer_type = t; + delete [] static_cast< char* >( param->buffer ); + param->buffer = nullptr; + delete param->length; + param->length = nullptr; +} + +void Core::Db::PreparedStatement::setString( uint32_t parameterIndex, const std::string& value ) +{ + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + { + throw std::runtime_error( "PreparedStatement::setString: invalid 'parameterIndex'" ); + } + if( value.length() > 256*1024 ) + { + std::string* pvalue = new std::string( value ); + ParamBind::Blob_t dummy( pvalue ); + return m_pParamBind->setBlob( --parameterIndex, dummy, true ); + } + + --parameterIndex; + + { + ParamBind::Blob_t dummy; + m_pParamBind->setBlob( parameterIndex, dummy, false ); + m_pParamBind->unset( parameterIndex ); + } + + enum_field_types t = MYSQL_TYPE_STRING; + + m_pParamBind->set( parameterIndex ); + MYSQL_BIND* param = &m_pParamBind->getBindObject()[parameterIndex]; + + delete [] static_cast< char* >( param->buffer ); + + param->buffer_type = t; + param->buffer = memcpy( new char[value.length() + 1], value.c_str(), value.length() + 1 ); + param->buffer_length = static_cast< unsigned long >( value.length() ) + 1; + param->is_null_value = 0; + + delete param->length; + param->length = new unsigned long( static_cast< unsigned long >( value.length() ) ); +} + +void Core::Db::PreparedStatement::setDouble( uint32_t parameterIndex, double value ) +{ + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + { + throw std::runtime_error( "PreparedStatement::setDouble: invalid 'parameterIndex'" ); + } + --parameterIndex; + + { + ParamBind::Blob_t dummy; + m_pParamBind->setBlob( parameterIndex, dummy, false ); + m_pParamBind->unset( parameterIndex ); + } + + enum_field_types t = MYSQL_TYPE_DOUBLE; + + BufferSizePair p = allocate_buffer_for_type(t); + + m_pParamBind->set( parameterIndex ); + MYSQL_BIND* param = &m_pParamBind->getBindObject()[parameterIndex]; + + param->buffer_type = t; + delete [] static_cast< char* >( param->buffer ); + param->buffer = p.first; + param->buffer_length = 0; + param->is_null_value = 0; + delete param->length; + param->length = nullptr; + + memcpy( param->buffer, &value, p.second ); +} + +void Core::Db::PreparedStatement::setDateTime( uint32_t parameterIndex, const std::string& value ) +{ + setString( parameterIndex, value ); +} + +void Core::Db::PreparedStatement::setBoolean( uint32_t parameterIndex, bool value ) +{ + setInt( parameterIndex, value ); +} + +void Core::Db::PreparedStatement::setBigInt( uint32_t parameterIndex, const std::string& value ) +{ + setString( parameterIndex, value ); +} + +void Core::Db::PreparedStatement::setBlob( uint32_t parameterIndex, std::istream* blob ) +{ + if( parameterIndex == 0 || parameterIndex > m_paramCount ) + throw std::runtime_error( "PreparedStatement::setBlob: invalid 'parameterIndex'" ); + + ParamBind::Blob_t dummy(blob); + m_pParamBind->setBlob( --parameterIndex, dummy, false ); +} \ No newline at end of file diff --git a/src/servers/Server_Common/Database/PreparedStatement.h b/src/servers/Server_Common/Database/PreparedStatement.h new file mode 100644 index 00000000..25965a80 --- /dev/null +++ b/src/servers/Server_Common/Database/PreparedStatement.h @@ -0,0 +1,95 @@ +#ifndef SAPPHIRE_PREPAREDSTATEMENT_H +#define SAPPHIRE_PREPAREDSTATEMENT_H + +#include +#include +#include "Statement.h" +#include + +namespace Core +{ + namespace Db + { + + class ParamBind; + class ResultBind; + + class PreparedStatement : public Statement + { + protected: + MYSQL_STMT * m_pStmt; + Connection * m_pConnection; + boost::scoped_ptr< ParamBind > m_pParamBind; + uint32_t m_paramCount; + + int32_t resultSetConcurrency; + int32_t resultSetType; + + //boost::scoped_ptr< MySQL_PreparedResultSetMetaData > res_meta; + //boost::scoped_ptr< MySQL_ParameterMetaData > param_meta; + + boost::shared_ptr< ResultBind > m_pResultBind; + + unsigned int warningsCount; + + virtual void doQuery(); + virtual void closeIntern(); + + bool sendLongDataBeforeParamBind(); + + public: + PreparedStatement( MYSQL_STMT* pStmt, Connection* pConn ); + virtual ~PreparedStatement(); + + Connection* getConnection() override; + MYSQL_STMT* getRawStmt(); + + uint32_t errNo() override; + + uint32_t getWarningCount() override; + + uint64_t getUpdateCount() override; + + void clearParameters(); + + bool execute(); + bool execute( const std::string& sql ); + + ResultSet* executeQuery(); + ResultSet* executeQuery( const std::string& sql ); + + bool getMoreResults(); + + ResultSet* getResultSet(); + + void setBlob( uint32_t parameterIndex, std::istream * blob ); + + void setBoolean( uint32_t parameterIndex, bool value ); + + void setBigInt( uint32_t parameterIndex, const std::string& value ); + + void setDateTime( uint32_t parameterIndex, const std::string& value ); + + void setDouble( uint32_t parameterIndex, double value ); + + void setInt( uint32_t parameterIndex, int32_t value ); + + void setUInt( uint32_t parameterIndex, uint32_t value ); + + void setInt64( uint32_t parameterIndex, int64_t value ); + + void setUInt64( uint32_t parameterIndex, uint64_t value ); + + void setNull( uint32_t parameterIndex, int sqlType ); + + void setString( uint32_t parameterIndex, const std::string& value ); + + private: + PreparedStatement( const PreparedStatement& ); + void operator=( PreparedStatement& ); + }; + + } +} + +#endif //SAPPHIRE_PREPAREDSTATEMENT_H diff --git a/src/servers/Server_Common/Database/ResultBind.cpp b/src/servers/Server_Common/Database/ResultBind.cpp new file mode 100644 index 00000000..54bb0dc4 --- /dev/null +++ b/src/servers/Server_Common/Database/ResultBind.cpp @@ -0,0 +1,157 @@ +#include "ResultBind.h" + +#include "mysql_util.h" +#include "ResultBind.h" + +#include + +#include + +namespace Core +{ + namespace Db + { + + struct st_buffer_size_type + { + char * buffer; + size_t size; + enum_field_types type; + st_buffer_size_type( char * b, size_t s, enum_field_types t ) + : buffer( b ), + size( s ), + type( t ) + { + + } + }; + + typedef std::pair< char*, size_t > BufferSizePair; + static struct st_buffer_size_type + allocate_buffer_for_field( const MYSQL_FIELD * const field ) + { + switch( field->type ) + { + case MYSQL_TYPE_NULL: + return st_buffer_size_type( nullptr, 0, field->type ); + case MYSQL_TYPE_TINY: + return st_buffer_size_type( new char[1], 1, field->type ); + case MYSQL_TYPE_SHORT: + return st_buffer_size_type( new char[2], 2, field->type ); + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + return st_buffer_size_type( new char[4], 4, field->type ); + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_LONGLONG: + return st_buffer_size_type( new char[8], 8, field->type ); + case MYSQL_TYPE_YEAR: + return st_buffer_size_type( new char[2], 2, MYSQL_TYPE_SHORT ); + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + return st_buffer_size_type( new char[sizeof( MYSQL_TIME )], sizeof( MYSQL_TIME ), field->type ); + + + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: +#if LIBMYSQL_VERSION_ID > 50700 + case MYSQL_TYPE_JSON: + return st_buffer_size_type(new char[field->max_length + 1], field->max_length + 1, field->type); +#endif //LIBMYSQL_VERSION_ID > 50700 + + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return st_buffer_size_type( new char[64], 64, field->type ); +#if A1 + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_YEAR: + return st_buffer_size_type(new char[10], 10, field->type); +#endif +#if A0 + // There two are not sent over the wire + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: +#endif + case MYSQL_TYPE_BIT: + return st_buffer_size_type( new char[8], 8, MYSQL_TYPE_BIT ); + case MYSQL_TYPE_GEOMETRY: + default: + throw std::runtime_error( "allocate_buffer_for_field: invalid rbind data type" ); + } + } + + ResultBind::ResultBind( MYSQL_STMT* pStmt ) + : m_pStmt( pStmt ), + m_numFields( 0 ), + m_isNull( nullptr ), + m_err( nullptr ), + m_len( nullptr ), + m_pBind( nullptr ) + { + } + + ResultBind::~ResultBind() + { + if( m_pBind.get() ) + { + for( uint32_t i = 0; i < m_numFields; ++i ) + delete[] ( char* ) m_pBind[i].buffer; + } + } + + void ResultBind::bindResult() + { + for( uint32_t i = 0; i < m_numFields; ++i ) + delete[] ( char* ) m_pBind[i].buffer; + + m_pBind.reset( nullptr ); + m_isNull.reset( nullptr ); + m_err.reset( nullptr ); + m_len.reset( nullptr ); + + m_numFields = mysql_stmt_field_count( m_pStmt ); + if( !m_numFields ) + return; + + m_pBind.reset( new MYSQL_BIND[m_numFields] ); + memset( m_pBind.get(), 0, sizeof( MYSQL_BIND ) * m_numFields ); + + m_isNull.reset( new my_bool[m_numFields] ); + memset( m_isNull.get(), 0, sizeof( my_bool ) * m_numFields ); + + m_err.reset( new my_bool[m_numFields] ); + memset( m_err.get(), 0, sizeof( my_bool ) * m_numFields ); + + m_len.reset( new unsigned long[m_numFields] ); + memset( m_len.get(), 0, sizeof( unsigned long ) * m_numFields ); + + //boost::scoped_ptr< NativeAPI::NativeResultsetWrapper > resultMeta(proxy->result_metadata()); + + MYSQL_RES* info = mysql_stmt_result_metadata( m_pStmt ); + for( uint32_t i = 0; i < m_numFields; ++i ) + { +// MYSQL_FIELD * field = resultMeta->fetch_field(); + + MYSQL_FIELD * field = mysql_fetch_field( info ); + struct st_buffer_size_type p = allocate_buffer_for_field(field); + m_pBind[i].buffer_type = p.type; + m_pBind[i].buffer = p.buffer; + m_pBind[i].buffer_length = static_cast< unsigned long >( p.size ); + m_pBind[i].length = &m_len[i]; + m_pBind[i].is_null = &m_isNull[i]; + m_pBind[i].error = &m_err[i]; + m_pBind[i].is_unsigned = field->flags & UNSIGNED_FLAG; + } + if( mysql_stmt_bind_result( m_pStmt, m_pBind.get() ) ) + throw std::runtime_error( "Couldn't bind : " + std::to_string( mysql_stmt_errno( m_pStmt ) ) ); + } + + } +} + diff --git a/src/servers/Server_Common/Database/ResultBind.h b/src/servers/Server_Common/Database/ResultBind.h new file mode 100644 index 00000000..d21a8a07 --- /dev/null +++ b/src/servers/Server_Common/Database/ResultBind.h @@ -0,0 +1,37 @@ +#ifndef SAPPHIRE_RESULTBIND_H +#define SAPPHIRE_RESULTBIND_H + +#include + +#include "mysql_util.h" +#include "mysql.h" +#include "PreparedStatement.h" + +namespace Core +{ +namespace Db +{ + + class ResultBind + { + uint32_t m_numFields; + boost::scoped_array< char > m_isNull; + boost::scoped_array< char > m_err; + boost::scoped_array< unsigned long > m_len; + MYSQL_STMT* m_pStmt; + + public: + boost::scoped_array< MYSQL_BIND > m_pBind; + + ResultBind( MYSQL_STMT* pStmt ); + + ~ResultBind(); + + void bindResult(); + + }; + +} +} + +#endif //SAPPHIRE_RESULTBIND_H diff --git a/src/servers/Server_Common/Database/ResultSet.h b/src/servers/Server_Common/Database/ResultSet.h index d167d976..30aa2095 100644 --- a/src/servers/Server_Common/Database/ResultSet.h +++ b/src/servers/Server_Common/Database/ResultSet.h @@ -18,7 +18,7 @@ namespace Core //class ResultSetMetaData; - class ResultSet : public ResultSetBase + class ResultSet { MYSQL_ROW m_row; @@ -48,50 +48,50 @@ namespace Core virtual ~ResultSet(); - uint32_t findColumn( const std::string& columnLabel ) const; + virtual uint32_t findColumn( const std::string& columnLabel ) const; - std::istream * getBlob( uint32_t columnIndex ) const; - std::istream * getBlob( const std::string& columnLabel ) const; + virtual std::istream * getBlob( uint32_t columnIndex ) const; + virtual std::istream * getBlob( const std::string& columnLabel ) const; - std::vector< char > getBlobVector( uint32_t columnIndex ) const; - std::vector< char > getBlobVector( const std::string& columnLabel ) const; + virtual std::vector< char > getBlobVector( uint32_t columnIndex ) const; + virtual std::vector< char > getBlobVector( const std::string& columnLabel ) const; - bool getBoolean( uint32_t columnIndex ) const; - bool getBoolean( const std::string& columnLabel ) const; + virtual bool getBoolean( uint32_t columnIndex ) const; + virtual bool getBoolean( const std::string& columnLabel ) const; - long double getDouble( uint32_t columnIndex ) const; - long double getDouble( const std::string& columnLabel ) const; + virtual long double getDouble( uint32_t columnIndex ) const; + virtual long double getDouble( const std::string& columnLabel ) const; - int32_t getInt( uint32_t columnIndex ) const; - int32_t getInt( const std::string& columnLabel ) const; + virtual int32_t getInt( uint32_t columnIndex ) const; + virtual int32_t getInt( const std::string& columnLabel ) const; - uint32_t getUInt( uint32_t columnIndex ) const; - uint32_t getUInt( const std::string& columnLabel ) const; + virtual uint32_t getUInt( uint32_t columnIndex ) const; + virtual uint32_t getUInt( const std::string& columnLabel ) const; - int64_t getInt64( uint32_t columnIndex ) const; - int64_t getInt64( const std::string& columnLabel ) const; + virtual int64_t getInt64( uint32_t columnIndex ) const; + virtual int64_t getInt64( const std::string& columnLabel ) const; - uint64_t getUInt64( uint32_t columnIndex ) const; - uint64_t getUInt64( const std::string& columnLabel ) const; + virtual uint64_t getUInt64( uint32_t columnIndex ) const; + virtual uint64_t getUInt64( const std::string& columnLabel ) const; //sql::ResultSetMetaData * getMetaData() const; - size_t getRow() const; + virtual size_t getRow() const; - const Statement * getStatement() const; + virtual const Statement * getStatement() const; - std::string getString( uint32_t columnIndex ) const; - std::string getString( const std::string& columnLabel ) const; + virtual std::string getString( uint32_t columnIndex ) const; + virtual std::string getString( const std::string& columnLabel ) const; - bool isFirst() const; + virtual bool isFirst() const; - bool isLast() const; + virtual bool isLast() const; - bool isNull( uint32_t columnIndex ) const; + virtual bool isNull( uint32_t columnIndex ) const; - bool isNull( const std::string& columnLabel ) const; + virtual bool isNull( const std::string& columnLabel ) const; - bool next(); + virtual bool next(); size_t rowsCount() const; private: diff --git a/src/servers/Server_Common/Database/Statement.h b/src/servers/Server_Common/Database/Statement.h index df63676b..5491118f 100644 --- a/src/servers/Server_Common/Database/Statement.h +++ b/src/servers/Server_Common/Database/Statement.h @@ -3,7 +3,6 @@ #include #include -#include "StatementBase.h" namespace Core { @@ -12,7 +11,7 @@ namespace Db class Connection; class ResultSet; - class Statement : public StatementBase + class Statement { protected: Connection * m_pConnection; @@ -28,19 +27,19 @@ namespace Db virtual ~Statement() {}; - Connection * getConnection() override; + virtual Connection * getConnection(); - bool execute( const std::string& sql ) override; + virtual bool execute( const std::string& sql ); - ResultSet * executeQuery( const std::string& sql ) override; + virtual ResultSet * executeQuery( const std::string& sql ); - ResultSet * getResultSet() override; + virtual ResultSet * getResultSet(); - uint64_t getUpdateCount() override; + virtual uint64_t getUpdateCount(); - uint32_t getWarningCount() override; + virtual uint32_t getWarningCount(); - uint32_t errNo() override; + virtual uint32_t errNo(); private: /* Prevent use of these */ diff --git a/src/servers/Server_Common/Database/mysql_util.cpp b/src/servers/Server_Common/Database/mysql_util.cpp index c0f75898..0260b847 100644 --- a/src/servers/Server_Common/Database/mysql_util.cpp +++ b/src/servers/Server_Common/Database/mysql_util.cpp @@ -1,4 +1,6 @@ #include "mysql_util.h" +#include "DataType.h" +#include long double Util::strtold(const char *nptr, char **endptr) { @@ -57,3 +59,374 @@ long double Util::strtonum( const std::string &str, int radix ) return val; } + +#define cppconn_mbcharlen_big5 NULL +#define check_mb_big5 NULL +#define cppconn_mbcharlen_ujis NULL +#define check_mb_ujis NULL +#define cppconn_mbcharlen_sjis NULL +#define check_mb_sjis NULL +#define cppconn_mbcharlen_euckr NULL +#define check_mb_euckr NULL +#define cppconn_mbcharlen_gb2312 NULL +#define check_mb_gb2312 NULL +#define cppconn_mbcharlen_gbk NULL +#define check_mb_gbk NULL +#define cppconn_mbcharlen_utf8 NULL +#define check_mb_utf8_valid NULL +#define cppconn_mbcharlen_ucs2 NULL +#define check_mb_ucs2 NULL +#define cppconn_mbcharlen_cp932 NULL +#define check_mb_cp932 NULL +#define cppconn_mbcharlen_eucjpms NULL +#define check_mb_eucjpms NULL +#define cppconn_mbcharlen_utf8 NULL +#define check_mb_utf8_valid NULL +#define cppconn_mbcharlen_utf8mb4 cppconn_mbcharlen_utf8 +#define check_mb_utf8mb4_valid check_mb_utf8_valid +#define cppconn_mbcharlen_utf16 NULL +#define check_mb_utf16_valid NULL +#define cppconn_mbcharlen_utf32 NULL +#define check_mb_utf32_valid NULL + +/* {{{ our_charsets60 */ +const Util::OUR_CHARSET our_charsets60[] = + { + { 1, "big5","big5_chinese_ci", 1, 2, "", cppconn_mbcharlen_big5, check_mb_big5}, + { 3, "dec8", "dec8_swedisch_ci", 1, 1, "", NULL, NULL}, + { 4, "cp850", "cp850_general_ci", 1, 1, "", NULL, NULL}, + { 6, "hp8", "hp8_english_ci", 1, 1, "", NULL, NULL}, + { 7, "koi8r", "koi8r_general_ci", 1, 1, "", NULL, NULL}, + { 8, "latin1", "latin1_swedish_ci", 1, 1, "", NULL, NULL}, + { 9, "latin2", "latin2_general_ci", 1, 1, "", NULL, NULL}, + { 10, "swe7", "swe7_swedish_ci", 1, 1, "", NULL, NULL}, + { 11, "ascii", "ascii_general_ci", 1, 1, "", NULL, NULL}, + { 12, "ujis", "ujis_japanese_ci", 1, 3, "", cppconn_mbcharlen_ujis, check_mb_ujis}, + { 13, "sjis", "sjis_japanese_ci", 1, 2, "", cppconn_mbcharlen_sjis, check_mb_sjis}, + { 16, "hebrew", "hebrew_general_ci", 1, 1, "", NULL, NULL}, + { 18, "tis620", "tis620_thai_ci", 1, 1, "", NULL, NULL}, + { 19, "euckr", "euckr_korean_ci", 1, 2, "", cppconn_mbcharlen_euckr, check_mb_euckr}, + { 22, "koi8u", "koi8u_general_ci", 1, 1, "", NULL, NULL}, + { 24, "gb2312", "gb2312_chinese_ci", 1, 2, "", cppconn_mbcharlen_gb2312, check_mb_gb2312}, + { 25, "greek", "greek_general_ci", 1, 1, "", NULL, NULL}, + { 26, "cp1250", "cp1250_general_ci", 1, 1, "", NULL, NULL}, + { 28, "gbk", "gbk_chinese_ci", 1, 2, "", cppconn_mbcharlen_gbk, check_mb_gbk}, + { 30, "latin5", "latin5_turkish_ci", 1, 1, "", NULL, NULL}, + { 32, "armscii8", "armscii8_general_ci", 1, 1, "", NULL, NULL}, + { 33, "utf8", "utf8_general_ci", 1, 3, "UTF-8 Unicode", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 35, "ucs2", "ucs2_general_ci", 2, 2, "UCS-2 Unicode", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 36, "cp866", "cp866_general_ci", 1, 1, "", NULL, NULL}, + { 37, "keybcs2", "keybcs2_general_ci", 1, 1, "", NULL, NULL}, + { 38, "macce", "macce_general_ci", 1, 1, "", NULL, NULL}, + { 39, "macroman", "macroman_general_ci", 1, 1, "", NULL, NULL}, + { 40, "cp852", "cp852_general_ci", 1, 1, "", NULL, NULL}, + { 41, "latin7", "latin7_general_ci", 1, 1, "", NULL, NULL}, + { 51, "cp1251", "cp1251_general_ci", 1, 1, "", NULL, NULL}, + { 57, "cp1256", "cp1256_general_ci", 1, 1, "", NULL, NULL}, + { 59, "cp1257", "cp1257_general_ci", 1, 1, "", NULL, NULL}, + { 63, "binary", "binary", 1, 1, "", NULL, NULL}, + { 92, "geostd8", "geostd8_general_ci", 1, 1, "", NULL, NULL}, + { 95, "cp932", "cp932_japanese_ci", 1, 2, "", cppconn_mbcharlen_cp932, check_mb_cp932}, + { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, "", cppconn_mbcharlen_eucjpms, check_mb_eucjpms}, + { 2, "latin2", "latin2_czech_cs", 1, 1, "", NULL, NULL}, + { 5, "latin1", "latin1_german_ci", 1, 1, "", NULL, NULL}, + { 14, "cp1251", "cp1251_bulgarian_ci", 1, 1, "", NULL, NULL}, + { 15, "latin1", "latin1_danish_ci", 1, 1, "", NULL, NULL}, + { 17, "filename", "filename", 1, 5, "", NULL, NULL}, + { 20, "latin7", "latin7_estonian_cs", 1, 1, "", NULL, NULL}, + { 21, "latin2", "latin2_hungarian_ci", 1, 1, "", NULL, NULL}, + { 23, "cp1251", "cp1251_ukrainian_ci", 1, 1, "", NULL, NULL}, + { 27, "latin2", "latin2_croatian_ci", 1, 1, "", NULL, NULL}, + { 29, "cp1257", "cp1257_lithunian_ci", 1, 1, "", NULL, NULL}, + { 31, "latin1", "latin1_german2_ci", 1, 1, "", NULL, NULL}, + { 34, "cp1250", "cp1250_czech_cs", 1, 1, "", NULL, NULL}, + { 42, "latin7", "latin7_general_cs", 1, 1, "", NULL, NULL}, + { 43, "macce", "macce_bin", 1, 1, "", NULL, NULL}, + { 44, "cp1250", "cp1250_croatian_ci", 1, 1, "", NULL, NULL}, + { 47, "latin1", "latin1_bin", 1, 1, "", NULL, NULL}, + { 48, "latin1", "latin1_general_ci", 1, 1, "", NULL, NULL}, + { 49, "latin1", "latin1_general_cs", 1, 1, "", NULL, NULL}, + { 50, "cp1251", "cp1251_bin", 1, 1, "", NULL, NULL}, + { 52, "cp1251", "cp1251_general_cs", 1, 1, "", NULL, NULL}, + { 53, "macroman", "macroman_bin", 1, 1, "", NULL, NULL}, + { 58, "cp1257", "cp1257_bin", 1, 1, "", NULL, NULL}, + { 60, "armascii8", "armascii8_bin", 1, 1, "", NULL, NULL}, + { 65, "ascii", "ascii_bin", 1, 1, "", NULL, NULL}, + { 66, "cp1250", "cp1250_bin", 1, 1, "", NULL, NULL}, + { 67, "cp1256", "cp1256_bin", 1, 1, "", NULL, NULL}, + { 68, "cp866", "cp866_bin", 1, 1, "", NULL, NULL}, + { 69, "dec8", "dec8_bin", 1, 1, "", NULL, NULL}, + { 70, "greek", "greek_bin", 1, 1, "", NULL, NULL}, + { 71, "hebrew", "hebrew_bin", 1, 1, "", NULL, NULL}, + { 72, "hp8", "hp8_bin", 1, 1, "", NULL, NULL}, + { 73, "keybcs2", "keybcs2_bin", 1, 1, "", NULL, NULL}, + { 74, "koi8r", "koi8r_bin", 1, 1, "", NULL, NULL}, + { 75, "koi8u", "koi8u_bin", 1, 1, "", NULL, NULL}, + { 77, "latin2", "latin2_bin", 1, 1, "", NULL, NULL}, + { 78, "latin5", "latin5_bin", 1, 1, "", NULL, NULL}, + { 79, "latin7", "latin7_bin", 1, 1, "", NULL, NULL}, + { 80, "cp850", "cp850_bin", 1, 1, "", NULL, NULL}, + { 81, "cp852", "cp852_bin", 1, 1, "", NULL, NULL}, + { 82, "swe7", "swe7_bin", 1, 1, "", NULL, NULL}, + { 93, "geostd8", "geostd8_bin", 1, 1, "", NULL, NULL}, + { 83, "utf8", "utf8_bin", 1, 3, "UTF-8 Unicode", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 84, "big5", "big5_bin", 1, 2, "", cppconn_mbcharlen_big5, check_mb_big5}, + { 85, "euckr", "euckr_bin", 1, 2, "", cppconn_mbcharlen_euckr, check_mb_euckr}, + { 86, "gb2312", "gb2312_bin", 1, 2, "", cppconn_mbcharlen_gb2312, check_mb_gb2312}, + { 87, "gbk", "gbk_bin", 1, 2, "", cppconn_mbcharlen_gbk, check_mb_gbk}, + { 88, "sjis", "sjis_bin", 1, 2, "", cppconn_mbcharlen_sjis, check_mb_sjis}, + { 89, "tis620", "tis620_bin", 1, 1, "", NULL, NULL}, + { 90, "ucs2", "ucs2_bin", 2, 2, "UCS-2 Unicode", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 91, "ujis", "ujis_bin", 1, 3, "", cppconn_mbcharlen_ujis, check_mb_ujis}, + { 94, "latin1", "latin1_spanish_ci", 1, 1, "", NULL, NULL}, + { 96, "cp932", "cp932_bin", 1, 2, "", cppconn_mbcharlen_cp932, check_mb_cp932}, + { 99, "cp1250", "cp1250_polish_ci", 1, 1, "", NULL, NULL}, + { 98, "eucjpms", "eucjpms_bin", 1, 3, "", cppconn_mbcharlen_eucjpms, check_mb_eucjpms}, + { 128, "ucs2", "ucs2_unicode_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 129, "ucs2", "ucs2_icelandic_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 130, "ucs2", "ucs2_latvian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 131, "ucs2", "ucs2_romanian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 132, "ucs2", "ucs2_slovenian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 133, "ucs2", "ucs2_polish_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 134, "ucs2", "ucs2_estonian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 135, "ucs2", "ucs2_spanish_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 136, "ucs2", "ucs2_swedish_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 137, "ucs2", "ucs2_turkish_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 138, "ucs2", "ucs2_czech_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 139, "ucs2", "ucs2_danish_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 140, "ucs2", "ucs2_lithunian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 141, "ucs2", "ucs2_slovak_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 142, "ucs2", "ucs2_spanish2_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 143, "ucs2", "ucs2_roman_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 144, "ucs2", "ucs2_persian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 145, "ucs2", "ucs2_esperanto_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 146, "ucs2", "ucs2_hungarian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 147, "ucs2", "ucs2_sinhala_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 148, "ucs2", "ucs2_german2_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 149, "ucs2", "ucs2_croatian_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 150, "ucs2", "ucs2_unicode_520_ci", 2, 2, "", cppconn_mbcharlen_ucs2, check_mb_ucs2}, + { 192, "utf8", "utf8_unicode_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 193, "utf8", "utf8_icelandic_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 194, "utf8", "utf8_latvian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 195, "utf8", "utf8_romanian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 196, "utf8", "utf8_slovenian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 197, "utf8", "utf8_polish_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 198, "utf8", "utf8_estonian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 199, "utf8", "utf8_spanish_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 200, "utf8", "utf8_swedish_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 201, "utf8", "utf8_turkish_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 202, "utf8", "utf8_czech_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 203, "utf8", "utf8_danish_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid }, + { 204, "utf8", "utf8_lithunian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid }, + { 205, "utf8", "utf8_slovak_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 206, "utf8", "utf8_spanish2_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 207, "utf8", "utf8_roman_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 208, "utf8", "utf8_persian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 209, "utf8", "utf8_esperanto_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 210, "utf8", "utf8_hungarian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 211, "utf8", "utf8_sinhala_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 212, "utf8", "utf8_german2_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 213, "utf8", "utf8_croatian_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 214, "utf8", "utf8_unicode_520_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 215, "utf8", "utf8_vietnamese_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 223, "utf8", "utf8_general_mysql500_ci", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + { 45, "utf8mb4", "utf8mb4_general_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 46, "utf8mb4", "utf8mb4_bin", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 224, "utf8mb4", "utf8mb4_unicode_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 225, "utf8mb4", "utf8mb4_icelandic_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 226, "utf8mb4", "utf8mb4_latvian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 227, "utf8mb4", "utf8mb4_romanian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 228, "utf8mb4", "utf8mb4_slovenian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 229, "utf8mb4", "utf8mb4_polish_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 230, "utf8mb4", "utf8mb4_estonian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 231, "utf8mb4", "utf8mb4_spanish_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 232, "utf8mb4", "utf8mb4_swedish_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 233, "utf8mb4", "utf8mb4_turkish_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 234, "utf8mb4", "utf8mb4_czech_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 235, "utf8mb4", "utf8mb4_danish_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 236, "utf8mb4", "utf8mb4_lithuanian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 237, "utf8mb4", "utf8mb4_slovak_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 238, "utf8mb4", "utf8mb4_spanish2_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 239, "utf8mb4", "utf8mb4_roman_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 240, "utf8mb4", "utf8mb4_persian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 241, "utf8mb4", "utf8mb4_esperanto_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 242, "utf8mb4", "utf8mb4_hungarian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 243, "utf8mb4", "utf8mb4_sinhala_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 244, "utf8mb4", "utf8mb4_german2_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 245, "utf8mb4", "utf8mb4_croatian_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 246, "utf8mb4", "utf8mb4_unicode_520_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + { 247, "utf8mb4", "utf8mb4_vietnamese_ci", 1, 4, "", cppconn_mbcharlen_utf8mb4, check_mb_utf8mb4_valid}, + + /*Should not really happen, but adding them */ + { 254, "utf8", "utf8_general_cs", 1, 3, "", cppconn_mbcharlen_utf8, check_mb_utf8_valid}, + + { 101, "utf16", "utf16_unicode_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 102, "utf16", "utf16_icelandic_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 103, "utf16", "utf16_latvian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 104, "utf16", "utf16_romanian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 105, "utf16", "utf16_slovenian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 106, "utf16", "utf16_polish_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 107, "utf16", "utf16_estonian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 108, "utf16", "utf16_spanish_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 109, "utf16", "utf16_swedish_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 110, "utf16", "utf16_turkish_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 111, "utf16", "utf16_czech_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 112, "utf16", "utf16_danish_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 113, "utf16", "utf16_lithuanian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 114, "utf16", "utf16_slovak_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 115, "utf16", "utf16_spanish2_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 116, "utf16", "utf16_roman_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 117, "utf16", "utf16_persian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 118, "utf16", "utf16_esperanto_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 119, "utf16", "utf16_hungarian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 120, "utf16", "utf16_sinhala_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 121, "utf16", "utf16_german2_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 122, "utf16", "utf16_croatian_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 123, "utf16", "utf16_unicode_520_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + { 124, "utf16", "utf16_vietnamese_ci", 2, 4, "", cppconn_mbcharlen_utf16, check_mb_utf16_valid}, + + { 160, "utf32", "utf32_unicode_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 161, "utf32", "utf32_icelandic_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 162, "utf32", "utf32_latvian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 163, "utf32", "utf32_romanian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 164, "utf32", "utf32_slovenian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 165, "utf32", "utf32_polish_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 166, "utf32", "utf32_estonian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 167, "utf32", "utf32_spanish_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 168, "utf32", "utf32_swedish_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 169, "utf32", "utf32_turkish_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 170, "utf32", "utf32_czech_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 171, "utf32", "utf32_danish_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 172, "utf32", "utf32_lithuanian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 173, "utf32", "utf32_slovak_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 174, "utf32", "utf32_spanish2_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 175, "utf32", "utf32_roman_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 176, "utf32", "utf32_persian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 177, "utf32", "utf32_esperanto_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 178, "utf32", "utf32_hungarian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 179, "utf32", "utf32_sinhala_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 180, "utf32", "utf32_german2_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 181, "utf32", "utf32_croatian_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 182, "utf32", "utf32_unicode_520_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + { 183, "utf32", "utf32_vietnamese_ci", 4, 4, "", cppconn_mbcharlen_utf32, check_mb_utf32_valid}, + + { 0, NULL, NULL, 0, 0, NULL, NULL, NULL} +}; +#define MAGIC_BINARY_CHARSET_NR 63 + +const Util::OUR_CHARSET * Util::find_charset(unsigned int charsetnr) +{ + const OUR_CHARSET * c = our_charsets60; + + do { + if (c->nr == charsetnr) { + return c; + } + ++c; + } while (c[0].nr != 0); + return NULL; +} + +int Util::mysql_type_to_datatype( const MYSQL_FIELD * const field ) +{ + switch (field->type) { + case MYSQL_TYPE_BIT: + if (field->flags !=(BINARY_FLAG|UNSIGNED_FLAG)) + return Core::Db::DataType::BIT; + return Core::Db::DataType::BINARY; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return Core::Db::DataType::DECIMAL; + case MYSQL_TYPE_TINY: + return Core::Db::DataType::TINYINT; + case MYSQL_TYPE_SHORT: + return Core::Db::DataType::SMALLINT; + case MYSQL_TYPE_INT24: + return Core::Db::DataType::MEDIUMINT; + case MYSQL_TYPE_LONG: + return Core::Db::DataType::INTEGER; + case MYSQL_TYPE_LONGLONG: + return Core::Db::DataType::BIGINT; + case MYSQL_TYPE_FLOAT: + return Core::Db::DataType::REAL; + case MYSQL_TYPE_DOUBLE: + return Core::Db::DataType::DOUBLE; + case MYSQL_TYPE_NULL: + return Core::Db::DataType::SQLNULL; + case MYSQL_TYPE_TIMESTAMP: + return Core::Db::DataType::TIMESTAMP; + case MYSQL_TYPE_DATE: + return Core::Db::DataType::DATE; + case MYSQL_TYPE_TIME: + return Core::Db::DataType::TIME; + case MYSQL_TYPE_YEAR: + return Core::Db::DataType::YEAR; + case MYSQL_TYPE_DATETIME: + return Core::Db::DataType::TIMESTAMP; + case MYSQL_TYPE_TINY_BLOB:// should no appear over the wire + { + bool isBinary = (field->flags & BINARY_FLAG) && + field->charsetnr == MAGIC_BINARY_CHARSET_NR; + const Util::OUR_CHARSET * const cs = + Util::find_charset(field->charsetnr); + if (!cs) { + std::ostringstream msg("Server sent unknown charsetnr ("); + msg << field->charsetnr << ") . Please report"; + throw std::runtime_error( msg.str() ); + } + return isBinary ? Core::Db::DataType::VARBINARY : Core::Db::DataType::VARCHAR; + } + case MYSQL_TYPE_MEDIUM_BLOB:// should no appear over the wire + case MYSQL_TYPE_LONG_BLOB:// should no appear over the wire + case MYSQL_TYPE_BLOB: + { + bool isBinary = (field->flags & BINARY_FLAG) && + field->charsetnr == MAGIC_BINARY_CHARSET_NR; + const Util::OUR_CHARSET * const cs = + Util::find_charset(field->charsetnr); + if (!cs) { + std::ostringstream msg("Server sent unknown charsetnr ("); + msg << field->charsetnr << ") . Please report"; + throw std::runtime_error( msg.str() ); + } + return isBinary ? Core::Db::DataType::LONGVARBINARY : Core::Db::DataType::LONGVARCHAR; + } + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + if (field->flags & SET_FLAG) { + return Core::Db::DataType::SET; + } + if (field->flags & ENUM_FLAG) { + return Core::Db::DataType::ENUM; + } + if ((field->flags & BINARY_FLAG) && field->charsetnr == MAGIC_BINARY_CHARSET_NR) { + return Core::Db::DataType::VARBINARY; + } + return Core::Db::DataType::VARCHAR; + case MYSQL_TYPE_STRING: + if (field->flags & SET_FLAG) { + return Core::Db::DataType::SET; + } + if (field->flags & ENUM_FLAG) { + return Core::Db::DataType::ENUM; + } + if ((field->flags & BINARY_FLAG) && field->charsetnr == MAGIC_BINARY_CHARSET_NR) { + return Core::Db::DataType::BINARY; + } + return Core::Db::DataType::CHAR; + case MYSQL_TYPE_ENUM: + /* This hould never happen - MYSQL_TYPE_ENUM is not sent over the wire, just used in the server */ + return Core::Db::DataType::ENUM; + case MYSQL_TYPE_SET: + /* This hould never happen - MYSQL_TYPE_SET is not sent over the wire, just used in the server */ + return Core::Db::DataType::SET; + case MYSQL_TYPE_GEOMETRY: + return Core::Db::DataType::GEOMETRY; +#if LIBMYSQL_VERSION_ID > 50700 + case MYSQL_TYPE_JSON: + return Core::Db::DataType::JSON; +#endif //LIBMYSQL_VERSION_ID > 50700 + default: + return Core::Db::DataType::UNKNOWN; + } +} \ No newline at end of file diff --git a/src/servers/Server_Common/Database/mysql_util.h b/src/servers/Server_Common/Database/mysql_util.h index 8421c4d1..ba5aa273 100644 --- a/src/servers/Server_Common/Database/mysql_util.h +++ b/src/servers/Server_Common/Database/mysql_util.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include #ifndef UL64 #ifdef _WIN32 @@ -107,7 +108,23 @@ with this program; if not, write to the Free Software Foundation, Inc., 32)) namespace Util { - long double strtold(const char *nptr, char **endptr); - long double strtonum( const std::string &str, int radix = 10); + long double strtold( const char *nptr, char **endptr ); + long double strtonum( const std::string &str, int radix = 10 ); + int32_t mysql_type_to_datatype( const MYSQL_FIELD * const field ); + + typedef struct st_our_charset + { + unsigned int nr; + const char *name; + const char *collation; + unsigned int char_minlen; + unsigned int char_maxlen; + const char *comment; + unsigned int (*mb_charlen)(unsigned int c); + unsigned int (*mb_valid)(const char *start, const char *end); + } OUR_CHARSET; + + const OUR_CHARSET * find_charset(unsigned int charsetnr); + } #endif //SAPPHIRE_MYSQL_UTIL_H diff --git a/src/servers/Server_Zone/ServerZone.cpp b/src/servers/Server_Zone/ServerZone.cpp index 978fe4a6..cc683b15 100644 --- a/src/servers/Server_Zone/ServerZone.cpp +++ b/src/servers/Server_Zone/ServerZone.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -213,6 +215,7 @@ bool Core::ServerZone::loadSettings( int32_t argc, char* argv[] ) } + // binary data test boost::scoped_ptr< Core::Db::Statement > stmt3( con->createStatement() ); boost::scoped_ptr< Core::Db::ResultSet > res1( stmt3->executeQuery( "SELECT * FROM charabase" ) ); @@ -221,6 +224,30 @@ bool Core::ServerZone::loadSettings( int32_t argc, char* argv[] ) auto blob = res1->getBlobVector( "Customize" ); } + boost::scoped_ptr< Core::Db::PreparedStatement > pstmt2( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) ); + pstmt2->setInt( 1, 1021 ); + pstmt2->execute(); + + pstmt2->setInt( 1, 1001 ); + pstmt2->execute(); + + boost::scoped_ptr< Core::Db::PreparedStatement > pstmt( con->prepareStatement( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( ?, ?, ?);" ) ); + pstmt->setInt( 1, 1001 ); + pstmt->setString( 2, "123.123.123.123" ); + pstmt->setInt( 3, 5454 ); + pstmt->execute(); + + pstmt->setInt( 1, 1021 ); + pstmt->setString( 2, "173.173.173.173" ); + pstmt->setInt( 3, 5151 ); + pstmt->execute(); + + boost::scoped_ptr< Core::Db::PreparedStatement > pstmt1( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) ); + pstmt->setInt( 1, 1021 ); + pstmt->execute(); + + pstmt->setInt( 1, 1001 ); + pstmt->execute(); } catch( std::runtime_error e )