forked from qt-creator/qt-creator
Sqlite: Introduce BindParameterCount template parameter
The BindParameterCount is checked at compile time and then again for the construction of the statement. So we provide an early error instead of an some error later or even stranger behavior. Change-Id: I860ca1f78645c222ae1accf5c7a469c77befc3bd Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -51,12 +51,8 @@ namespace Sqlite {
|
||||
BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database)
|
||||
: m_compiledStatement(nullptr, deleteCompiledStatement)
|
||||
, m_database(database)
|
||||
, m_bindingParameterCount(0)
|
||||
, m_columnCount(0)
|
||||
{
|
||||
prepare(sqlStatement);
|
||||
setBindingParameterCount();
|
||||
setColumnCount();
|
||||
}
|
||||
|
||||
void BaseStatement::deleteCompiledStatement(sqlite3_stmt *compiledStatement)
|
||||
@@ -141,11 +137,6 @@ void BaseStatement::step() const
|
||||
next();
|
||||
}
|
||||
|
||||
int BaseStatement::columnCount() const
|
||||
{
|
||||
return m_columnCount;
|
||||
}
|
||||
|
||||
void BaseStatement::bind(int index, NullValue)
|
||||
{
|
||||
int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index);
|
||||
@@ -512,34 +503,16 @@ void BaseStatement::checkForBindingError(int resultCode) const
|
||||
throwUnknowError("SqliteStatement::bind: unknown error has happened");
|
||||
}
|
||||
|
||||
void BaseStatement::checkBindingParameterCount(int bindingParameterCount) const
|
||||
{
|
||||
if (bindingParameterCount != sqlite3_bind_parameter_count(m_compiledStatement.get()))
|
||||
throw WrongBindingParameterCount{"Sqlite: wrong binding parameter count!"};
|
||||
}
|
||||
|
||||
void BaseStatement::checkColumnCount(int columnCount) const
|
||||
{
|
||||
if (columnCount != m_columnCount)
|
||||
throw ColumnCountDoesNotMatch("SqliteStatement::values: column count does not match!");
|
||||
}
|
||||
|
||||
void BaseStatement::checkBindingName(int index) const
|
||||
{
|
||||
if (index <= 0 || index > m_bindingParameterCount)
|
||||
throwWrongBingingName("SqliteStatement::bind: binding name are not exists in this statement!");
|
||||
}
|
||||
|
||||
void BaseStatement::setBindingParameterCount()
|
||||
{
|
||||
m_bindingParameterCount = sqlite3_bind_parameter_count(m_compiledStatement.get());
|
||||
}
|
||||
|
||||
Utils::SmallStringView chopFirstLetter(const char *rawBindingName)
|
||||
{
|
||||
if (rawBindingName != nullptr)
|
||||
return Utils::SmallStringView(++rawBindingName);
|
||||
|
||||
return Utils::SmallStringView("");
|
||||
}
|
||||
|
||||
void BaseStatement::setColumnCount()
|
||||
{
|
||||
m_columnCount = sqlite3_column_count(m_compiledStatement.get());
|
||||
if (columnCount != sqlite3_column_count(m_compiledStatement.get()))
|
||||
throw WrongColumnCount{"Sqlite: wrong column count!"};
|
||||
}
|
||||
|
||||
bool BaseStatement::isReadOnlyStatement() const
|
||||
@@ -582,11 +555,6 @@ void BaseStatement::throwBindingIndexIsOutOfRange(const char *whatHasHappened) c
|
||||
throw BindingIndexIsOutOfRange(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||
}
|
||||
|
||||
void BaseStatement::throwWrongBingingName(const char *whatHasHappened) const
|
||||
{
|
||||
throw WrongBindingName(whatHasHappened);
|
||||
}
|
||||
|
||||
void BaseStatement::throwUnknowError(const char *whatHasHappened) const
|
||||
{
|
||||
if (sqliteDatabaseHandle())
|
||||
|
||||
@@ -80,7 +80,6 @@ public:
|
||||
BlobView fetchBlobValue(int column) const;
|
||||
template<typename Type>
|
||||
Type fetchValue(int column) const;
|
||||
int columnCount() const;
|
||||
|
||||
void bind(int index, NullValue);
|
||||
void bind(int index, int value);
|
||||
@@ -112,10 +111,9 @@ public:
|
||||
[[noreturn]] void checkForPrepareError(int resultCode) const;
|
||||
[[noreturn]] void checkForBindingError(int resultCode) const;
|
||||
void setIfIsReadyToFetchValues(int resultCode) const;
|
||||
void checkColumnCount(int columnCount) const;
|
||||
void checkBindingName(int index) const;
|
||||
void setBindingParameterCount();
|
||||
void setColumnCount();
|
||||
void checkBindingParameterCount(int bindingParameterCount) const;
|
||||
void checkColumnCount(int columnCount) const;
|
||||
bool isReadOnlyStatement() const;
|
||||
[[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwStatementHasError(const char *whatHasHappened) const;
|
||||
@@ -149,8 +147,6 @@ protected:
|
||||
private:
|
||||
std::unique_ptr<sqlite3_stmt, void (*)(sqlite3_stmt *)> m_compiledStatement;
|
||||
Database &m_database;
|
||||
int m_bindingParameterCount;
|
||||
int m_columnCount;
|
||||
};
|
||||
|
||||
template <> SQLITE_EXPORT int BaseStatement::fetchValue<int>(int column) const;
|
||||
@@ -161,7 +157,7 @@ extern template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<U
|
||||
extern template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue<Utils::SmallString>(int column) const;
|
||||
extern template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathString>(int column) const;
|
||||
|
||||
template<typename BaseStatement, int ResultCount>
|
||||
template<typename BaseStatement, int ResultCount, int BindParameterCount>
|
||||
class StatementImplementation : public BaseStatement
|
||||
{
|
||||
struct Resetter;
|
||||
@@ -175,11 +171,11 @@ public:
|
||||
BaseStatement::next();
|
||||
}
|
||||
|
||||
void bindValues() {}
|
||||
|
||||
template<typename... ValueType>
|
||||
void bindValues(const ValueType&... values)
|
||||
void bindValues(const ValueType &...values)
|
||||
{
|
||||
static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!");
|
||||
|
||||
int index = 0;
|
||||
(BaseStatement::bind(++index, values), ...);
|
||||
}
|
||||
@@ -344,10 +340,9 @@ public:
|
||||
using const_iterator = iterator;
|
||||
|
||||
template<typename... QueryTypes>
|
||||
BaseSqliteResultRange(StatementImplementation &statement, const QueryTypes &...queryValues)
|
||||
BaseSqliteResultRange(StatementImplementation &statement)
|
||||
: m_statement{statement}
|
||||
{
|
||||
statement.bindValues(queryValues...);
|
||||
}
|
||||
|
||||
BaseSqliteResultRange(BaseSqliteResultRange &) = delete;
|
||||
@@ -376,7 +371,6 @@ public:
|
||||
SqliteResultRange(StatementImplementation &statement, const QueryTypes &...queryValues)
|
||||
: BaseSqliteResultRange<ResultType>{statement}
|
||||
, resetter{&statement}
|
||||
|
||||
{
|
||||
statement.bindValues(queryValues...);
|
||||
}
|
||||
|
||||
@@ -45,10 +45,11 @@ namespace Sqlite {
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template<int ResultCount>
|
||||
template<int ResultCount, int BindParameterCount>
|
||||
class ReadStatement;
|
||||
template<int BindParameterCount>
|
||||
class WriteStatement;
|
||||
template<int ResultCount>
|
||||
template<int ResultCount, int BindParameterCount>
|
||||
class ReadWriteStatement;
|
||||
|
||||
class SQLITE_EXPORT Database final : public TransactionInterface, public DatabaseInterface
|
||||
@@ -59,11 +60,12 @@ class SQLITE_EXPORT Database final : public TransactionInterface, public Databas
|
||||
|
||||
public:
|
||||
using MutexType = std::mutex;
|
||||
template<int ResultCount>
|
||||
using ReadStatement = Sqlite::ReadStatement<ResultCount>;
|
||||
using WriteStatement = Sqlite::WriteStatement;
|
||||
template<int ResultCount = 0>
|
||||
using ReadWriteStatement = Sqlite::ReadWriteStatement<ResultCount>;
|
||||
template<int ResultCount, int BindParameterCount = 0>
|
||||
using ReadStatement = Sqlite::ReadStatement<ResultCount, BindParameterCount>;
|
||||
template<int BindParameterCount>
|
||||
using WriteStatement = Sqlite::WriteStatement<BindParameterCount>;
|
||||
template<int ResultCount = 0, int BindParameterCount = 0>
|
||||
using ReadWriteStatement = Sqlite::ReadWriteStatement<ResultCount, BindParameterCount>;
|
||||
using BusyHandler = DatabaseBackend::BusyHandler;
|
||||
|
||||
Database();
|
||||
|
||||
@@ -104,12 +104,6 @@ public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
class ColumnCountDoesNotMatch : public Exception
|
||||
{
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
class BindingIndexIsOutOfRange : public ExceptionWithMessage
|
||||
{
|
||||
public:
|
||||
@@ -326,4 +320,10 @@ public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
class WrongColumnCount : public Exception
|
||||
{
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
} // namespace Sqlite
|
||||
|
||||
@@ -29,16 +29,18 @@
|
||||
|
||||
namespace Sqlite {
|
||||
|
||||
template<int ResultCount>
|
||||
class ReadStatement final : protected StatementImplementation<BaseStatement, ResultCount>
|
||||
template<int ResultCount, int BindParameterCount = 0>
|
||||
class ReadStatement final
|
||||
: protected StatementImplementation<BaseStatement, ResultCount, BindParameterCount>
|
||||
{
|
||||
using Base = StatementImplementation<BaseStatement, ResultCount>;
|
||||
using Base = StatementImplementation<BaseStatement, ResultCount, BindParameterCount>;
|
||||
|
||||
public:
|
||||
ReadStatement(Utils::SmallStringView sqlStatement, Database &database)
|
||||
: Base{sqlStatement, database}
|
||||
{
|
||||
checkIsReadOnlyStatement();
|
||||
Base::checkBindingParameterCount(BindParameterCount);
|
||||
Base::checkColumnCount(ResultCount);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,16 +29,18 @@
|
||||
|
||||
namespace Sqlite {
|
||||
|
||||
template<int ResultCount = 0>
|
||||
class ReadWriteStatement final : protected StatementImplementation<BaseStatement, ResultCount>
|
||||
template<int ResultCount = 0, int BindParameterCount = 0>
|
||||
class ReadWriteStatement final
|
||||
: protected StatementImplementation<BaseStatement, ResultCount, BindParameterCount>
|
||||
{
|
||||
friend class DatabaseBackend;
|
||||
using Base = StatementImplementation<BaseStatement, ResultCount>;
|
||||
using Base = StatementImplementation<BaseStatement, ResultCount, BindParameterCount>;
|
||||
|
||||
public:
|
||||
ReadWriteStatement(Utils::SmallStringView sqlStatement, Database &database)
|
||||
: Base{sqlStatement, database}
|
||||
{
|
||||
Base::checkBindingParameterCount(BindParameterCount);
|
||||
Base::checkColumnCount(ResultCount);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ private:
|
||||
|
||||
public:
|
||||
Database &database;
|
||||
WriteStatement insertSession;
|
||||
WriteStatement<1> insertSession;
|
||||
Utils::SmallString databaseName;
|
||||
Utils::SmallStringVector tableNames;
|
||||
std::unique_ptr<sqlite3_session, decltype(&sqlite3session_delete)> session;
|
||||
|
||||
@@ -28,21 +28,23 @@
|
||||
#include "sqlitebasestatement.h"
|
||||
|
||||
namespace Sqlite {
|
||||
|
||||
class WriteStatement : protected StatementImplementation<BaseStatement, -1>
|
||||
template<int BindParameterCount = 0>
|
||||
class WriteStatement : protected StatementImplementation<BaseStatement, -1, BindParameterCount>
|
||||
{
|
||||
using Base = StatementImplementation<BaseStatement, -1>;
|
||||
using Base = StatementImplementation<BaseStatement, -1, BindParameterCount>;
|
||||
|
||||
public:
|
||||
WriteStatement(Utils::SmallStringView sqlStatement, Database &database)
|
||||
: StatementImplementation(sqlStatement, database)
|
||||
: Base(sqlStatement, database)
|
||||
{
|
||||
checkIsWritableStatement();
|
||||
Base::checkBindingParameterCount(BindParameterCount);
|
||||
Base::checkColumnCount(0);
|
||||
}
|
||||
|
||||
using StatementImplementation::database;
|
||||
using StatementImplementation::execute;
|
||||
using StatementImplementation::write;
|
||||
using Base::database;
|
||||
using Base::execute;
|
||||
using Base::write;
|
||||
|
||||
protected:
|
||||
void checkIsWritableStatement()
|
||||
|
||||
Reference in New Issue
Block a user