Pol  Revision:cb584c9
sqlscrobj.cpp
Go to the documentation of this file.
1 
7 #include "sqlscrobj.h"
8 
9 #include <exception>
10 #include <string.h>
11 
12 #include "../bscript/bobject.h"
13 
14 
15 #ifdef HAVE_MYSQL
16 
17 #include "../bscript/berror.h"
18 #include "../bscript/impstr.h"
19 #include "../bscript/objmembers.h"
20 #include "../bscript/objmethods.h"
21 #include "../clib/esignal.h"
22 #include "../clib/logfacility.h"
23 #include "../clib/threadhelp.h"
24 #include "globals/network.h"
25 
26 // std::regex support is broken in GCC < 4.9. This define is a workaround for GCC 4.8.
27 // TODO: remove this in future and just use std:: namespace
28 #ifndef USE_BOOST_REGEX
29 #include <regex>
30 
31 #define REGEX_NSPACE std
32 #else
33 #include <boost/regex.hpp>
34 
35 #define REGEX_NSPACE boost
36 #endif
37 
38 namespace Pol
39 {
40 namespace Core
41 {
42 using namespace Bscript;
43 
44 BSQLRow::BSQLRow( BSQLResultSet* resultset ) : Bscript::BObjectImp( OTSQLRow )
45 {
46  _result = resultset->_result;
47  _row = mysql_fetch_row( _result->ptr() );
48  _fields = mysql_fetch_fields( _result->ptr() );
49 }
51 {
52  _result = result;
53  _row = mysql_fetch_row( _result->ptr() );
54  _fields = mysql_fetch_fields( _result->ptr() );
55 }
56 BSQLRow::BSQLRow( RES_WRAPPER result, MYSQL_ROW row, MYSQL_FIELD* fields )
57  : Bscript::BObjectImp( OTSQLRow ), _row( row ), _result( result ), _fields( fields )
58 {
59 }
61 {
62  const Bscript::BObjectImp& right = obj.impref();
63  if ( _result == 0 )
64  return BObjectRef( new BError( "No result" ) );
65  unsigned int num_fields = mysql_num_fields( _result->ptr() );
66  if ( right.isa( OTLong ) ) // vector
67  {
68  BLong& lng = (BLong&)right;
69 
70  unsigned index = (unsigned)lng.value();
71  if ( index > num_fields || index <= 0 )
72  {
73  return BObjectRef( new BError( "Index out of bounds" ) );
74  }
75  else if ( _row[index - 1] == 0 )
76  {
77  return BObjectRef( UninitObject::create() );
78  }
79  else if ( IS_NUM( _fields[index - 1].type ) && _fields[index - 1].type != MYSQL_TYPE_TIMESTAMP )
80  {
81  if ( _fields[index - 1].type == MYSQL_TYPE_DECIMAL ||
82  _fields[index - 1].type == MYSQL_TYPE_NEWDECIMAL ||
83  _fields[index - 1].type == MYSQL_TYPE_FLOAT ||
84  _fields[index - 1].type == MYSQL_TYPE_DOUBLE )
85  return BObjectRef( new Double( strtod( _row[index - 1], nullptr ) ) );
86  return BObjectRef( new BLong( strtoul( _row[index - 1], nullptr, 0 ) ) );
87  }
88  return BObjectRef( new String( _row[index - 1] ) );
89  }
90  else if ( right.isa( OTString ) )
91  {
92  String& string = (String&)right;
93  for ( unsigned int i = 0; i < num_fields; i++ )
94  {
95  if ( !strncmp( _fields[i].name, string.data(), _fields[i].name_length ) )
96  {
97  if ( _row[i] == 0 )
98  {
99  return BObjectRef( UninitObject::create() );
100  }
101  else if ( IS_NUM( _fields[i].type ) && _fields[i].type != MYSQL_TYPE_TIMESTAMP )
102  {
103  if ( _fields[i].type == MYSQL_TYPE_DECIMAL || _fields[i].type == MYSQL_TYPE_NEWDECIMAL ||
104  _fields[i].type == MYSQL_TYPE_FLOAT || _fields[i].type == MYSQL_TYPE_DOUBLE )
105  return BObjectRef( new Double( strtod( _row[i], nullptr ) ) );
106  return BObjectRef( new BLong( strtoul( _row[i], nullptr, 0 ) ) );
107  }
108  return BObjectRef( new String( _row[i] ) );
109  }
110  }
111  return BObjectRef( new BError( "Column does not exist" ) );
112  }
113  else
114  {
115  return BObjectRef( new BError( "SQLRow keys must be integer" ) );
116  }
117 }
120 {
121  return new BSQLRow( _result, _row, _fields );
122 }
123 
125  : Bscript::BObjectImp( OTSQLResultSet ),
126  _result( result ),
127  _fields( nullptr ),
128  _affected_rows( 0 )
129 {
130  if ( result->ptr() != nullptr )
131  _fields = mysql_fetch_fields( result->ptr() );
132 }
133 BSQLResultSet::BSQLResultSet( RES_WRAPPER result, MYSQL_FIELD* fields )
134  : Bscript::BObjectImp( OTSQLResultSet ),
135  _result( result ),
136  _fields( fields ),
137  _affected_rows( 0 )
138 {
139 }
141  : Bscript::BObjectImp( OTSQLResultSet ),
142  _result( nullptr ),
143  _fields( nullptr ),
144  _affected_rows( affected_rows )
145 {
146 }
147 const char* BSQLResultSet::field_name( unsigned int index ) const
148 {
149  if ( !_result || _result->ptr() == nullptr )
150  return 0;
151  if ( index <= 0 || index > mysql_num_fields( _result->ptr() ) )
152  {
153  return 0;
154  }
155  return _fields[index - 1].name;
156 }
158 {
159  if ( !_result )
160  return 0;
161  return static_cast<int>( mysql_num_rows( _result->ptr() ) );
162 };
164 {
165  if ( _affected_rows )
166  return new BSQLResultSet( _affected_rows );
167  else
168  return new BSQLResultSet( _result, _fields );
169 };
170 
172 {
173  if ( _result && _result->ptr() != nullptr )
174  return mysql_num_fields( _result->ptr() );
175  return 0;
176 }
178 {
179  return _affected_rows;
180 }
183 {
184  return true;
185 }
186 std::string BSQLResultSet::getStringRep() const
187 {
188  return "SQLResultSet";
189 }
190 
192 {
193  _conn->set( nullptr );
194  return true;
195 }
197 {
198  if ( _errno )
199  return new BError( _error );
200  RES_WRAPPER result = std::make_shared<ResultWrapper>( mysql_store_result( _conn->ptr() ) );
201  if ( result && result->ptr() != nullptr ) // there are rows
202  {
203  return new BSQLResultSet( result );
204  // retrieve rows, then call mysql_free_result(result)
205  }
206  else // mysql_store_result() returned nothing; should it have?
207  {
208  /* if (mysql_errno(_conn))
209  {
210  _error = mysql_error(_conn);
211  _errno = mysql_errno(_conn);
212  return new BError(_error);
213  }
214  else */
215  if ( mysql_field_count( _conn->ptr() ) == 0 )
216  {
217  return new BSQLResultSet( static_cast<int>( mysql_affected_rows( _conn->ptr() ) ) );
218  }
219  }
220  return new BError( "Unknown error getting ResultSet" );
221 }
223  : Bscript::BObjectImp( OTSQLConnection ), _conn( new ConnectionWrapper ), _errno( 0 )
224 {
225  _conn->set( mysql_init( nullptr ) );
226  if ( !_conn->ptr() )
227  {
228  _error = "Insufficient memory";
229  _errno = 1;
230  }
231 }
232 
233 BSQLConnection::BSQLConnection( std::shared_ptr<ConnectionWrapper> conn )
234  : Bscript::BObjectImp( OTSQLConnection ), _conn( conn ), _errno( 0 )
235 {
236 }
237 
239 std::string BSQLConnection::getStringRep() const
240 {
241  return "SQLConnection";
242 }
244 {
245  if ( !_conn->ptr() )
246  return false; // closed by hand
247  if ( !mysql_ping( _conn->ptr() ) )
248  return true;
249  return false;
250 }
251 bool BSQLConnection::connect( const char* host, const char* user, const char* passwd )
252 {
253  if ( !_conn->ptr() )
254  {
255  _errno = -1;
256  _error = "No active MYSQL object instance.";
257  return false;
258  }
259  if ( !mysql_real_connect( _conn->ptr(), host, user, passwd, nullptr, 0, nullptr, 0 ) )
260  {
261  _errno = mysql_errno( _conn->ptr() );
262  _error = mysql_error( _conn->ptr() );
263  return false;
264  }
265  return true;
266 }
267 bool BSQLConnection::select_db( const char* db )
268 {
269  if ( !_conn->ptr() )
270  {
271  _errno = -1;
272  _error = "No active MYSQL object instance.";
273  return false;
274  }
275  else if ( mysql_select_db( _conn->ptr(), db ) )
276  {
277  _errno = mysql_errno( _conn->ptr() );
278  _error = mysql_error( _conn->ptr() );
279  return false;
280  }
281  return true;
282 }
283 
284 bool BSQLConnection::query( const std::string query )
285 {
286  if ( !_conn->ptr() )
287  {
288  _errno = -1;
289  _error = "No active MYSQL object instance.";
290  return false;
291  }
292 
293  if ( mysql_query( _conn->ptr(), query.c_str() ) )
294  {
295  _errno = mysql_errno( _conn->ptr() );
296  _error = mysql_error( _conn->ptr() );
297  return false;
298  }
299 
300  return true;
301 }
302 
303 /*
304  * Allows binding parameters to the query
305  * Every occurrence of "?" is replaced with a single parameter
306  */
307 bool BSQLConnection::query( const std::string query, QueryParams params )
308 {
309  if ( params == nullptr || !params->size() )
310  return this->query( query );
311 
312  if ( !_conn->ptr() )
313  {
314  _errno = -1;
315  _error = "No active MYSQL object instance.";
316  return false;
317  }
318 
319  std::string replaced = query;
320  REGEX_NSPACE::regex re( "^((?:[^']|'[^']*')*?)(\\?)" );
321  for ( auto it = params->begin(); it != params->end(); ++it )
322  {
323  if ( !REGEX_NSPACE::regex_search( replaced, re ) )
324  {
325  _errno = -2;
326  _error = "Could not replace parameters.";
327  return false;
328  }
329 
330  if ( it->size() > ( std::numeric_limits<size_t>::max() - 5 ) / 2 )
331  {
332  _errno = -3;
333  _error = "Parameter is too long.";
334  }
335 
336  // Escape the string and add quoting. A bit tricky, but effective.
337  size_t escaped_max_size =
338  it->size() * 2 + 5; // max is +1, using +5 to leave space for quoting and "$1"
339  std::unique_ptr<char[]> escptr(
340  new char[escaped_max_size] ); // will contain the escaped string
341  // use +3 to leave space for quoting
342  unsigned long esclen = mysql_real_escape_string( _conn->ptr(), escptr.get() + 3, it->c_str(),
343  static_cast<unsigned long>( it->size() ) );
344  // Now add quoting, equivalent to escptr = "$1'" + escptr + "'"
345  esclen += 4;
346  escptr[0] = '$';
347  escptr[1] = '1';
348  escptr[2] = '\'';
349  escptr[esclen - 1] = '\'';
350  escptr[esclen] = '\0';
351 
352  replaced = REGEX_NSPACE::regex_replace( replaced, re, escptr.get(),
353  REGEX_NSPACE::regex_constants::format_first_only );
354  }
355 
356  return this->query( replaced );
357 }
358 
359 std::string BSQLConnection::getLastError() const
360 {
361  return _error;
362 }
364 {
365  return _errno;
366 }
367 std::shared_ptr<BSQLConnection::ConnectionWrapper> BSQLConnection::getConnection() const
368 {
369  return _conn;
370 }
371 
372 
373 BObjectRef BSQLConnection::get_member_id( const int /*id*/ ) // id test
374 {
375  return BObjectRef( UninitObject::create() );
376  // switch(id)
377  //{
378 
379  // default: return BObjectRef(UninitObject::create());
380  //}
381 }
382 BObjectRef BSQLConnection::get_member( const char* membername )
383 {
384  ObjMember* objmember = getKnownObjMember( membername );
385  if ( objmember != nullptr )
386  return this->get_member_id( objmember->id );
387  else
388  return BObjectRef( UninitObject::create() );
389 }
390 
392 {
393  ObjMethod* objmethod = getKnownObjMethod( methodname );
394  if ( objmethod != nullptr )
395  return this->call_method_id( objmethod->id, ex );
396  else
397  return nullptr;
398 }
399 
401  bool /*forcebuiltin*/ )
402 {
403  return new BLong( 0 );
404 }
405 
407 {
408  return new BSQLConnection( _conn );
409 }
410 
413 {
414  if ( _conn )
415  mysql_close( _conn );
416  _conn = nullptr;
417 }
419 {
420  if ( _conn )
421  mysql_close( _conn );
422  _conn = conn;
423 }
425 {
426  return _conn;
427 };
428 
429 ResultWrapper::ResultWrapper( MYSQL_RES* res ) : _result( res ) {}
432 {
433  if ( _result )
434  mysql_free_result( _result );
435  _result = nullptr;
436 }
437 void ResultWrapper::set( MYSQL_RES* result )
438 {
439  if ( _result )
440  mysql_free_result( _result );
441  _result = result;
442 }
443 MYSQL_RES* ResultWrapper::ptr()
444 {
445  return _result;
446 }
447 
448 
450 {
451  try
452  {
453  networkManager.sql_service->start();
454  }
455  catch ( const char* msg )
456  {
457  POLLOG.Format( "SQL Thread exits due to exception: {}\n" ) << msg;
458  throw;
459  }
460  catch ( std::string& str )
461  {
462  POLLOG.Format( "SQL Thread exits due to exception: {}\n" ) << str;
463  throw;
464  }
465  catch ( std::exception& ex )
466  {
467  POLLOG.Format( "SQL Thread exits due to exception: {}\n" ) << ex.what();
468  throw;
469  }
470 }
471 
475 {
476  _msgs.cancel();
477 }
478 void SQLService::push( msg&& msg_ )
479 {
480  _msgs.push_move( std::move( msg_ ) );
481 }
482 void SQLService::start() // executed inside a extra thread
483 {
484  while ( !Clib::exit_signalled )
485  {
486  try
487  {
488  msg task;
489  _msgs.pop_wait( &task );
490  task();
491  }
492  catch ( msg_queue::Canceled& )
493  {
494  break;
495  }
496  // ignore remaining tasks
497  }
498 }
499 
500 
502 {
504 }
505 }
506 }
507 #undef REGEX_NSPACE
508 #endif
virtual bool isTrue() const POL_OVERRIDE
Definition: sqlscrobj.cpp:243
std::shared_ptr< ConnectionWrapper > getConnection() const
Definition: sqlscrobj.cpp:367
const char * field_name(unsigned int index) const
Definition: sqlscrobj.cpp:147
BObjectType type() const
Definition: bobject.h:358
int value() const
Definition: bobject.h:592
virtual Bscript::BObjectImp * copy() const POL_OVERRIDE
Definition: sqlscrobj.cpp:163
virtual Bscript::BObjectRef get_member(const char *membername) POL_OVERRIDE
Definition: sqlscrobj.cpp:382
bool connect(const char *host, const char *user, const char *passwd)
Definition: sqlscrobj.cpp:251
bool isa(BObjectType type) const
Definition: bobject.h:353
virtual std::string getStringRep() const POL_OVERRIDE
Definition: sqlscrobj.cpp:239
void set(MYSQL_RES *result)
Definition: sqlscrobj.cpp:437
ObjMember * getKnownObjMember(const char *token)
Definition: parser.cpp:483
virtual Bscript::BObjectRef OperSubscript(const Bscript::BObject &obj) POL_OVERRIDE
Definition: sqlscrobj.cpp:60
MYSQL_FIELD * _fields
Definition: sqlscrobj.h:114
void sql_service_thread_stub()
Definition: sqlscrobj.cpp:449
int affected_rows() const
Definition: sqlscrobj.cpp:177
Bscript::BObjectImp * getResultSet() const
Definition: sqlscrobj.cpp:196
void start_thread(void(*entry)(void *), const char *thread_name, void *arg)
Definition: threadhelp.cpp:233
std::unique_ptr< SQLService > sql_service
Definition: network.h:61
ObjMethod * getKnownObjMethod(const char *token)
Definition: parser.cpp:666
BSQLResultSet(RES_WRAPPER result)
Definition: sqlscrobj.cpp:124
virtual Bscript::BObjectImp * copy() const POL_OVERRIDE
Definition: sqlscrobj.cpp:119
virtual Bscript::BObjectRef get_member_id(const int id) POL_OVERRIDE
Definition: sqlscrobj.cpp:373
virtual Bscript::BObjectImp * copy() const POL_OVERRIDE
Definition: sqlscrobj.cpp:406
bool select_db(const char *db)
Definition: sqlscrobj.cpp:267
virtual Bscript::BObjectImp * call_method_id(const int id, Bscript::Executor &ex, bool forcebuiltin=false) POL_OVERRIDE
Definition: sqlscrobj.cpp:400
bool query(const std::string query)
Definition: sqlscrobj.cpp:284
NetworkManager networkManager
Definition: network.cpp:28
std::shared_ptr< QueryParam > QueryParams
Definition: sqlscrobj.h:120
MYSQL_FIELD * _fields
Definition: sqlscrobj.h:85
static UninitObject * create()
Definition: bobject.h:482
void start_sql_service()
Definition: sqlscrobj.cpp:501
virtual Bscript::BObjectImp * call_method(const char *methodname, Bscript::Executor &ex) POL_OVERRIDE
Definition: sqlscrobj.cpp:391
#define POLLOG
Definition: logfacility.h:219
std::function< void()> msg
Definition: sqlscrobj.h:176
std::unordered_map< u64, ScriptDiffData > data
Definition: osmod.cpp:966
BObjectImp & impref()
Definition: bobject.h:438
MYSQL_ROW _row
Definition: sqlscrobj.h:83
std::string name
Definition: osmod.cpp:943
std::shared_ptr< ResultWrapper > RES_WRAPPER
Definition: sqlscrobj.h:55
virtual bool isTrue() const POL_OVERRIDE
Definition: sqlscrobj.cpp:182
BSQLRow(RES_WRAPPER resultset)
Definition: sqlscrobj.cpp:50
virtual std::string getStringRep() const POL_OVERRIDE
Definition: sqlscrobj.cpp:186
std::shared_ptr< ConnectionWrapper > _conn
Definition: sqlscrobj.h:156
Definition: berror.cpp:12
RES_WRAPPER _result
Definition: sqlscrobj.h:84
std::atomic< bool > exit_signalled
void push(msg &&msg_)
Definition: sqlscrobj.cpp:478
std::string getLastError() const
Definition: sqlscrobj.cpp:359