1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-26 06:27:45 +00:00

Implemented prepared statmements

This commit is contained in:
Mordred 2017-09-23 23:26:23 +02:00
parent 98899756fb
commit 4e25fa0ed7
14 changed files with 2237 additions and 39 deletions

View file

@ -1,6 +1,7 @@
#include "Connection.h"
#include "MySqlBase.h"
#include "Statement.h"
#include "PreparedStatement.h"
#include <vector>
#include <boost/scoped_array.hpp>
@ -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 );
}

View file

@ -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);

View file

@ -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

View file

@ -0,0 +1,623 @@
#include "PreparedResultSet.h"
#include "ResultBind.h"
#include "DataType.h"
#include <string>
#include <cctype>
#include <clocale>
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<MYSQL_TIME*>( 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;
}

View file

@ -0,0 +1,115 @@
#ifndef SAPPHIRE_PREPAREDRESULTSET_H
#define SAPPHIRE_PREPAREDRESULTSET_H
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#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

View file

@ -0,0 +1,696 @@
#include "PreparedStatement.h"
#include "PreparedResultSet.h"
#include "Connection.h"
#include "ResultBind.h"
#include <boost/variant.hpp>
#include <boost/scoped_array.hpp>
#include <errmsg.h>
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<char *>(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<unsigned int, Blob_t > 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 );
}

View file

@ -0,0 +1,95 @@
#ifndef SAPPHIRE_PREPAREDSTATEMENT_H
#define SAPPHIRE_PREPAREDSTATEMENT_H
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include "Statement.h"
#include <mysql.h>
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

View file

@ -0,0 +1,157 @@
#include "ResultBind.h"
#include "mysql_util.h"
#include "ResultBind.h"
#include <string.h>
#include <boost/scoped_ptr.hpp>
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 ) ) );
}
}
}

View file

@ -0,0 +1,37 @@
#ifndef SAPPHIRE_RESULTBIND_H
#define SAPPHIRE_RESULTBIND_H
#include <boost/scoped_array.hpp>
#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

View file

@ -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:

View file

@ -3,7 +3,6 @@
#include <stdint.h>
#include <string>
#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 */

View file

@ -1,4 +1,6 @@
#include "mysql_util.h"
#include "DataType.h"
#include <mysql.h>
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;
}
}

View file

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include <sstream>
#include <mysql.h>
#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

View file

@ -12,6 +12,8 @@
#include <src/servers/Server_Common/Database/Connection.h>
#include <src/servers/Server_Common/Database/Statement.h>
#include <src/servers/Server_Common/Database/ResultSet.h>
#include <src/servers/Server_Common/Database/PreparedStatement.h>
#include <src/servers/Server_Common/Database/PreparedResultSet.h>
#include <src/servers/Server_Common/Network/Connection.h>
#include <src/servers/Server_Common/Network/Hive.h>
@ -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 )