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

SRombauts / SQLiteCpp / 28716898655

04 Jul 2026 07:17PM UTC coverage: 99.248% (-0.4%) from 99.689%
28716898655

Pull #545

github

web-flow
Merge 7de8d9e74 into a6537fe4c
Pull Request #545: Range for loop for statements

19 of 22 new or added lines in 1 file covered. (86.36%)

660 of 665 relevant lines covered (99.25%)

34.77 hits per line

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

97.92
/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-2025 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) :
121 ✔
35
    mQuery(apQuery),
242 ✔
36
    mpSQLite(aDatabase.getHandle()),
121 ✔
37
    mpPreparedStatement(prepareStatement()) // prepare the SQL query (needs Database friendship)
121 ✔
38
{
39
    mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
120 ✔
40
}
121 ✔
41

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

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

64
int Statement::tryReset() noexcept
44 ✔
65
{
66
    mbHasRow = false;
44 ✔
67
    mbDone = false;
44 ✔
68
    return sqlite3_reset(mpPreparedStatement.get());
44 ✔
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
33 ✔
79
{
80
    return sqlite3_bind_parameter_index(getPreparedStatement(), apName);
33 ✔
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)
29 ✔
85
{
86
    const int ret = sqlite3_bind_int(getPreparedStatement(), aIndex, aValue);
29 ✔
87
    check(ret);
28 ✔
88
}
22 ✔
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)
12 ✔
113
{
114
    const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(),
12 ✔
115
                                      static_cast<int>(aValue.size()), SQLITE_TRANSIENT);
12 ✔
116
    check(ret);
12 ✔
117
}
12 ✔
118

119
// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
120
void Statement::bind(const int aIndex, const char* apValue)
44 ✔
121
{
122
    const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_TRANSIENT);
44 ✔
123
    check(ret);
44 ✔
124
}
43 ✔
125

126
// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
127
void Statement::bind(const int aIndex, const void* apValue, const int aSize)
7 ✔
128
{
129
    const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_TRANSIENT);
7 ✔
130
    check(ret);
7 ✔
131
}
7 ✔
132

133
// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
134
void Statement::bindNoCopy(const int aIndex, const std::string& aValue)
3 ✔
135
{
136
    const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(),
3 ✔
137
                                      static_cast<int>(aValue.size()), SQLITE_STATIC);
3 ✔
138
    check(ret);
3 ✔
139
}
3 ✔
140

141
// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
142
void Statement::bindNoCopy(const int aIndex, const char* apValue)
3 ✔
143
{
144
    const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_STATIC);
3 ✔
145
    check(ret);
3 ✔
146
}
3 ✔
147

148
// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
149
void Statement::bindNoCopy(const int aIndex, const void* apValue, const int aSize)
3 ✔
150
{
151
    const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_STATIC);
3 ✔
152
    check(ret);
3 ✔
153
}
3 ✔
154

155
// Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
156
void Statement::bind(const int aIndex)
4 ✔
157
{
158
    const int ret = sqlite3_bind_null(getPreparedStatement(), aIndex);
4 ✔
159
    check(ret);
4 ✔
160
}
3 ✔
161

162

163
// Execute a step of the query to fetch one row of results
164
bool Statement::executeStep()
187 ✔
165
{
166
    const int ret = tryExecuteStep();
187 ✔
167
    if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem
187 ✔
168
    {
169
        if (ret == sqlite3_errcode(mpSQLite))
2 ✔
170
        {
171
            throw SQLite::Exception(mpSQLite, ret);
1 ✔
172
        }
173
        else
174
        {
175
            throw SQLite::Exception("Statement needs to be reseted", ret);
1 ✔
176
        }
177
    }
178

179
    return mbHasRow; // true only if one row is accessible by getColumn(N)
185 ✔
180
}
181

182
// Execute a one-step query with no expected result, and return the number of changes.
183
int Statement::exec()
40 ✔
184
{
185
    const int ret = tryExecuteStep();
40 ✔
186
    if (SQLITE_DONE != ret) // the statement has finished executing successfully
40 ✔
187
    {
188
        if (SQLITE_ROW == ret)
5 ✔
189
        {
190
            throw SQLite::Exception("exec() does not expect results. Use executeStep.");
1 ✔
191
        }
192
        else if (ret == sqlite3_errcode(mpSQLite))
4 ✔
193
        {
194
            throw SQLite::Exception(mpSQLite, ret);
1 ✔
195
        }
196
        else
197
        {
198
            throw SQLite::Exception("Statement needs to be reseted", ret);
3 ✔
199
        }
200
    }
201

202
    // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE)
203
    return sqlite3_changes(mpSQLite);
35 ✔
204
}
205

206
int Statement::tryExecuteStep() noexcept
230 ✔
207
{
208
    if (mbDone)
230 ✔
209
    {
210
        return SQLITE_MISUSE; // Statement needs to be reseted !
4 ✔
211
    }
212

213
    const int ret = sqlite3_step(mpPreparedStatement.get());
226 ✔
214
    if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it
226 ✔
215
    {
216
        mbHasRow = true;
156 ✔
217
    }
218
    else
219
    {
220
        mbHasRow = false;
70 ✔
221
        mbDone = SQLITE_DONE == ret; // check if the query has finished executing
70 ✔
222
    }
223
    return ret;
226 ✔
224
}
225

226

227
// Return a copy of the column data specified by its index starting at 0
228
// (use the Column copy-constructor)
229
Column Statement::getColumn(const int aIndex) const
446 ✔
230
{
231
    checkRow();
446 ✔
232
    checkIndex(aIndex);
438 ✔
233

234
    // Share the Statement Object handle with the new Column created
235
    return Column(mpPreparedStatement, aIndex);
438 ✔
236
}
237

238
// Return a copy of the column data specified by its column name starting at 0
239
// (use the Column copy-constructor)
240
Column Statement::getColumn(const char* apName) const
17 ✔
241
{
242
    checkRow();
17 ✔
243
    const int index = getColumnIndex(apName);
17 ✔
244

245
    // Share the Statement Object handle with the new Column created
246
    return Column(mpPreparedStatement, index);
15 ✔
247
}
248

249
// Test if the column is NULL
250
bool Statement::isColumnNull(const int aIndex) const
36 ✔
251
{
252
    checkRow();
36 ✔
253
    checkIndex(aIndex);
31 ✔
254
    return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), aIndex));
19 ✔
255
}
256

257
bool Statement::isColumnNull(const char* apName) const
15 ✔
258
{
259
    checkRow();
15 ✔
260
    const int index = getColumnIndex(apName);
15 ✔
261
    return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), index));
11 ✔
262
}
263

264
// Return the named assigned to the specified result column (potentially aliased)
265
const char* Statement::getColumnName(const int aIndex) const
5 ✔
266
{
267
    checkIndex(aIndex);
5 ✔
268
    return sqlite3_column_name(getPreparedStatement(), aIndex);
5 ✔
269
}
270

271
#ifdef SQLITE_ENABLE_COLUMN_METADATA
272
// Return the named assigned to the specified result column (potentially aliased)
273
const char* Statement::getColumnOriginName(const int aIndex) const
5 ✔
274
{
275
    checkIndex(aIndex);
5 ✔
276
    return sqlite3_column_origin_name(getPreparedStatement(), aIndex);
5 ✔
277
}
278
#endif
279

280
// Return the index of the specified (potentially aliased) column name
281
int Statement::getColumnIndex(const char* apName) const
34 ✔
282
{
283
    // Build the map of column index by name on first call
284
    if (mColumnNames.empty())
34 ✔
285
    {
286
        for (int i = 0; i < mColumnCount; ++i)
17 ✔
287
        {
288
            const char* pName = sqlite3_column_name(getPreparedStatement(), i);
12 ✔
289
            if (pName)
12 ✔
290
            {
291
                mColumnNames[pName] = i;
24 ✔
292
            }
293
        }
294
    }
295

296
    const auto iIndex = mColumnNames.find(apName);
68 ✔
297
    if (iIndex == mColumnNames.end())
34 ✔
298
    {
299
        throw SQLite::Exception("Unknown column name.");
7 ✔
300
    }
301

302
    return iIndex->second;
54 ✔
303
}
304

305
const char* Statement::getColumnDeclaredType(const int aIndex) const
6 ✔
306
{
307
    checkIndex(aIndex);
6 ✔
308
    const char * result = sqlite3_column_decltype(getPreparedStatement(), aIndex);
5 ✔
309
    if (!result)
5 ✔
310
    {
311
        throw SQLite::Exception("Could not determine declared column type.");
2 ✔
312
    }
313
    else
314
    {
315
        return result;
3 ✔
316
    }
317
}
318

319
// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
320
int Statement::getChanges() const noexcept
3 ✔
321
{
322
    return sqlite3_changes(mpSQLite);
3 ✔
323
}
324

325
int Statement::getBindParameterCount() const noexcept
3 ✔
326
{
327
    return sqlite3_bind_parameter_count(mpPreparedStatement.get());
3 ✔
328
}
329

330
// Return the numeric result code for the most recent failed API call (if any).
331
int Statement::getErrorCode() const noexcept
2 ✔
332
{
333
    return sqlite3_errcode(mpSQLite);
2 ✔
334
}
335

336
// Return the extended numeric result code for the most recent failed API call (if any).
337
int Statement::getExtendedErrorCode() const noexcept
2 ✔
338
{
339
    return sqlite3_extended_errcode(mpSQLite);
2 ✔
340
}
341

342
// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
343
const char* Statement::getErrorMsg() const noexcept
1 ✔
344
{
345
    return sqlite3_errmsg(mpSQLite);
1 ✔
346
}
347

348

349
// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded.
350
std::string Statement::getExpandedSQL() const {
1 ✔
351
    #ifdef SQLITECPP_DISABLE_SQLITE3_EXPANDED_SQL
352
    throw SQLite::Exception("this version of SQLiteCpp does not support expanded SQL");
353
    #else
354
    char* expanded = sqlite3_expanded_sql(getPreparedStatement());
1 ✔
355
    std::string expandedString(expanded ? expanded : "");
1 ✔
356
    sqlite3_free(expanded);
1 ✔
357
    return expandedString;
1 ✔
358
    #endif
359
}
×
360

361
Statement::RowIterator::RowIterator(Statement* apStatement): mpStatement(apStatement)
10 ✔
362
{}
10 ✔
363

364
Statement::RowIterator& Statement::RowIterator::operator++()
11 ✔
365
{
366
    if (!mpStatement->executeStep())
11 ✔
367
        mpStatement = nullptr;
4 ✔
368
    return *this;
11 ✔
369
}
370

NEW
371
void Statement::RowIterator::operator++(int)
×
372
{
NEW
373
    ++(*this);
×
NEW
374
}
×
375

376
bool Statement::RowIterator::operator==(const RowIterator& aOther) const
16 ✔
377
{
378
    return mpStatement == aOther.mpStatement;
16 ✔
379
}
380

381
bool Statement::RowIterator::operator!=(const RowIterator& aOther) const
16 ✔
382
{
383
    return !this->operator==(aOther);
16 ✔
384
}
385

386
Statement& Statement::RowIterator::operator*() const
11 ✔
387
{
388
    return *mpStatement;
11 ✔
389
}
390

391
Statement::RowIterator Statement::begin()
5 ✔
392
{
393
    reset();
5 ✔
394
    if (executeStep())
5 ✔
395
        return RowIterator { this };
4 ✔
396
    return RowIterator { nullptr };
1 ✔
397
}
398

399
Statement::RowIterator Statement::end()
5 ✔
400
{
401
    return RowIterator{ nullptr };
5 ✔
402
}
403

404

405
// Prepare SQLite statement object and return shared pointer to this object
406
Statement::TStatementPtr Statement::prepareStatement()
121 ✔
407
{
408
    sqlite3_stmt* statement;
409
    const int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), static_cast<int>(mQuery.size()), &statement, nullptr);
121 ✔
410
    if (SQLITE_OK != ret)
121 ✔
411
    {
412
        throw SQLite::Exception(mpSQLite, ret);
1 ✔
413
    }
414
    return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt)
120 ✔
415
        {
416
            sqlite3_finalize(stmt);
120 ✔
417
        });
240 ✔
418
}
419

420
// Return prepered statement object or throw
421
sqlite3_stmt* Statement::getPreparedStatement() const
220 ✔
422
{
423
    sqlite3_stmt* ret = mpPreparedStatement.get();
220 ✔
424
    if (ret)
220 ✔
425
    {
426
        return ret;
219 ✔
427
    }
428
    throw SQLite::Exception("Statement was not prepared.");
1 ✔
429
}
430

431
}  // 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