Skip to content

Commit a81ee9e

Browse files
committed
Change how Column handle statement pointer to enforce better safety.
1 parent 12a3886 commit a81ee9e

File tree

11 files changed

+75
-106
lines changed

11 files changed

+75
-106
lines changed

include/SQLiteCpp/Column.h

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,16 @@ class Column
5050
*
5151
* @param[in] aStmtPtr Shared pointer to the prepared SQLite Statement Object.
5252
* @param[in] aIndex Index of the column in the row of result, starting at 0
53-
*
54-
* @throws Exception is thrown in case of error, then the Column object is NOT constructed.
5553
*/
56-
explicit Column(const StatementPtr::TRawStatementPtr& aStmtPtr, int aIndex);
54+
explicit Column(const TStatementPtr& aStatementPtr, uint16_t aIndex) noexcept :
55+
mStatementPtr(aStatementPtr),
56+
mIndex(aIndex), mRowIndex(mStatementPtr->mCurrentStep) {}
5757

5858
Column(const Column&) = delete;
5959
Column& operator=(const Column&) = delete;
6060

61-
Column(Column&& aColumn) = default;
62-
Column& operator=(Column&& aColumn) = default;
61+
Column(Column&& aColumn) noexcept = default;
62+
Column& operator=(Column&& aColumn) noexcept = default;
6363

6464
/**
6565
* @brief Return a pointer to the named assigned to this result column (potentially aliased)
@@ -80,27 +80,27 @@ class Column
8080
#endif
8181

8282
/// Return the integer value of the column.
83-
int32_t getInt() const noexcept;
83+
int32_t getInt() const;
8484
/// Return the 32bits unsigned integer value of the column (note that SQLite3 does not support unsigned 64bits).
85-
uint32_t getUInt() const noexcept;
85+
uint32_t getUInt() const;
8686
/// Return the 64bits integer value of the column (note that SQLite3 does not support unsigned 64bits).
87-
int64_t getInt64() const noexcept;
87+
int64_t getInt64() const;
8888
/// Return the double (64bits float) value of the column
89-
double getDouble() const noexcept;
89+
double getDouble() const;
9090
/**
9191
* @brief Return a pointer to the text value (NULL terminated string) of the column.
9292
*
9393
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
9494
* thus you must copy it before using it beyond its scope (to a std::string for instance).
9595
*/
96-
const char* getText(const char* apDefaultValue = "") const noexcept;
96+
const char* getText(const char* apDefaultValue = "") const;
9797
/**
9898
* @brief Return a pointer to the binary blob value of the column.
9999
*
100100
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
101101
* thus you must copy it before using it beyond its scope (to a std::string for instance).
102102
*/
103-
const void* getBlob() const noexcept;
103+
const void* getBlob() const;
104104
/**
105105
* @brief Return a std::string for a TEXT or BLOB column.
106106
*
@@ -153,12 +153,12 @@ class Column
153153
* - size in bytes of the binary blob returned by getBlob()
154154
* - 0 for a NULL value
155155
*/
156-
int getBytes() const noexcept;
156+
int getBytes() const;
157157

158158
/// Alias returning the number of bytes used by the text (or blob) value of the column
159-
int size() const noexcept
159+
int size() const
160160
{
161-
return getBytes ();
161+
return getBytes();
162162
}
163163

164164
/// Inline cast operators to basic types
@@ -230,8 +230,19 @@ class Column
230230
}
231231

232232
private:
233-
StatementPtr::TRawStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object
234-
int mIndex; ///< Index of the column in the row of result, starting at 0
233+
/**
234+
* @brief Returns pointer to SQLite Statement Object to use with sqlite3 API.
235+
* Checks if SQLite::Column is used with proper statement step.
236+
*
237+
* @throws SQLite::Exception when statement has changed since this object constrution.
238+
*
239+
* @return Raw pointer to SQLite Statement Object
240+
*/
241+
sqlite3_stmt* getStatement() const;
242+
243+
TStatementPtr mStatementPtr; ///< Shared Pointer to the prepared SQLite Statement Object
244+
uint16_t mIndex; ///< Index of the column in the row of result, starting at 0
245+
std::size_t mRowIndex; ///< Index of the statement row, starting at 0
235246
};
236247

237248
/**

include/SQLiteCpp/Row.h

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#pragma once
1313

1414
#include <SQLiteCpp/StatementPtr.h>
15+
#include <SQLiteCpp/Column.h>
1516

1617
#include <memory>
1718
#include <string>
@@ -26,35 +27,17 @@ namespace SQLite
2627
class Row
2728
{
2829
public:
29-
Row(TStatementWeakPtr apStatement, std::size_t aID);
30+
Row(TStatementWeakPtr apStatement, std::size_t aID) :
31+
mpStatement(apStatement), mID(aID) {}
3032

3133
std::size_t getRowNumber() const
3234
{
33-
return ID;
35+
return mID;
3436
}
3537

36-
/**
37-
* @brief Test if the column value is NULL
38-
*
39-
* @param[in] aIndex Index of the column, starting at 0
40-
*
41-
* @return true if the column value is NULL
42-
*
43-
* Throw an exception if the specified index is out of the [0, getColumnCount()) range.
44-
*/
45-
bool isColumnNull(const int aIndex) const;
46-
47-
/**
48-
* @brief Return a pointer to the text value (NULL terminated string) of the column.
49-
*
50-
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
51-
* thus you must copy it before using it beyond its scope (to a std::string for instance).
52-
*/
53-
const char* getText(uint32_t aColumnID) const noexcept;
54-
5538
private:
5639
TStatementWeakPtr mpStatement;
57-
std::size_t ID;
40+
std::size_t mID;
5841
};
5942

6043
} // namespace SQLite

include/SQLiteCpp/Statement.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ namespace SQLite
2525

2626
// Forward declaration
2727
class Database;
28-
class Column;
2928

3029

3130
/**
@@ -379,7 +378,7 @@ class Statement : public StatementExecutor
379378
* Thus, you should instead extract immediately its data (getInt(), getText()...)
380379
* and use or copy this data for any later usage.
381380
*/
382-
Column getColumn(const int aIndex) const;
381+
Column getColumn(const int aIndex);
383382

384383
/**
385384
* @brief Return a copy of the column data specified by its column name (less efficient than using an index)
@@ -410,7 +409,7 @@ class Statement : public StatementExecutor
410409
*
411410
* Throw an exception if the specified name is not one of the aliased name of the columns in the result.
412411
*/
413-
Column getColumn(const char* apName) const;
412+
Column getColumn(const char* apName);
414413

415414
#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015
416415
/**

include/SQLiteCpp/StatementExecutor.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,11 @@ class StatementExecutor
250250
/**
251251
* @brief Return a std::shared_ptr with prepared SQLite Statement Object.
252252
*
253-
* @return TRawStatementPtr with SQLite Statement Object
253+
* @return TStatementPtr with SQLite Statement Object
254254
*/
255-
StatementPtr::TRawStatementPtr getStatementPtr() const noexcept
255+
TStatementPtr getStatementPtr() const noexcept
256256
{
257-
return mpStatement->mpStatement;
257+
return mpStatement;
258258
}
259259

260260
/**
@@ -316,7 +316,7 @@ class StatementExecutor
316316

317317
/// Shared Pointer to this object.
318318
/// Allows RowIterator to execute next step.
319-
TStatementPtr mpStatement{};
319+
const TStatementPtr mpStatement{};
320320

321321
int mColumnCount = 0; //!< Number of columns in the result of the prepared statement
322322
bool mbHasRow = false; //!< true when a row has been fetched with executeStep()

include/SQLiteCpp/StatementPtr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ struct StatementPtr
6767

6868

6969
/// Shared pointer to SQLite StatementPtr
70-
using TStatementPtr = const std::shared_ptr<StatementPtr>;
70+
using TStatementPtr = std::shared_ptr<StatementPtr>;
7171

7272
/// Weak pointer to SQLite StatementPtr
7373
using TStatementWeakPtr = std::weak_ptr<StatementPtr>;

src/Column.cpp

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,90 +25,90 @@ const int BLOB = SQLITE_BLOB;
2525
const int Null = SQLITE_NULL;
2626

2727

28-
// Encapsulation of a Column in a row of the result pointed by the prepared Statement.
29-
Column::Column(const StatementPtr::TRawStatementPtr& aStmtPtr, int aIndex) :
30-
mStmtPtr(aStmtPtr),
31-
mIndex(aIndex)
32-
{
33-
if (!aStmtPtr)
34-
{
35-
throw SQLite::Exception("Statement was destroyed");
36-
}
37-
}
38-
3928
// Return the named assigned to this result column (potentially aliased)
4029
const char* Column::getName() const noexcept
4130
{
42-
return sqlite3_column_name(mStmtPtr.get(), mIndex);
31+
return sqlite3_column_name(mStatementPtr->getStatement(), mIndex);
4332
}
4433

4534
#ifdef SQLITE_ENABLE_COLUMN_METADATA
4635
// Return the name of the table column that is the origin of this result column
4736
const char* Column::getOriginName() const noexcept
4837
{
49-
return sqlite3_column_origin_name(mStmtPtr.get(), mIndex);
38+
return sqlite3_column_origin_name(mStatementPtr->getStatement(), mIndex);
5039
}
5140
#endif
5241

5342
// Return the integer value of the column specified by its index starting at 0
54-
int32_t Column::getInt() const noexcept
43+
int32_t Column::getInt() const
5544
{
56-
return sqlite3_column_int(mStmtPtr.get(), mIndex);
45+
return sqlite3_column_int(getStatement(), mIndex);
5746
}
5847

5948
// Return the unsigned integer value of the column specified by its index starting at 0
60-
uint32_t Column::getUInt() const noexcept
49+
uint32_t Column::getUInt() const
6150
{
6251
return static_cast<unsigned>(getInt64());
6352
}
6453

6554
// Return the 64bits integer value of the column specified by its index starting at 0
66-
int64_t Column::getInt64() const noexcept
55+
int64_t Column::getInt64() const
6756
{
68-
return sqlite3_column_int64(mStmtPtr.get(), mIndex);
57+
return sqlite3_column_int64(getStatement(), mIndex);
6958
}
7059

7160
// Return the double value of the column specified by its index starting at 0
72-
double Column::getDouble() const noexcept
61+
double Column::getDouble() const
7362
{
74-
return sqlite3_column_double(mStmtPtr.get(), mIndex);
63+
return sqlite3_column_double(getStatement(), mIndex);
7564
}
7665

7766
// Return a pointer to the text value (NULL terminated string) of the column specified by its index starting at 0
78-
const char* Column::getText(const char* apDefaultValue /* = "" */) const noexcept
67+
const char* Column::getText(const char* apDefaultValue /* = "" */) const
7968
{
80-
auto pText = reinterpret_cast<const char*>(sqlite3_column_text(mStmtPtr.get(), mIndex));
69+
auto pText = reinterpret_cast<const char*>(sqlite3_column_text(getStatement(), mIndex));
8170
return (pText ? pText : apDefaultValue);
8271
}
8372

8473
// Return a pointer to the blob value (*not* NULL terminated) of the column specified by its index starting at 0
85-
const void* Column::getBlob() const noexcept
74+
const void* Column::getBlob() const
8675
{
87-
return sqlite3_column_blob(mStmtPtr.get(), mIndex);
76+
return sqlite3_column_blob(getStatement(), mIndex);
8877
}
8978

9079
// Return a std::string to a TEXT or BLOB column
9180
std::string Column::getString() const
9281
{
82+
auto statement = getStatement();
83+
9384
// Note: using sqlite3_column_blob and not sqlite3_column_text
9485
// - no need for sqlite3_column_text to add a \0 on the end, as we're getting the bytes length directly
95-
auto data = static_cast<const char*>(sqlite3_column_blob(mStmtPtr.get(), mIndex));
86+
auto data = static_cast<const char*>(sqlite3_column_blob(statement, mIndex));
9687

9788
// SQLite docs: "The safest policy is to invoke… sqlite3_column_blob() followed by sqlite3_column_bytes()"
9889
// Note: std::string is ok to pass nullptr as first arg, if length is 0
99-
return std::string(data, sqlite3_column_bytes(mStmtPtr.get(), mIndex));
90+
return std::string(data, sqlite3_column_bytes(statement, mIndex));
10091
}
10192

10293
// Return the type of the value of the column
10394
int Column::getType() const noexcept
10495
{
105-
return sqlite3_column_type(mStmtPtr.get(), mIndex);
96+
return sqlite3_column_type(mStatementPtr->getStatement(), mIndex);
10697
}
10798

10899
// Return the number of bytes used by the text value of the column
109-
int Column::getBytes() const noexcept
100+
int Column::getBytes() const
110101
{
111-
return sqlite3_column_bytes(mStmtPtr.get(), mIndex);
102+
return sqlite3_column_bytes(getStatement(), mIndex);
103+
}
104+
105+
sqlite3_stmt* Column::getStatement() const
106+
{
107+
if (mStatementPtr->mCurrentStep != mRowIndex)
108+
{
109+
throw SQLite::Exception("Column is used after SQLite Statement Object has been changed");
110+
}
111+
return mStatementPtr->getStatement();
112112
}
113113

114114
// Standard std::ostream inserter

src/Row.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,5 @@ namespace SQLite
1919
{
2020

2121

22-
Row::Row(TStatementWeakPtr apStatement, std::size_t aID) :
23-
mpStatement(apStatement), ID(aID)
24-
{
25-
}
26-
27-
bool Row::isColumnNull(const int aIndex) const
28-
{
29-
auto statement = mpStatement.lock();
30-
31-
return (SQLITE_NULL == sqlite3_column_type(statement->getStatement(), aIndex));
32-
}
33-
34-
const char* Row::getText(uint32_t aColumnID) const noexcept
35-
{
36-
auto statement = mpStatement.lock();
37-
38-
39-
auto pText = reinterpret_cast<const char*>(sqlite3_column_text(statement->getStatement(), aColumnID));
40-
return (pText ? pText : "");
41-
}
4222

4323
} // namespace SQLite

src/Statement.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ void Statement::bind(const int aIndex)
119119

120120
// Return a copy of the column data specified by its index starting at 0
121121
// (use the Column copy-constructor)
122-
Column Statement::getColumn(const int aIndex) const
122+
Column Statement::getColumn(const int aIndex)
123123
{
124124
checkRow();
125125
checkIndex(aIndex);
@@ -130,7 +130,7 @@ Column Statement::getColumn(const int aIndex) const
130130

131131
// Return a copy of the column data specified by its column name starting at 0
132132
// (use the Column copy-constructor)
133-
Column Statement::getColumn(const char* apName) const
133+
Column Statement::getColumn(const char* apName)
134134
{
135135
checkRow();
136136
const int index = getColumnIndex(apName);

src/StatementPtr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ namespace SQLite
4040
return sqlite3_step(mpStatement.get());
4141
}
4242

43-
StatementPtr::TRawStatementPtr StatementPtr::prepareStatement(sqlite3* apConnection, const std::string& aQuery) const
43+
StatementPtr::TRawStatementPtr StatementPtr::prepareStatement(sqlite3* apConnection,
44+
const std::string& aQuery) const
4445
{
4546
if (!apConnection)
4647
throw SQLite::Exception("Can't create statement without valid database connection");

tests/Column_test.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,16 +260,12 @@ TEST(Column, shared_ptr)
260260
query->executeStep();
261261
column0 = query->getColumn(0);
262262
EXPECT_EQ(true, column0.isInteger());
263+
263264
query->executeStep(); // query is done
264-
265-
// Undefined behavior
266-
// auto x = column0.getInt();
265+
EXPECT_THROW(column0.getInt(), SQLite::Exception);
267266

268267
query.reset();
269-
270-
// Undefined behavior
271-
// auto x = column0.getInt();
272-
// bool isInt = column0.isInteger();
268+
EXPECT_THROW(column0.getInt(), SQLite::Exception);
273269

274270
EXPECT_STREQ("id", column0.getName());
275271
}

0 commit comments

Comments
 (0)