2016-01-28 14:52:40 +01:00
|
|
|
/** @file
|
|
|
|
|
*
|
|
|
|
|
* @par History
|
|
|
|
|
*/
|
2014-08-30 12:25:08 +02:00
|
|
|
|
|
|
|
|
|
2014-11-02 13:50:25 +01:00
|
|
|
#include "sqlscrobj.h"
|
|
|
|
|
|
2018-01-29 21:55:48 +01:00
|
|
|
#include <exception>
|
2020-08-05 20:28:07 +02:00
|
|
|
#include <regex>
|
2018-01-29 21:55:48 +01:00
|
|
|
#include <string.h>
|
2015-10-31 23:37:38 +01:00
|
|
|
|
2014-08-30 12:25:08 +02:00
|
|
|
#include "../bscript/berror.h"
|
2026-03-30 22:50:24 +02:00
|
|
|
#include "../bscript/bobject.h"
|
2025-02-17 21:59:42 +01:00
|
|
|
#include "../bscript/contiter.h"
|
2014-08-30 12:25:08 +02:00
|
|
|
#include "../bscript/impstr.h"
|
|
|
|
|
#include "../bscript/objmembers.h"
|
|
|
|
|
#include "../bscript/objmethods.h"
|
2018-01-29 21:55:48 +01:00
|
|
|
#include "../clib/esignal.h"
|
|
|
|
|
#include "../clib/logfacility.h"
|
|
|
|
|
#include "../clib/threadhelp.h"
|
2015-01-02 14:46:44 +01:00
|
|
|
#include "globals/network.h"
|
2014-11-02 13:50:25 +01:00
|
|
|
|
2026-01-17 10:30:21 +01:00
|
|
|
|
|
|
|
|
namespace Pol::Core
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
using namespace Bscript;
|
|
|
|
|
|
2020-02-08 09:21:35 +01:00
|
|
|
BSQLRow::BSQLRow( BSQLResultSet* resultset ) : PolObjectImp( OTSQLRow )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
_result = resultset->_result;
|
|
|
|
|
_row = mysql_fetch_row( _result->ptr() );
|
|
|
|
|
_fields = mysql_fetch_fields( _result->ptr() );
|
|
|
|
|
}
|
2020-02-08 09:21:35 +01:00
|
|
|
BSQLRow::BSQLRow( RES_WRAPPER result ) : PolObjectImp( OTSQLRow )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
_result = result;
|
|
|
|
|
_row = mysql_fetch_row( _result->ptr() );
|
|
|
|
|
_fields = mysql_fetch_fields( _result->ptr() );
|
|
|
|
|
}
|
|
|
|
|
BSQLRow::BSQLRow( RES_WRAPPER result, MYSQL_ROW row, MYSQL_FIELD* fields )
|
2020-02-08 09:21:35 +01:00
|
|
|
: PolObjectImp( OTSQLRow ), _row( row ), _result( result ), _fields( fields )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
}
|
2025-02-17 21:59:42 +01:00
|
|
|
|
|
|
|
|
class SQLRowIterator final : public Bscript::ContIterator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SQLRowIterator( BSQLRow* node, Bscript::BObject* pIter );
|
2026-01-14 21:35:59 +01:00
|
|
|
Bscript::BObject* step() override;
|
2025-02-17 21:59:42 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Bscript::BObject m_RowObj;
|
|
|
|
|
BSQLRow* rowimp;
|
|
|
|
|
Bscript::BObjectRef m_IterVal;
|
|
|
|
|
unsigned int index;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SQLRowIterator::SQLRowIterator( BSQLRow* row, Bscript::BObject* pIter )
|
|
|
|
|
: ContIterator(), m_RowObj( row ), rowimp( row ), m_IterVal( pIter ), index( 0 )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bscript::BObject* SQLRowIterator::step()
|
|
|
|
|
{
|
|
|
|
|
unsigned int num_fields = mysql_num_fields( rowimp->_result->ptr() );
|
|
|
|
|
const auto& fields = rowimp->_fields;
|
|
|
|
|
const auto& row = rowimp->_row;
|
|
|
|
|
|
|
|
|
|
if ( index >= num_fields )
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
m_IterVal->setimp( new String( fields[index].name ) );
|
|
|
|
|
|
|
|
|
|
if ( IS_NUM( fields[index].type ) && fields[index].type != MYSQL_TYPE_TIMESTAMP )
|
|
|
|
|
{
|
|
|
|
|
if ( fields[index].type == MYSQL_TYPE_DECIMAL || fields[index].type == MYSQL_TYPE_NEWDECIMAL ||
|
|
|
|
|
fields[index].type == MYSQL_TYPE_FLOAT || fields[index].type == MYSQL_TYPE_DOUBLE )
|
|
|
|
|
return new BObject( new Double( strtod( row[index++], nullptr ) ) );
|
|
|
|
|
return new BObject( new BLong( strtoul( row[index++], nullptr, 0 ) ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new BObject( new String( row[index++], String::Tainted::YES ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ContIterator* BSQLRow::createIterator( Bscript::BObject* pIterVal )
|
|
|
|
|
{
|
|
|
|
|
if ( !_result )
|
|
|
|
|
{
|
|
|
|
|
return BObjectImp::createIterator( pIterVal );
|
|
|
|
|
}
|
|
|
|
|
return new SQLRowIterator( this, pIterVal );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
BObjectRef BSQLRow::OperSubscript( const BObject& obj )
|
|
|
|
|
{
|
|
|
|
|
const Bscript::BObjectImp& right = obj.impref();
|
2018-10-09 16:36:35 +02:00
|
|
|
if ( _result == nullptr )
|
2016-02-19 19:26:35 +01:00
|
|
|
return BObjectRef( new BError( "No result" ) );
|
|
|
|
|
unsigned int num_fields = mysql_num_fields( _result->ptr() );
|
|
|
|
|
if ( right.isa( OTLong ) ) // vector
|
|
|
|
|
{
|
|
|
|
|
BLong& lng = (BLong&)right;
|
|
|
|
|
|
|
|
|
|
unsigned index = (unsigned)lng.value();
|
|
|
|
|
if ( index > num_fields || index <= 0 )
|
2014-08-30 12:25:08 +02:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
return BObjectRef( new BError( "Index out of bounds" ) );
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
if ( _row[index - 1] == nullptr )
|
2015-01-02 19:11:57 +01:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
return BObjectRef( UninitObject::create() );
|
2015-01-02 19:11:57 +01:00
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
if ( IS_NUM( _fields[index - 1].type ) && _fields[index - 1].type != MYSQL_TYPE_TIMESTAMP )
|
2015-01-02 19:11:57 +01:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
if ( _fields[index - 1].type == MYSQL_TYPE_DECIMAL ||
|
|
|
|
|
_fields[index - 1].type == MYSQL_TYPE_NEWDECIMAL ||
|
|
|
|
|
_fields[index - 1].type == MYSQL_TYPE_FLOAT ||
|
|
|
|
|
_fields[index - 1].type == MYSQL_TYPE_DOUBLE )
|
2018-07-31 14:30:29 +02:00
|
|
|
return BObjectRef( new Double( strtod( _row[index - 1], nullptr ) ) );
|
|
|
|
|
return BObjectRef( new BLong( strtoul( _row[index - 1], nullptr, 0 ) ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
2019-01-12 17:14:05 +01:00
|
|
|
return BObjectRef( new String( _row[index - 1], String::Tainted::YES ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
if ( right.isa( OTString ) )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
String& string = (String&)right;
|
|
|
|
|
for ( unsigned int i = 0; i < num_fields; i++ )
|
2014-08-30 12:25:08 +02:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
if ( !strncmp( _fields[i].name, string.data(), _fields[i].name_length ) )
|
|
|
|
|
{
|
2018-10-09 16:36:35 +02:00
|
|
|
if ( _row[i] == nullptr )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
return BObjectRef( UninitObject::create() );
|
|
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
if ( IS_NUM( _fields[i].type ) && _fields[i].type != MYSQL_TYPE_TIMESTAMP )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
if ( _fields[i].type == MYSQL_TYPE_DECIMAL || _fields[i].type == MYSQL_TYPE_NEWDECIMAL ||
|
|
|
|
|
_fields[i].type == MYSQL_TYPE_FLOAT || _fields[i].type == MYSQL_TYPE_DOUBLE )
|
2018-07-31 14:30:29 +02:00
|
|
|
return BObjectRef( new Double( strtod( _row[i], nullptr ) ) );
|
|
|
|
|
return BObjectRef( new BLong( strtoul( _row[i], nullptr, 0 ) ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
2019-01-12 17:14:05 +01:00
|
|
|
return BObjectRef( new String( _row[i], String::Tainted::YES ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
2016-02-19 19:26:35 +01:00
|
|
|
return BObjectRef( new BError( "Column does not exist" ) );
|
|
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
|
|
|
|
|
return BObjectRef( new BError( "SQLRow keys must be integer" ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
2026-01-15 22:31:14 +01:00
|
|
|
BSQLRow::~BSQLRow() = default;
|
2016-02-19 19:26:35 +01:00
|
|
|
Bscript::BObjectImp* BSQLRow::copy() const
|
|
|
|
|
{
|
|
|
|
|
return new BSQLRow( _result, _row, _fields );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BSQLResultSet::BSQLResultSet( RES_WRAPPER result )
|
|
|
|
|
: Bscript::BObjectImp( OTSQLResultSet ),
|
|
|
|
|
_result( result ),
|
|
|
|
|
_fields( nullptr ),
|
|
|
|
|
_affected_rows( 0 )
|
|
|
|
|
{
|
|
|
|
|
if ( result->ptr() != nullptr )
|
|
|
|
|
_fields = mysql_fetch_fields( result->ptr() );
|
|
|
|
|
}
|
|
|
|
|
BSQLResultSet::BSQLResultSet( RES_WRAPPER result, MYSQL_FIELD* fields )
|
|
|
|
|
: Bscript::BObjectImp( OTSQLResultSet ),
|
|
|
|
|
_result( result ),
|
|
|
|
|
_fields( fields ),
|
|
|
|
|
_affected_rows( 0 )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
BSQLResultSet::BSQLResultSet( int affected_rows )
|
|
|
|
|
: Bscript::BObjectImp( OTSQLResultSet ),
|
|
|
|
|
_result( nullptr ),
|
|
|
|
|
_fields( nullptr ),
|
|
|
|
|
_affected_rows( affected_rows )
|
|
|
|
|
{
|
|
|
|
|
}
|
2025-02-17 21:59:42 +01:00
|
|
|
|
|
|
|
|
class SQLResultSetIterator final : public Bscript::ContIterator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SQLResultSetIterator( BSQLResultSet* node, Bscript::BObject* pIter );
|
2026-01-14 21:35:59 +01:00
|
|
|
Bscript::BObject* step() override;
|
2025-02-17 21:59:42 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Bscript::BObject m_ResultsObj;
|
|
|
|
|
BSQLResultSet* results;
|
|
|
|
|
Bscript::BObjectRef m_IterVal;
|
|
|
|
|
BLong* m_pIterVal;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SQLResultSetIterator::SQLResultSetIterator( BSQLResultSet* results, Bscript::BObject* pIterVal )
|
|
|
|
|
: ContIterator(),
|
|
|
|
|
m_ResultsObj( results ),
|
|
|
|
|
results( results ),
|
|
|
|
|
m_IterVal( pIterVal ),
|
|
|
|
|
m_pIterVal( new BLong( 0 ) )
|
|
|
|
|
{
|
|
|
|
|
m_IterVal.get()->setimp( m_pIterVal );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bscript::BObject* SQLResultSetIterator::step()
|
|
|
|
|
{
|
2025-02-19 18:33:56 +01:00
|
|
|
if ( static_cast<uint64_t>( m_pIterVal->value() ) >= mysql_num_rows( results->_result->ptr() ) )
|
2025-02-17 21:59:42 +01:00
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
m_pIterVal->increment();
|
|
|
|
|
return new BObject( new BSQLRow( results ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ContIterator* BSQLResultSet::createIterator( Bscript::BObject* pIterVal )
|
|
|
|
|
{
|
|
|
|
|
if ( !_result )
|
|
|
|
|
{
|
|
|
|
|
return BObjectImp::createIterator( pIterVal );
|
|
|
|
|
}
|
|
|
|
|
return new SQLResultSetIterator( this, pIterVal );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
const char* BSQLResultSet::field_name( unsigned int index ) const
|
|
|
|
|
{
|
|
|
|
|
if ( !_result || _result->ptr() == nullptr )
|
2018-10-09 16:36:35 +02:00
|
|
|
return nullptr;
|
2016-02-19 19:26:35 +01:00
|
|
|
if ( index <= 0 || index > mysql_num_fields( _result->ptr() ) )
|
|
|
|
|
{
|
2018-10-09 16:36:35 +02:00
|
|
|
return nullptr;
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
|
|
|
|
return _fields[index - 1].name;
|
|
|
|
|
}
|
|
|
|
|
int BSQLResultSet::num_rows() const
|
|
|
|
|
{
|
|
|
|
|
if ( !_result )
|
|
|
|
|
return 0;
|
|
|
|
|
return static_cast<int>( mysql_num_rows( _result->ptr() ) );
|
|
|
|
|
};
|
2023-05-25 08:13:39 +02:00
|
|
|
|
|
|
|
|
bool BSQLResultSet::has_result() const
|
|
|
|
|
{
|
|
|
|
|
return _result && _result->ptr();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
Bscript::BObjectImp* BSQLResultSet::copy() const
|
|
|
|
|
{
|
|
|
|
|
if ( _affected_rows )
|
|
|
|
|
return new BSQLResultSet( _affected_rows );
|
2026-01-18 09:35:52 +01:00
|
|
|
return new BSQLResultSet( _result, _fields );
|
2016-02-19 19:26:35 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int BSQLResultSet::num_fields() const
|
|
|
|
|
{
|
|
|
|
|
if ( _result && _result->ptr() != nullptr )
|
|
|
|
|
return mysql_num_fields( _result->ptr() );
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
int BSQLResultSet::affected_rows() const
|
|
|
|
|
{
|
|
|
|
|
return _affected_rows;
|
|
|
|
|
}
|
2026-01-15 22:31:14 +01:00
|
|
|
BSQLResultSet::~BSQLResultSet() = default;
|
2016-02-19 19:26:35 +01:00
|
|
|
bool BSQLResultSet::isTrue() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
std::string BSQLResultSet::getStringRep() const
|
|
|
|
|
{
|
|
|
|
|
return "SQLResultSet";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BSQLConnection::close()
|
|
|
|
|
{
|
|
|
|
|
_conn->set( nullptr );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
Bscript::BObjectImp* BSQLConnection::getResultSet() const
|
|
|
|
|
{
|
|
|
|
|
if ( _errno )
|
|
|
|
|
return new BError( _error );
|
|
|
|
|
RES_WRAPPER result = std::make_shared<ResultWrapper>( mysql_store_result( _conn->ptr() ) );
|
|
|
|
|
if ( result && result->ptr() != nullptr ) // there are rows
|
|
|
|
|
{
|
|
|
|
|
return new BSQLResultSet( result );
|
|
|
|
|
// retrieve rows, then call mysql_free_result(result)
|
|
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
// mysql_store_result() returned nothing; should it have?
|
|
|
|
|
/* if (mysql_errno(_conn))
|
2014-08-30 12:25:08 +02:00
|
|
|
{
|
2026-01-18 09:35:52 +01:00
|
|
|
_error = mysql_error(_conn);
|
|
|
|
|
_errno = mysql_errno(_conn);
|
|
|
|
|
return new BError(_error);
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
else */
|
|
|
|
|
if ( mysql_field_count( _conn->ptr() ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
return new BSQLResultSet( static_cast<int>( mysql_affected_rows( _conn->ptr() ) ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
return new BError( "Unknown error getting ResultSet" );
|
|
|
|
|
}
|
|
|
|
|
BSQLConnection::BSQLConnection()
|
2020-02-08 09:21:35 +01:00
|
|
|
: PolObjectImp( OTSQLConnection ), _conn( new ConnectionWrapper ), _errno( 0 )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
2018-07-31 14:30:29 +02:00
|
|
|
_conn->set( mysql_init( nullptr ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
if ( !_conn->ptr() )
|
|
|
|
|
{
|
|
|
|
|
_error = "Insufficient memory";
|
|
|
|
|
_errno = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-08-30 12:25:08 +02:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
BSQLConnection::BSQLConnection( std::shared_ptr<ConnectionWrapper> conn )
|
2020-02-08 09:21:35 +01:00
|
|
|
: PolObjectImp( OTSQLConnection ), _conn( conn ), _errno( 0 )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
}
|
2015-11-01 21:00:06 +01:00
|
|
|
|
2026-01-15 22:31:14 +01:00
|
|
|
BSQLConnection::~BSQLConnection() = default;
|
2016-02-19 19:26:35 +01:00
|
|
|
std::string BSQLConnection::getStringRep() const
|
|
|
|
|
{
|
|
|
|
|
return "SQLConnection";
|
|
|
|
|
}
|
|
|
|
|
bool BSQLConnection::isTrue() const
|
|
|
|
|
{
|
|
|
|
|
if ( !_conn->ptr() )
|
|
|
|
|
return false; // closed by hand
|
|
|
|
|
if ( !mysql_ping( _conn->ptr() ) )
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-03-10 14:31:30 +01:00
|
|
|
bool BSQLConnection::connect( const char* host, const char* user, const char* passwd, int port )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
if ( !_conn->ptr() )
|
|
|
|
|
{
|
|
|
|
|
_errno = -1;
|
|
|
|
|
_error = "No active MYSQL object instance.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-03-10 14:31:30 +01:00
|
|
|
// port == 0 means default sql port
|
|
|
|
|
if ( !mysql_real_connect( _conn->ptr(), host, user, passwd, nullptr, port, nullptr, 0 ) )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
_errno = mysql_errno( _conn->ptr() );
|
|
|
|
|
_error = mysql_error( _conn->ptr() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bool BSQLConnection::select_db( const char* db )
|
|
|
|
|
{
|
|
|
|
|
if ( !_conn->ptr() )
|
|
|
|
|
{
|
|
|
|
|
_errno = -1;
|
|
|
|
|
_error = "No active MYSQL object instance.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-01-18 09:35:52 +01:00
|
|
|
if ( mysql_select_db( _conn->ptr(), db ) )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
_errno = mysql_errno( _conn->ptr() );
|
|
|
|
|
_error = mysql_error( _conn->ptr() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2015-11-01 21:00:06 +01:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
bool BSQLConnection::query( const std::string query )
|
|
|
|
|
{
|
|
|
|
|
if ( !_conn->ptr() )
|
|
|
|
|
{
|
|
|
|
|
_errno = -1;
|
|
|
|
|
_error = "No active MYSQL object instance.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-11-01 21:00:06 +01:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
if ( mysql_query( _conn->ptr(), query.c_str() ) )
|
|
|
|
|
{
|
|
|
|
|
_errno = mysql_errno( _conn->ptr() );
|
|
|
|
|
_error = mysql_error( _conn->ptr() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-11-01 21:00:06 +01:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2015-11-01 21:00:06 +01:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
/*
|
|
|
|
|
* Allows binding parameters to the query
|
|
|
|
|
* Every occurrence of "?" is replaced with a single parameter
|
|
|
|
|
*/
|
|
|
|
|
bool BSQLConnection::query( const std::string query, QueryParams params )
|
|
|
|
|
{
|
2026-01-16 13:22:43 +01:00
|
|
|
if ( params == nullptr || params->empty() )
|
2016-02-19 19:26:35 +01:00
|
|
|
return this->query( query );
|
|
|
|
|
|
|
|
|
|
if ( !_conn->ptr() )
|
|
|
|
|
{
|
|
|
|
|
_errno = -1;
|
|
|
|
|
_error = "No active MYSQL object instance.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-11-01 21:00:06 +01:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
std::string replaced = query;
|
2020-08-05 20:28:07 +02:00
|
|
|
std::regex re( "^((?:[^']|'[^']*')*?)(\\?)" );
|
2026-01-31 09:14:02 +01:00
|
|
|
for ( auto& it : *params )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
2020-08-05 20:28:07 +02:00
|
|
|
if ( !std::regex_search( replaced, re ) )
|
2014-08-30 12:25:08 +02:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
_errno = -2;
|
|
|
|
|
_error = "Could not replace parameters.";
|
|
|
|
|
return false;
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
2016-02-19 19:26:35 +01:00
|
|
|
|
2026-01-31 09:14:02 +01:00
|
|
|
if ( it.size() > ( std::numeric_limits<size_t>::max() - 5 ) / 2 )
|
2014-08-30 12:25:08 +02:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
_errno = -3;
|
|
|
|
|
_error = "Parameter is too long.";
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
// Escape the string and add quoting. A bit tricky, but effective.
|
|
|
|
|
size_t escaped_max_size =
|
2026-01-31 09:14:02 +01:00
|
|
|
it.size() * 2 + 5; // max is +1, using +5 to leave space for quoting and "$1"
|
2016-02-19 19:26:35 +01:00
|
|
|
std::unique_ptr<char[]> escptr(
|
|
|
|
|
new char[escaped_max_size] ); // will contain the escaped string
|
|
|
|
|
// use +3 to leave space for quoting
|
2026-01-31 09:14:02 +01:00
|
|
|
unsigned long esclen = mysql_real_escape_string( _conn->ptr(), escptr.get() + 3, it.c_str(),
|
|
|
|
|
static_cast<unsigned long>( it.size() ) );
|
2016-02-19 19:26:35 +01:00
|
|
|
// Now add quoting, equivalent to escptr = "$1'" + escptr + "'"
|
|
|
|
|
esclen += 4;
|
|
|
|
|
escptr[0] = '$';
|
|
|
|
|
escptr[1] = '1';
|
|
|
|
|
escptr[2] = '\'';
|
|
|
|
|
escptr[esclen - 1] = '\'';
|
|
|
|
|
escptr[esclen] = '\0';
|
|
|
|
|
|
2020-08-05 20:28:07 +02:00
|
|
|
replaced =
|
|
|
|
|
std::regex_replace( replaced, re, escptr.get(), std::regex_constants::format_first_only );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
2014-08-30 12:25:08 +02:00
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
return this->query( replaced );
|
|
|
|
|
}
|
2014-08-30 12:25:08 +02:00
|
|
|
|
2024-03-10 14:31:30 +01:00
|
|
|
bool BSQLConnection::escape_string( const std::string& text, std::string* escaped ) const
|
|
|
|
|
{
|
|
|
|
|
if ( !_conn->ptr() )
|
|
|
|
|
return false;
|
|
|
|
|
*escaped = std::string( text.size() * 2 + 1, '\0' );
|
|
|
|
|
if ( mysql_real_escape_string( _conn->ptr(), escaped->data(), text.data(),
|
|
|
|
|
(unsigned long)text.size() ) == (unsigned long)-1 )
|
|
|
|
|
return false;
|
|
|
|
|
escaped->resize( escaped->find_first_of( '\0' ) );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 19:26:35 +01:00
|
|
|
std::string BSQLConnection::getLastError() const
|
|
|
|
|
{
|
|
|
|
|
return _error;
|
|
|
|
|
}
|
|
|
|
|
int BSQLConnection::getLastErrNo() const
|
|
|
|
|
{
|
|
|
|
|
return _errno;
|
|
|
|
|
}
|
|
|
|
|
std::shared_ptr<BSQLConnection::ConnectionWrapper> BSQLConnection::getConnection() const
|
|
|
|
|
{
|
|
|
|
|
return _conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BObjectRef BSQLConnection::get_member_id( const int /*id*/ ) // id test
|
|
|
|
|
{
|
|
|
|
|
return BObjectRef( UninitObject::create() );
|
|
|
|
|
// switch(id)
|
|
|
|
|
//{
|
|
|
|
|
|
2018-04-03 04:13:03 +02:00
|
|
|
// default: return BObjectRef(UninitObject::create());
|
2016-02-19 19:26:35 +01:00
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
BObjectRef BSQLConnection::get_member( const char* membername )
|
|
|
|
|
{
|
|
|
|
|
ObjMember* objmember = getKnownObjMember( membername );
|
2018-07-31 14:30:29 +02:00
|
|
|
if ( objmember != nullptr )
|
2016-02-19 19:26:35 +01:00
|
|
|
return this->get_member_id( objmember->id );
|
2026-01-18 09:35:52 +01:00
|
|
|
return BObjectRef( UninitObject::create() );
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-08 09:21:35 +01:00
|
|
|
Bscript::BObjectImp* BSQLConnection::call_polmethod( const char* methodname, UOExecutor& ex )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
ObjMethod* objmethod = getKnownObjMethod( methodname );
|
2018-07-31 14:30:29 +02:00
|
|
|
if ( objmethod != nullptr )
|
2020-02-08 09:21:35 +01:00
|
|
|
return this->call_polmethod_id( objmethod->id, ex );
|
2026-01-18 09:35:52 +01:00
|
|
|
return nullptr;
|
2016-02-19 19:26:35 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-08 09:21:35 +01:00
|
|
|
Bscript::BObjectImp* BSQLConnection::call_polmethod_id( const int /*id*/, UOExecutor& /*ex*/,
|
2020-08-03 20:30:27 +02:00
|
|
|
bool /*forcebuiltin*/ )
|
2016-02-19 19:26:35 +01:00
|
|
|
{
|
|
|
|
|
return new BLong( 0 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bscript::BObjectImp* BSQLConnection::copy() const
|
|
|
|
|
{
|
|
|
|
|
return new BSQLConnection( _conn );
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-29 21:55:48 +01:00
|
|
|
BSQLConnection::ConnectionWrapper::ConnectionWrapper() : _conn( nullptr ) {}
|
2016-02-19 19:26:35 +01:00
|
|
|
BSQLConnection::ConnectionWrapper::~ConnectionWrapper()
|
|
|
|
|
{
|
|
|
|
|
if ( _conn )
|
|
|
|
|
mysql_close( _conn );
|
|
|
|
|
_conn = nullptr;
|
|
|
|
|
}
|
|
|
|
|
void BSQLConnection::ConnectionWrapper::set( MYSQL* conn )
|
|
|
|
|
{
|
|
|
|
|
if ( _conn )
|
|
|
|
|
mysql_close( _conn );
|
|
|
|
|
_conn = conn;
|
|
|
|
|
}
|
|
|
|
|
MYSQL* BSQLConnection::ConnectionWrapper::ptr()
|
|
|
|
|
{
|
|
|
|
|
return _conn;
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-29 21:55:48 +01:00
|
|
|
ResultWrapper::ResultWrapper( MYSQL_RES* res ) : _result( res ) {}
|
|
|
|
|
ResultWrapper::ResultWrapper() : _result( nullptr ) {}
|
2016-02-19 19:26:35 +01:00
|
|
|
ResultWrapper::~ResultWrapper()
|
|
|
|
|
{
|
|
|
|
|
if ( _result )
|
|
|
|
|
mysql_free_result( _result );
|
|
|
|
|
_result = nullptr;
|
|
|
|
|
}
|
|
|
|
|
void ResultWrapper::set( MYSQL_RES* result )
|
|
|
|
|
{
|
|
|
|
|
if ( _result )
|
|
|
|
|
mysql_free_result( _result );
|
|
|
|
|
_result = result;
|
|
|
|
|
}
|
|
|
|
|
MYSQL_RES* ResultWrapper::ptr()
|
|
|
|
|
{
|
|
|
|
|
return _result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void sql_service_thread_stub()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
networkManager.sql_service->start();
|
|
|
|
|
}
|
|
|
|
|
catch ( const char* msg )
|
|
|
|
|
{
|
2024-01-16 17:55:42 +01:00
|
|
|
POLLOGLN( "SQL Thread exits due to exception: {}", msg );
|
2016-02-19 19:26:35 +01:00
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
catch ( std::string& str )
|
|
|
|
|
{
|
2024-01-16 17:55:42 +01:00
|
|
|
POLLOGLN( "SQL Thread exits due to exception: {}", str );
|
2016-02-19 19:26:35 +01:00
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
catch ( std::exception& ex )
|
|
|
|
|
{
|
2024-01-16 17:55:42 +01:00
|
|
|
POLLOGLN( "SQL Thread exits due to exception: {}", ex.what() );
|
2016-02-19 19:26:35 +01:00
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 22:31:14 +01:00
|
|
|
SQLService::SQLService() = default;
|
|
|
|
|
SQLService::~SQLService() = default;
|
2016-02-19 19:26:35 +01:00
|
|
|
void SQLService::stop()
|
|
|
|
|
{
|
|
|
|
|
_msgs.cancel();
|
|
|
|
|
}
|
|
|
|
|
void SQLService::push( msg&& msg_ )
|
|
|
|
|
{
|
|
|
|
|
_msgs.push_move( std::move( msg_ ) );
|
|
|
|
|
}
|
|
|
|
|
void SQLService::start() // executed inside a extra thread
|
|
|
|
|
{
|
|
|
|
|
while ( !Clib::exit_signalled )
|
|
|
|
|
{
|
|
|
|
|
try
|
2014-08-30 12:25:08 +02:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
msg task;
|
|
|
|
|
_msgs.pop_wait( &task );
|
|
|
|
|
task();
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
2016-02-19 19:26:35 +01:00
|
|
|
catch ( msg_queue::Canceled& )
|
2014-08-30 12:25:08 +02:00
|
|
|
{
|
2016-02-19 19:26:35 +01:00
|
|
|
break;
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
2016-02-19 19:26:35 +01:00
|
|
|
// ignore remaining tasks
|
2014-08-30 12:25:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
2016-02-19 19:26:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
void start_sql_service()
|
|
|
|
|
{
|
|
|
|
|
threadhelp::start_thread( sql_service_thread_stub, "SQLService" );
|
|
|
|
|
}
|
2026-01-17 10:30:21 +01:00
|
|
|
} // namespace Pol::Core
|