• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 21100551564

17 Jan 2026 08:40PM UTC coverage: 60.504% (+0.01%) from 60.492%
21100551564

Pull #857

github

turleypol
fixed scope
Pull Request #857: ClangTidy readability-else-after-return

837 of 1874 new or added lines in 151 files covered. (44.66%)

48 existing lines in 26 files now uncovered.

44445 of 73458 relevant lines covered (60.5%)

515341.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.52
/pol-core/pol/sqlscrobj.cpp
1
/** @file
2
 *
3
 * @par History
4
 */
5

6

7
#include "sqlscrobj.h"
8

9
#include <exception>
10
#include <regex>
11
#include <string.h>
12

13
#include "../bscript/bobject.h"
14

15

16
#ifdef HAVE_MYSQL
17

18
#include "../bscript/berror.h"
19
#include "../bscript/contiter.h"
20
#include "../bscript/impstr.h"
21
#include "../bscript/objmembers.h"
22
#include "../bscript/objmethods.h"
23
#include "../clib/esignal.h"
24
#include "../clib/logfacility.h"
25
#include "../clib/threadhelp.h"
26
#include "globals/network.h"
27

28

29
namespace Pol::Core
30
{
31
using namespace Bscript;
32

33
BSQLRow::BSQLRow( BSQLResultSet* resultset ) : PolObjectImp( OTSQLRow )
10✔
34
{
35
  _result = resultset->_result;
10✔
36
  _row = mysql_fetch_row( _result->ptr() );
10✔
37
  _fields = mysql_fetch_fields( _result->ptr() );
10✔
38
}
10✔
39
BSQLRow::BSQLRow( RES_WRAPPER result ) : PolObjectImp( OTSQLRow )
×
40
{
41
  _result = result;
×
42
  _row = mysql_fetch_row( _result->ptr() );
×
43
  _fields = mysql_fetch_fields( _result->ptr() );
×
44
}
×
45
BSQLRow::BSQLRow( RES_WRAPPER result, MYSQL_ROW row, MYSQL_FIELD* fields )
×
46
    : PolObjectImp( OTSQLRow ), _row( row ), _result( result ), _fields( fields )
×
47
{
48
}
×
49

50
class SQLRowIterator final : public Bscript::ContIterator
51
{
52
public:
53
  SQLRowIterator( BSQLRow* node, Bscript::BObject* pIter );
54
  Bscript::BObject* step() override;
55

56
private:
57
  Bscript::BObject m_RowObj;
58
  BSQLRow* rowimp;
59
  Bscript::BObjectRef m_IterVal;
60
  unsigned int index;
61
};
62

63
SQLRowIterator::SQLRowIterator( BSQLRow* row, Bscript::BObject* pIter )
4✔
64
    : ContIterator(), m_RowObj( row ), rowimp( row ), m_IterVal( pIter ), index( 0 )
4✔
65
{
66
}
4✔
67

68
Bscript::BObject* SQLRowIterator::step()
10✔
69
{
70
  unsigned int num_fields = mysql_num_fields( rowimp->_result->ptr() );
10✔
71
  const auto& fields = rowimp->_fields;
10✔
72
  const auto& row = rowimp->_row;
10✔
73

74
  if ( index >= num_fields )
10✔
75
    return nullptr;
2✔
76

77
  m_IterVal->setimp( new String( fields[index].name ) );
8✔
78

79
  if ( IS_NUM( fields[index].type ) && fields[index].type != MYSQL_TYPE_TIMESTAMP )
8✔
80
  {
81
    if ( fields[index].type == MYSQL_TYPE_DECIMAL || fields[index].type == MYSQL_TYPE_NEWDECIMAL ||
4✔
82
         fields[index].type == MYSQL_TYPE_FLOAT || fields[index].type == MYSQL_TYPE_DOUBLE )
4✔
83
      return new BObject( new Double( strtod( row[index++], nullptr ) ) );
×
84
    return new BObject( new BLong( strtoul( row[index++], nullptr, 0 ) ) );
4✔
85
  }
86

87
  return new BObject( new String( row[index++], String::Tainted::YES ) );
4✔
88
}
89

90
ContIterator* BSQLRow::createIterator( Bscript::BObject* pIterVal )
4✔
91
{
92
  if ( !_result )
8✔
93
  {
94
    return BObjectImp::createIterator( pIterVal );
×
95
  }
96
  return new SQLRowIterator( this, pIterVal );
4✔
97
}
98

99
BObjectRef BSQLRow::OperSubscript( const BObject& obj )
6✔
100
{
101
  const Bscript::BObjectImp& right = obj.impref();
6✔
102
  if ( _result == nullptr )
6✔
103
    return BObjectRef( new BError( "No result" ) );
×
104
  unsigned int num_fields = mysql_num_fields( _result->ptr() );
6✔
105
  if ( right.isa( OTLong ) )  // vector
6✔
106
  {
107
    BLong& lng = (BLong&)right;
6✔
108

109
    unsigned index = (unsigned)lng.value();
6✔
110
    if ( index > num_fields || index <= 0 )
6✔
111
    {
112
      return BObjectRef( new BError( "Index out of bounds" ) );
×
113
    }
114
    if ( _row[index - 1] == nullptr )
6✔
115
    {
116
      return BObjectRef( UninitObject::create() );
×
117
    }
118
    if ( IS_NUM( _fields[index - 1].type ) && _fields[index - 1].type != MYSQL_TYPE_TIMESTAMP )
6✔
119
    {
120
      if ( _fields[index - 1].type == MYSQL_TYPE_DECIMAL ||
×
121
           _fields[index - 1].type == MYSQL_TYPE_NEWDECIMAL ||
×
122
           _fields[index - 1].type == MYSQL_TYPE_FLOAT ||
×
123
           _fields[index - 1].type == MYSQL_TYPE_DOUBLE )
×
124
        return BObjectRef( new Double( strtod( _row[index - 1], nullptr ) ) );
×
125
      return BObjectRef( new BLong( strtoul( _row[index - 1], nullptr, 0 ) ) );
×
126
    }
127
    return BObjectRef( new String( _row[index - 1], String::Tainted::YES ) );
6✔
128
  }
NEW
129
  if ( right.isa( OTString ) )
×
130
  {
131
    String& string = (String&)right;
×
132
    for ( unsigned int i = 0; i < num_fields; i++ )
×
133
    {
134
      if ( !strncmp( _fields[i].name, string.data(), _fields[i].name_length ) )
×
135
      {
136
        if ( _row[i] == nullptr )
×
137
        {
138
          return BObjectRef( UninitObject::create() );
×
139
        }
NEW
140
        if ( IS_NUM( _fields[i].type ) && _fields[i].type != MYSQL_TYPE_TIMESTAMP )
×
141
        {
142
          if ( _fields[i].type == MYSQL_TYPE_DECIMAL || _fields[i].type == MYSQL_TYPE_NEWDECIMAL ||
×
143
               _fields[i].type == MYSQL_TYPE_FLOAT || _fields[i].type == MYSQL_TYPE_DOUBLE )
×
144
            return BObjectRef( new Double( strtod( _row[i], nullptr ) ) );
×
145
          return BObjectRef( new BLong( strtoul( _row[i], nullptr, 0 ) ) );
×
146
        }
147
        return BObjectRef( new String( _row[i], String::Tainted::YES ) );
×
148
      }
149
    }
150
    return BObjectRef( new BError( "Column does not exist" ) );
×
151
  }
152

NEW
153
  return BObjectRef( new BError( "SQLRow keys must be integer" ) );
×
154
}
155
BSQLRow::~BSQLRow() = default;
20✔
156
Bscript::BObjectImp* BSQLRow::copy() const
×
157
{
158
  return new BSQLRow( _result, _row, _fields );
×
159
}
160

161
BSQLResultSet::BSQLResultSet( RES_WRAPPER result )
4✔
162
    : Bscript::BObjectImp( OTSQLResultSet ),
163
      _result( result ),
4✔
164
      _fields( nullptr ),
4✔
165
      _affected_rows( 0 )
4✔
166
{
167
  if ( result->ptr() != nullptr )
4✔
168
    _fields = mysql_fetch_fields( result->ptr() );
4✔
169
}
4✔
170
BSQLResultSet::BSQLResultSet( RES_WRAPPER result, MYSQL_FIELD* fields )
×
171
    : Bscript::BObjectImp( OTSQLResultSet ),
172
      _result( result ),
×
173
      _fields( fields ),
×
174
      _affected_rows( 0 )
×
175
{
176
}
×
177
BSQLResultSet::BSQLResultSet( int affected_rows )
4✔
178
    : Bscript::BObjectImp( OTSQLResultSet ),
179
      _result( nullptr ),
4✔
180
      _fields( nullptr ),
4✔
181
      _affected_rows( affected_rows )
4✔
182
{
183
}
4✔
184

185
class SQLResultSetIterator final : public Bscript::ContIterator
186
{
187
public:
188
  SQLResultSetIterator( BSQLResultSet* node, Bscript::BObject* pIter );
189
  Bscript::BObject* step() override;
190

191
private:
192
  Bscript::BObject m_ResultsObj;
193
  BSQLResultSet* results;
194
  Bscript::BObjectRef m_IterVal;
195
  BLong* m_pIterVal;
196
};
197

198
SQLResultSetIterator::SQLResultSetIterator( BSQLResultSet* results, Bscript::BObject* pIterVal )
2✔
199
    : ContIterator(),
200
      m_ResultsObj( results ),
2✔
201
      results( results ),
2✔
202
      m_IterVal( pIterVal ),
2✔
203
      m_pIterVal( new BLong( 0 ) )
4✔
204
{
205
  m_IterVal.get()->setimp( m_pIterVal );
2✔
206
}
2✔
207

208
Bscript::BObject* SQLResultSetIterator::step()
6✔
209
{
210
  if ( static_cast<uint64_t>( m_pIterVal->value() ) >= mysql_num_rows( results->_result->ptr() ) )
6✔
211
    return nullptr;
2✔
212

213
  m_pIterVal->increment();
4✔
214
  return new BObject( new BSQLRow( results ) );
4✔
215
}
216

217
ContIterator* BSQLResultSet::createIterator( Bscript::BObject* pIterVal )
2✔
218
{
219
  if ( !_result )
4✔
220
  {
221
    return BObjectImp::createIterator( pIterVal );
×
222
  }
223
  return new SQLResultSetIterator( this, pIterVal );
2✔
224
}
225

226
const char* BSQLResultSet::field_name( unsigned int index ) const
×
227
{
228
  if ( !_result || _result->ptr() == nullptr )
×
229
    return nullptr;
×
230
  if ( index <= 0 || index > mysql_num_fields( _result->ptr() ) )
×
231
  {
232
    return nullptr;
×
233
  }
234
  return _fields[index - 1].name;
×
235
}
236
int BSQLResultSet::num_rows() const
6✔
237
{
238
  if ( !_result )
12✔
239
    return 0;
×
240
  return static_cast<int>( mysql_num_rows( _result->ptr() ) );
6✔
241
};
242

243
bool BSQLResultSet::has_result() const
6✔
244
{
245
  return _result && _result->ptr();
12✔
246
}
247

248
Bscript::BObjectImp* BSQLResultSet::copy() const
×
249
{
250
  if ( _affected_rows )
×
251
    return new BSQLResultSet( _affected_rows );
×
NEW
252
  return new BSQLResultSet( _result, _fields );
×
253
};
254

255
int BSQLResultSet::num_fields() const
×
256
{
257
  if ( _result && _result->ptr() != nullptr )
×
258
    return mysql_num_fields( _result->ptr() );
×
259
  return 0;
×
260
}
261
int BSQLResultSet::affected_rows() const
×
262
{
263
  return _affected_rows;
×
264
}
265
BSQLResultSet::~BSQLResultSet() = default;
16✔
266
bool BSQLResultSet::isTrue() const
4✔
267
{
268
  return true;
4✔
269
}
270
std::string BSQLResultSet::getStringRep() const
×
271
{
272
  return "SQLResultSet";
×
273
}
274

275
bool BSQLConnection::close()
×
276
{
277
  _conn->set( nullptr );
×
278
  return true;
×
279
}
280
Bscript::BObjectImp* BSQLConnection::getResultSet() const
8✔
281
{
282
  if ( _errno )
8✔
283
    return new BError( _error );
×
284
  RES_WRAPPER result = std::make_shared<ResultWrapper>( mysql_store_result( _conn->ptr() ) );
8✔
285
  if ( result && result->ptr() != nullptr )  // there are rows
8✔
286
  {
287
    return new BSQLResultSet( result );
4✔
288
    // retrieve rows, then call mysql_free_result(result)
289
  }
290
  // mysql_store_result() returned nothing; should it have?
291
  /*  if (mysql_errno(_conn))
292
    {
293
      _error = mysql_error(_conn);
294
      _errno = mysql_errno(_conn);
295
      return new BError(_error);
296
    }
297
    else */
298
  if ( mysql_field_count( _conn->ptr() ) == 0 )
4✔
299
  {
300
    return new BSQLResultSet( static_cast<int>( mysql_affected_rows( _conn->ptr() ) ) );
4✔
301
  }
302

UNCOV
303
  return new BError( "Unknown error getting ResultSet" );
×
304
}
8✔
305
BSQLConnection::BSQLConnection()
1✔
306
    : PolObjectImp( OTSQLConnection ), _conn( new ConnectionWrapper ), _errno( 0 )
1✔
307
{
308
  _conn->set( mysql_init( nullptr ) );
1✔
309
  if ( !_conn->ptr() )
1✔
310
  {
311
    _error = "Insufficient memory";
×
312
    _errno = 1;
×
313
  }
314
}
1✔
315

316
BSQLConnection::BSQLConnection( std::shared_ptr<ConnectionWrapper> conn )
×
317
    : PolObjectImp( OTSQLConnection ), _conn( conn ), _errno( 0 )
×
318
{
319
}
×
320

321
BSQLConnection::~BSQLConnection() = default;
2✔
322
std::string BSQLConnection::getStringRep() const
×
323
{
324
  return "SQLConnection";
×
325
}
326
bool BSQLConnection::isTrue() const
1✔
327
{
328
  if ( !_conn->ptr() )
1✔
329
    return false;  // closed by hand
×
330
  if ( !mysql_ping( _conn->ptr() ) )
1✔
331
    return true;
1✔
332
  return false;
×
333
}
334
bool BSQLConnection::connect( const char* host, const char* user, const char* passwd, int port )
1✔
335
{
336
  if ( !_conn->ptr() )
1✔
337
  {
338
    _errno = -1;
×
339
    _error = "No active MYSQL object instance.";
×
340
    return false;
×
341
  }
342
  // port == 0 means default sql port
343
  if ( !mysql_real_connect( _conn->ptr(), host, user, passwd, nullptr, port, nullptr, 0 ) )
1✔
344
  {
345
    _errno = mysql_errno( _conn->ptr() );
×
346
    _error = mysql_error( _conn->ptr() );
×
347
    return false;
×
348
  }
349
  return true;
1✔
350
}
351
bool BSQLConnection::select_db( const char* db )
1✔
352
{
353
  if ( !_conn->ptr() )
1✔
354
  {
355
    _errno = -1;
×
356
    _error = "No active MYSQL object instance.";
×
357
    return false;
×
358
  }
359
  if ( mysql_select_db( _conn->ptr(), db ) )
1✔
360
  {
361
    _errno = mysql_errno( _conn->ptr() );
×
362
    _error = mysql_error( _conn->ptr() );
×
363
    return false;
×
364
  }
365
  return true;
1✔
366
}
367

368
bool BSQLConnection::query( const std::string query )
8✔
369
{
370
  if ( !_conn->ptr() )
8✔
371
  {
372
    _errno = -1;
×
373
    _error = "No active MYSQL object instance.";
×
374
    return false;
×
375
  }
376

377
  if ( mysql_query( _conn->ptr(), query.c_str() ) )
8✔
378
  {
379
    _errno = mysql_errno( _conn->ptr() );
×
380
    _error = mysql_error( _conn->ptr() );
×
381
    return false;
×
382
  }
383

384
  return true;
8✔
385
}
386

387
/*
388
 * Allows binding parameters to the query
389
 * Every occurrence of "?" is replaced with a single parameter
390
 */
391
bool BSQLConnection::query( const std::string query, QueryParams params )
8✔
392
{
393
  if ( params == nullptr || params->empty() )
8✔
394
    return this->query( query );
7✔
395

396
  if ( !_conn->ptr() )
1✔
397
  {
398
    _errno = -1;
×
399
    _error = "No active MYSQL object instance.";
×
400
    return false;
×
401
  }
402

403
  std::string replaced = query;
1✔
404
  std::regex re( "^((?:[^']|'[^']*')*?)(\\?)" );
1✔
405
  for ( auto it = params->begin(); it != params->end(); ++it )
3✔
406
  {
407
    if ( !std::regex_search( replaced, re ) )
2✔
408
    {
409
      _errno = -2;
×
410
      _error = "Could not replace parameters.";
×
411
      return false;
×
412
    }
413

414
    if ( it->size() > ( std::numeric_limits<size_t>::max() - 5 ) / 2 )
2✔
415
    {
416
      _errno = -3;
×
417
      _error = "Parameter is too long.";
×
418
    }
419

420
    // Escape the string and add quoting. A bit tricky, but effective.
421
    size_t escaped_max_size =
422
        it->size() * 2 + 5;  // max is +1, using +5 to leave space for quoting and "$1"
2✔
423
    std::unique_ptr<char[]> escptr(
424
        new char[escaped_max_size] );  // will contain the escaped string
2✔
425
    // use +3 to leave space for quoting
426
    unsigned long esclen = mysql_real_escape_string( _conn->ptr(), escptr.get() + 3, it->c_str(),
2✔
427
                                                     static_cast<unsigned long>( it->size() ) );
2✔
428
    // Now add quoting, equivalent to escptr = "$1'" + escptr + "'"
429
    esclen += 4;
2✔
430
    escptr[0] = '$';
2✔
431
    escptr[1] = '1';
2✔
432
    escptr[2] = '\'';
2✔
433
    escptr[esclen - 1] = '\'';
2✔
434
    escptr[esclen] = '\0';
2✔
435

436
    replaced =
437
        std::regex_replace( replaced, re, escptr.get(), std::regex_constants::format_first_only );
2✔
438
  }
2✔
439

440
  return this->query( replaced );
1✔
441
}
1✔
442

443
bool BSQLConnection::escape_string( const std::string& text, std::string* escaped ) const
1✔
444
{
445
  if ( !_conn->ptr() )
1✔
446
    return false;
×
447
  *escaped = std::string( text.size() * 2 + 1, '\0' );
1✔
448
  if ( mysql_real_escape_string( _conn->ptr(), escaped->data(), text.data(),
1✔
449
                                 (unsigned long)text.size() ) == (unsigned long)-1 )
2✔
450
    return false;
×
451
  escaped->resize( escaped->find_first_of( '\0' ) );
1✔
452
  return true;
1✔
453
}
454

455
std::string BSQLConnection::getLastError() const
×
456
{
457
  return _error;
×
458
}
459
int BSQLConnection::getLastErrNo() const
1✔
460
{
461
  return _errno;
1✔
462
}
463
std::shared_ptr<BSQLConnection::ConnectionWrapper> BSQLConnection::getConnection() const
×
464
{
465
  return _conn;
×
466
}
467

468

469
BObjectRef BSQLConnection::get_member_id( const int /*id*/ )  // id test
×
470
{
471
  return BObjectRef( UninitObject::create() );
×
472
  // switch(id)
473
  //{
474

475
  //  default: return BObjectRef(UninitObject::create());
476
  //}
477
}
478
BObjectRef BSQLConnection::get_member( const char* membername )
×
479
{
480
  ObjMember* objmember = getKnownObjMember( membername );
×
481
  if ( objmember != nullptr )
×
482
    return this->get_member_id( objmember->id );
×
NEW
483
  return BObjectRef( UninitObject::create() );
×
484
}
485

486
Bscript::BObjectImp* BSQLConnection::call_polmethod( const char* methodname, UOExecutor& ex )
×
487
{
488
  ObjMethod* objmethod = getKnownObjMethod( methodname );
×
489
  if ( objmethod != nullptr )
×
490
    return this->call_polmethod_id( objmethod->id, ex );
×
NEW
491
  return nullptr;
×
492
}
493

494
Bscript::BObjectImp* BSQLConnection::call_polmethod_id( const int /*id*/, UOExecutor& /*ex*/,
×
495
                                                        bool /*forcebuiltin*/ )
496
{
497
  return new BLong( 0 );
×
498
}
499

500
Bscript::BObjectImp* BSQLConnection::copy() const
×
501
{
502
  return new BSQLConnection( _conn );
×
503
}
504

505
BSQLConnection::ConnectionWrapper::ConnectionWrapper() : _conn( nullptr ) {}
1✔
506
BSQLConnection::ConnectionWrapper::~ConnectionWrapper()
1✔
507
{
508
  if ( _conn )
1✔
509
    mysql_close( _conn );
1✔
510
  _conn = nullptr;
1✔
511
}
1✔
512
void BSQLConnection::ConnectionWrapper::set( MYSQL* conn )
1✔
513
{
514
  if ( _conn )
1✔
515
    mysql_close( _conn );
×
516
  _conn = conn;
1✔
517
}
1✔
518
MYSQL* BSQLConnection::ConnectionWrapper::ptr()
44✔
519
{
520
  return _conn;
44✔
521
};
522

523
ResultWrapper::ResultWrapper( MYSQL_RES* res ) : _result( res ) {}
8✔
524
ResultWrapper::ResultWrapper() : _result( nullptr ) {}
×
525
ResultWrapper::~ResultWrapper()
8✔
526
{
527
  if ( _result )
8✔
528
    mysql_free_result( _result );
4✔
529
  _result = nullptr;
8✔
530
}
8✔
531
void ResultWrapper::set( MYSQL_RES* result )
×
532
{
533
  if ( _result )
×
534
    mysql_free_result( _result );
×
535
  _result = result;
×
536
}
×
537
MYSQL_RES* ResultWrapper::ptr()
70✔
538
{
539
  return _result;
70✔
540
}
541

542

543
void sql_service_thread_stub()
2✔
544
{
545
  try
546
  {
547
    networkManager.sql_service->start();
2✔
548
  }
549
  catch ( const char* msg )
×
550
  {
551
    POLLOGLN( "SQL Thread exits due to exception: {}", msg );
×
552
    throw;
×
553
  }
×
554
  catch ( std::string& str )
×
555
  {
556
    POLLOGLN( "SQL Thread exits due to exception: {}", str );
×
557
    throw;
×
558
  }
×
559
  catch ( std::exception& ex )
×
560
  {
561
    POLLOGLN( "SQL Thread exits due to exception: {}", ex.what() );
×
562
    throw;
×
563
  }
×
564
}
2✔
565

566
SQLService::SQLService() = default;
3✔
567
SQLService::~SQLService() = default;
3✔
568
void SQLService::stop()
2✔
569
{
570
  _msgs.cancel();
2✔
571
}
2✔
572
void SQLService::push( msg&& msg_ )
10✔
573
{
574
  _msgs.push_move( std::move( msg_ ) );
10✔
575
}
10✔
576
void SQLService::start()  // executed inside a extra thread
2✔
577
{
578
  while ( !Clib::exit_signalled )
12✔
579
  {
580
    try
581
    {
582
      msg task;
12✔
583
      _msgs.pop_wait( &task );
12✔
584
      task();
10✔
585
    }
12✔
586
    catch ( msg_queue::Canceled& )
2✔
587
    {
588
      break;
2✔
589
    }
2✔
590
    // ignore remaining tasks
591
  }
592
}
2✔
593

594

595
void start_sql_service()
2✔
596
{
597
  threadhelp::start_thread( sql_service_thread_stub, "SQLService" );
2✔
598
}
2✔
599
}  // namespace Pol::Core
600

601
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc