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

SRombauts / SQLiteCpp / 39963216385

23 Oct 2026 01:40PM UTC coverage: 99.727% (+0.01%) from 99.714%
39963216385

Pull #518

github

web-flow
Merge a5085cadc into 8ce502e03
Pull Request #518: std::string_view support

30 of 30 new or added lines in 4 files covered. (100.0%)

2 existing lines in 2 files now uncovered.

731 of 733 relevant lines covered (99.73%)

38.11 hits per line

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

99.53
/src/Statement.cpp
1
/**
2
 * @file    Statement.cpp
3
 * @ingroup SQLiteCpp
4
 * @brief   A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result.
5
 *
6
 * Copyright (c) 2012-2026 Sebastien Rombauts (sebastien.rombauts@gmail.com)
7
 *
8
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
9
 * or copy at http://opensource.org/licenses/MIT)
10
 */
11
#include <SQLiteCpp/Statement.h>
12

13
#include <SQLiteCpp/Database.h>
14
#include <SQLiteCpp/Column.h>
15
#include <SQLiteCpp/Assertion.h>
16
#include <SQLiteCpp/Exception.h>
17

18
#include <sqlite3.h>
19

20
// check for if SQLite3 version >= 3.14.0
21
#if SQLITE_VERSION_NUMBER < 3014000
22
    #warning "SQLite3 version is less than 3.14.0, so expanded SQL is not available"
23
    #warning "To use expanded SQL, please upgrade to SQLite3 version 3.14.0 or later"
24
    #warning "If you want to disable this warning, define SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL"
25
    #warning "or use the specific project option in your build system"
26
    #warning "disabling expanded SQL support"
27
    #define SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL
28
#endif
29

30

31
namespace SQLite
32
{
33

34
Statement::Statement(const Database& aDatabase, const char* apQuery) :
140 ✔
35
    mQuery(apQuery),
280 ✔
36
    mpSQLite(aDatabase.getHandle()),
140 ✔
37
    mpPreparedStatement(prepareStatement()) // prepare the SQL query (needs Database friendship)
140 ✔
38
{
39
    mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
139 ✔
40
}
140 ✔
41

42
Statement::Statement(Statement&& aStatement) noexcept :
3 ✔
43
    mQuery(std::move(aStatement.mQuery)),
3 ✔
44
    mpSQLite(aStatement.mpSQLite),
3 ✔
45
    mpPreparedStatement(std::move(aStatement.mpPreparedStatement)),
3 ✔
46
    mColumnCount(aStatement.mColumnCount),
3 ✔
47
    mbHasRow(aStatement.mbHasRow),
3 ✔
48
    mbDone(aStatement.mbDone),
3 ✔
49
    mColumnNames(std::move(aStatement.mColumnNames))
3 ✔
50
{
51
    aStatement.mpSQLite = nullptr;
3 ✔
52
    aStatement.mColumnCount = 0;
3 ✔
53
    aStatement.mbHasRow = false;
3 ✔
54
    aStatement.mbDone = false;
3 ✔
55
}
3 ✔
56

57
// Reset the statement to make it ready for a new execution (see also #clearBindings() below)
58
void Statement::reset()
50 ✔
59
{
60
    const int ret = tryReset();
50 ✔
61
    check(ret);
50 ✔
62
}
49 ✔
63

64
int Statement::tryReset() noexcept
51 ✔
65
{
66
    mbHasRow = false;
51 ✔
67
    mbDone = false;
51 ✔
68
    return sqlite3_reset(mpPreparedStatement.get());
51 ✔
69
}
70

71
// Clears away all the bindings of a prepared statement (can be associated with #reset() above).
72
void Statement::clearBindings()
7 ✔
73
{
74
    const int ret = sqlite3_clear_bindings(getPreparedStatement());
7 ✔
75
    check(ret);
7 ✔
76
}
7 ✔
77

78
int Statement::getIndex(const char * const apName) const
40 ✔
79
{
80
    return sqlite3_bind_parameter_index(getPreparedStatement(), apName);
40 ✔
81
}
82

83
// Bind an 32bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
84
void Statement::bind(const int aIndex, const int32_t aValue)
30 ✔
85
{
86
    const int ret = sqlite3_bind_int(getPreparedStatement(), aIndex, aValue);
30 ✔
87
    check(ret);
29 ✔
88
}
23 ✔
89

90
// Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
91
void Statement::bind(const int aIndex, const uint32_t aValue)
3 ✔
92
{
93
    const int ret = sqlite3_bind_int64(getPreparedStatement(), aIndex, aValue);
3 ✔
94
    check(ret);
3 ✔
95
}
3 ✔
96

97
// Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
98
void Statement::bind(const int aIndex, const int64_t aValue)
8 ✔
99
{
100
    const int ret = sqlite3_bind_int64(getPreparedStatement(), aIndex, aValue);
8 ✔
101
    check(ret);
8 ✔
102
}
8 ✔
103

104
// Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
105
void Statement::bind(const int aIndex, const double aValue)
6 ✔
106
{
107
    const int ret = sqlite3_bind_double(getPreparedStatement(), aIndex, aValue);
6 ✔
108
    check(ret);
6 ✔
109
}
6 ✔
110

111
// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
112
void Statement::bind(const int aIndex, const std::string& aValue)
14 ✔
113
{
114
    const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, aValue.c_str(),
14 ✔
115
                                        static_cast<sqlite3_uint64>(aValue.size()), SQLITE_TRANSIENT, SQLITE_UTF8);
14 ✔
116
    check(ret);
14 ✔
117
}
14 ✔
118

119
// Bind a string_view value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
120
void Statement::bind(const int aIndex, const std::string_view aValue)
10 ✔
121
{
122
    const char* data = aValue.empty() ? "" : aValue.data();
10 ✔
123
    const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, data,
10 ✔
124
                                        aValue.size(), SQLITE_TRANSIENT, SQLITE_UTF8);
10 ✔
125
    check(ret);
10 ✔
126
}
10 ✔
127

128
// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
129
void Statement::bind(const int aIndex, const char* apValue)
45 ✔
130
{
131
    const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_TRANSIENT);
45 ✔
132
    check(ret);
45 ✔
133
}
44 ✔
134

135
// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
136
void Statement::bind(const int aIndex, const void* apValue, const int aSize)
7 ✔
137
{
138
    const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_TRANSIENT);
7 ✔
139
    check(ret);
7 ✔
140
}
7 ✔
141

142
// Bind a binary blob using a 64-bit size and SQLITE_TRANSIENT
143
void Statement::bind64(const int aIndex, const void* apValue, const uint64_t aSize)
4 ✔
144
{
145
    const int ret = sqlite3_bind_blob64(getPreparedStatement(), aIndex, apValue,
4 ✔
146
                                        static_cast<sqlite3_uint64>(aSize), SQLITE_TRANSIENT);
147
    check(ret);
4 ✔
148
}
3 ✔
149

150
// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
151
void Statement::bindNoCopy(const int aIndex, const std::string& aValue)
3 ✔
152
{
153
    const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, aValue.c_str(),
3 ✔
154
                                        static_cast<sqlite3_uint64>(aValue.size()), SQLITE_STATIC, SQLITE_UTF8);
3 ✔
155
    check(ret);
3 ✔
156
}
3 ✔
157

158
// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
159
void Statement::bindNoCopy(const int aIndex, const std::string_view aValue)
10 ✔
160
{
161
    const char* data = aValue.empty() ? "" : aValue.data();
10 ✔
162
    const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, data,
10 ✔
163
                                        aValue.size(), SQLITE_STATIC, SQLITE_UTF8);
10 ✔
164
    check(ret);
10 ✔
165
}
10 ✔
166

167
// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
168
void Statement::bindNoCopy(const int aIndex, const char* apValue)
3 ✔
169
{
170
    const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_STATIC);
3 ✔
171
    check(ret);
3 ✔
172
}
3 ✔
173

174
// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
175
void Statement::bindNoCopy(const int aIndex, const void* apValue, const int aSize)
3 ✔
176
{
177
    const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_STATIC);
3 ✔
178
    check(ret);
3 ✔
179
}
3 ✔
180

181
// Bind a binary blob using a 64-bit size and SQLITE_STATIC
182
void Statement::bindNoCopy64(const int aIndex, const void* apValue, const uint64_t aSize)
3 ✔
183
{
184
    const int ret = sqlite3_bind_blob64(getPreparedStatement(), aIndex, apValue,
3 ✔
185
                                        static_cast<sqlite3_uint64>(aSize), SQLITE_STATIC);
186
    check(ret);
3 ✔
187
}
2 ✔
188

189
// Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
190
void Statement::bind(const int aIndex)
4 ✔
191
{
192
    const int ret = sqlite3_bind_null(getPreparedStatement(), aIndex);
4 ✔
193
    check(ret);
4 ✔
194
}
3 ✔
195

196

197
// Execute a step of the query to fetch one row of results
198
bool Statement::executeStep()
217 ✔
199
{
200
    const int ret = tryExecuteStep();
217 ✔
201
    if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem
217 ✔
202
    {
203
        if (ret == sqlite3_errcode(mpSQLite))
2 ✔
204
        {
205
            throw SQLite::Exception(mpSQLite, ret);
1 ✔
206
        }
207
        else
208
        {
209
            throw SQLite::Exception("Statement needs to be reseted", ret);
1 ✔
210
        }
211
    }
212

213
    return mbHasRow; // true only if one row is accessible by getColumn(N)
215 ✔
214
}
215

216
// Execute a one-step query with no expected result, and return the number of changes.
217
int Statement::exec()
46 ✔
218
{
219
    const int ret = tryExecuteStep();
46 ✔
220
    if (SQLITE_DONE != ret) // the statement has finished executing successfully
46 ✔
221
    {
222
        if (SQLITE_ROW == ret)
5 ✔
223
        {
224
            throw SQLite::Exception("exec() does not expect results. Use executeStep.");
1 ✔
225
        }
226
        else if (ret == sqlite3_errcode(mpSQLite))
4 ✔
227
        {
228
            throw SQLite::Exception(mpSQLite, ret);
1 ✔
229
        }
230
        else
231
        {
232
            throw SQLite::Exception("Statement needs to be reseted", ret);
3 ✔
233
        }
234
    }
235

236
    // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE)
237
    return sqlite3_changes(mpSQLite);
41 ✔
238
}
239

240
int Statement::tryExecuteStep() noexcept
266 ✔
241
{
242
    if (mbDone)
266 ✔
243
    {
244
        return SQLITE_MISUSE; // Statement needs to be reseted !
4 ✔
245
    }
246

247
    const int ret = sqlite3_step(mpPreparedStatement.get());
262 ✔
248
    if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it
262 ✔
249
    {
250
        mbHasRow = true;
181 ✔
251
    }
252
    else
253
    {
254
        mbHasRow = false;
81 ✔
255
        mbDone = SQLITE_DONE == ret; // check if the query has finished executing
81 ✔
256
    }
257
    return ret;
262 ✔
258
}
259

260

261
// Return a copy of the column data specified by its index starting at 0
262
// (use the Column copy-constructor)
263
Column Statement::getColumn(const int aIndex) const
575 ✔
264
{
265
    checkRow();
575 ✔
266
    checkIndex(aIndex);
567 ✔
267

268
    // Share the Statement Object handle with the new Column created
269
    return Column(mpPreparedStatement, aIndex);
567 ✔
270
}
271

272
// Return a copy of the column data specified by its column name starting at 0
273
// (use the Column copy-constructor)
274
Column Statement::getColumn(const char* apName) const
17 ✔
275
{
276
    checkRow();
17 ✔
277
    const int index = getColumnIndex(apName);
17 ✔
278

279
    // Share the Statement Object handle with the new Column created
280
    return Column(mpPreparedStatement, index);
15 ✔
281
}
282

283
// Test if the column is NULL
284
bool Statement::isColumnNull(const int aIndex) const
36 ✔
285
{
286
    checkRow();
36 ✔
287
    checkIndex(aIndex);
31 ✔
288
    return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), aIndex));
19 ✔
289
}
290

291
bool Statement::isColumnNull(const char* apName) const
15 ✔
292
{
293
    checkRow();
15 ✔
294
    const int index = getColumnIndex(apName);
15 ✔
295
    return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), index));
11 ✔
296
}
297

298
// Return the named assigned to the specified result column (potentially aliased)
299
const char* Statement::getColumnName(const int aIndex) const
5 ✔
300
{
301
    checkIndex(aIndex);
5 ✔
302
    return sqlite3_column_name(getPreparedStatement(), aIndex);
5 ✔
303
}
304

305
#ifdef SQLITE_ENABLE_COLUMN_METADATA
306
// Return the named assigned to the specified result column (potentially aliased)
307
const char* Statement::getColumnOriginName(const int aIndex) const
5 ✔
308
{
309
    checkIndex(aIndex);
5 ✔
310
    return sqlite3_column_origin_name(getPreparedStatement(), aIndex);
5 ✔
311
}
312

313
// Return the original table name for the specified result column.
314
const char* Statement::getColumnTableName(const int aIndex) const
11 ✔
315
{
316
    checkIndex(aIndex);
11 ✔
317
    return sqlite3_column_table_name(getPreparedStatement(), aIndex);
8 ✔
318
}
319
#endif
320

321
// Return the index of the specified (potentially aliased) column name
322
int Statement::getColumnIndex(const char* apName) const
34 ✔
323
{
324
    // Build the map of column index by name on first call
325
    if (mColumnNames.empty())
34 ✔
326
    {
327
        for (int i = 0; i < mColumnCount; ++i)
17 ✔
328
        {
329
            const char* pName = sqlite3_column_name(getPreparedStatement(), i);
12 ✔
330
            if (pName)
12 ✔
331
            {
332
                mColumnNames[pName] = i;
36 ✔
333
            }
334
        }
335
    }
336

337
    const auto iIndex = mColumnNames.find(apName);
68 ✔
338
    if (iIndex == mColumnNames.end())
34 ✔
339
    {
340
        throw SQLite::Exception("Unknown column name.");
7 ✔
341
    }
342

343
    return iIndex->second;
54 ✔
344
}
345

346
const char* Statement::getColumnDeclaredType(const int aIndex) const
6 ✔
347
{
348
    checkIndex(aIndex);
6 ✔
349
    const char * result = sqlite3_column_decltype(getPreparedStatement(), aIndex);
5 ✔
350
    if (!result)
5 ✔
351
    {
352
        throw SQLite::Exception("Could not determine declared column type.");
2 ✔
353
    }
354
    else
355
    {
356
        return result;
3 ✔
357
    }
358
}
359

360
// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
361
int Statement::getChanges() const noexcept
3 ✔
362
{
363
    return sqlite3_changes(mpSQLite);
3 ✔
364
}
365

366
int Statement::getBindParameterCount() const noexcept
3 ✔
367
{
368
    return sqlite3_bind_parameter_count(mpPreparedStatement.get());
3 ✔
369
}
370

371
// Return the numeric result code for the most recent failed API call (if any).
372
int Statement::getErrorCode() const noexcept
2 ✔
373
{
374
    return sqlite3_errcode(mpSQLite);
2 ✔
375
}
376

377
// Return the extended numeric result code for the most recent failed API call (if any).
378
int Statement::getExtendedErrorCode() const noexcept
2 ✔
379
{
380
    return sqlite3_extended_errcode(mpSQLite);
2 ✔
381
}
382

383
// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
384
const char* Statement::getErrorMsg() const noexcept
1 ✔
385
{
386
    return sqlite3_errmsg(mpSQLite);
1 ✔
387
}
388

389

390
// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded.
391
std::string Statement::getExpandedSQL() const {
1 ✔
392
    #ifdef SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL
393
    throw SQLite::Exception("this version of SQLiteCpp does not support expanded SQL");
394
    #else
395
    char* expanded = sqlite3_expanded_sql(getPreparedStatement());
1 ✔
396
    std::string expandedString(expanded ? expanded : "");
1 ✔
397
    sqlite3_free(expanded);
1 ✔
398
    return expandedString;
1 ✔
399
    #endif
UNCOV
400
}
×
401

402
Statement::RowIterator::RowIterator(Statement* apStatement): mpStatement(apStatement)
17 ✔
403
{}
17 ✔
404

405
Statement::RowIterator& Statement::RowIterator::operator++()
20 ✔
406
{
407
    if (!mpStatement->executeStep())
20 ✔
408
        mpStatement = nullptr;
7 ✔
409
    return *this;
20 ✔
410
}
411

412
void Statement::RowIterator::operator++(int)
3 ✔
413
{
414
    ++(*this);
3 ✔
415
}
3 ✔
416

417
bool Statement::RowIterator::operator==(const RowIterator& aOther) const
36 ✔
418
{
419
    return mpStatement == aOther.mpStatement;
36 ✔
420
}
421

422
bool Statement::RowIterator::operator!=(const RowIterator& aOther) const
32 ✔
423
{
424
    return !this->operator==(aOther);
32 ✔
425
}
426

427
Statement& Statement::RowIterator::operator*() const
17 ✔
428
{
429
    return *mpStatement;
17 ✔
430
}
431

432
Statement::RowIterator Statement::begin()
8 ✔
433
{
434
    reset();
8 ✔
435
    if (executeStep())
8 ✔
436
        return RowIterator { this };
7 ✔
437
    return RowIterator { nullptr };
1 ✔
438
}
439

440
Statement::RowIterator Statement::end()
9 ✔
441
{
442
    return RowIterator{ nullptr };
9 ✔
443
}
444

445

446
// Prepare SQLite statement object and return shared pointer to this object
447
Statement::TStatementPtr Statement::prepareStatement()
140 ✔
448
{
449
    sqlite3_stmt* statement;
450
    const int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), static_cast<int>(mQuery.size()), &statement, nullptr);
140 ✔
451
    if (SQLITE_OK != ret)
140 ✔
452
    {
453
        throw SQLite::Exception(mpSQLite, ret);
1 ✔
454
    }
455
    return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt)
139 ✔
456
        {
457
            sqlite3_finalize(stmt);
139 ✔
458
        });
278 ✔
459
}
460

461
// Return prepered statement object or throw
462
sqlite3_stmt* Statement::getPreparedStatement() const
266 ✔
463
{
464
    sqlite3_stmt* ret = mpPreparedStatement.get();
266 ✔
465
    if (ret)
266 ✔
466
    {
467
        return ret;
265 ✔
468
    }
469
    throw SQLite::Exception("Statement was not prepared.");
1 ✔
470
}
471

472
}  // namespace SQLite
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc