sqlitebasestatement.h 12.8 KB
Newer Older
1 2
/****************************************************************************
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5 6 7 8 9 10 11
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 24 25
**
****************************************************************************/

hjk's avatar
hjk committed
26
#pragma once
27

Marco Bubke's avatar
Marco Bubke committed
28 29 30
#include "sqliteglobal.h"

#include "sqliteexception.h"
31

32
#include <utils/smallstringvector.h>
33

34 35
#include <utils/optional.h>

Marco Bubke's avatar
Marco Bubke committed
36
#include <cstdint>
Marco Bubke's avatar
Marco Bubke committed
37
#include <memory>
Marco Bubke's avatar
Marco Bubke committed
38 39 40 41
#include <type_traits>
#include <tuple>

using std::int64_t;
42 43 44 45

struct sqlite3_stmt;
struct sqlite3;

Marco Bubke's avatar
Marco Bubke committed
46 47
namespace Sqlite {

48 49
class Database;
class DatabaseBackend;
50

Marco Bubke's avatar
Marco Bubke committed
51
class SQLITE_EXPORT BaseStatement
52
{
Marco Bubke's avatar
Marco Bubke committed
53 54
public:
    explicit BaseStatement(Utils::SmallStringView sqlStatement, Database &database);
55

Marco Bubke's avatar
Marco Bubke committed
56
    static void deleteCompiledStatement(sqlite3_stmt *m_compiledStatement);
57 58 59

    bool next() const;
    void step() const;
60
    void execute() const;
61 62
    void reset() const;

63 64 65 66
    int fetchIntValue(int column) const;
    long fetchLongValue(int column) const;
    long long fetchLongLongValue(int column) const;
    double fetchDoubleValue(int column) const;
67
    Utils::SmallStringView fetchSmallStringViewValue(int column) const;
68
    template<typename Type>
69
    Type fetchValue(int column) const;
70
    int columnCount() const;
71
    Utils::SmallStringVector columnNames() const;
72

73 74 75 76
    void bind(int index, int fetchValue);
    void bind(int index, long long fetchValue);
    void bind(int index, double fetchValue);
    void bind(int index, Utils::SmallStringView fetchValue);
77

Marco Bubke's avatar
Marco Bubke committed
78 79 80 81 82 83 84 85 86 87
    void bind(int index, uint value)
    {
        bind(index, static_cast<long long>(value));
    }

    void bind(int index, long value)
    {
        bind(index, static_cast<long long>(value));
    }

Marco Bubke's avatar
Marco Bubke committed
88 89 90 91 92 93 94 95 96 97 98
    template <typename Type>
    void bind(Utils::SmallStringView name, Type fetchValue);

    int bindingIndexForName(Utils::SmallStringView name) const;

    void prepare(Utils::SmallStringView sqlStatement);
    void waitForUnlockNotify() const;

    sqlite3 *sqliteDatabaseHandle() const;
    TextEncoding databaseTextEncoding();

99 100 101 102
    [[noreturn]] void checkForStepError(int resultCode) const;
    [[noreturn]] void checkForResetError(int resultCode) const;
    [[noreturn]] void checkForPrepareError(int resultCode) const;
    [[noreturn]] void checkForBindingError(int resultCode) const;
Marco Bubke's avatar
Marco Bubke committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    void setIfIsReadyToFetchValues(int resultCode) const;
    void checkIfIsReadyToFetchValues() const;
    void checkColumnsAreValid(const std::vector<int> &columns) const;
    void checkColumnIsValid(int column) const;
    void checkBindingName(int index) const;
    void setBindingParameterCount();
    void setColumnCount();
    bool isReadOnlyStatement() const;
    [[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const;
    [[noreturn]] void throwStatementHasError(const char *whatHasHappened) const;
    [[noreturn]] void throwStatementIsMisused(const char *whatHasHappened) const;
    [[noreturn]] void throwConstraintPreventsModification(const char *whatHasHappened) const;
    [[noreturn]] void throwNoValuesToFetch(const char *whatHasHappened) const;
    [[noreturn]] void throwInvalidColumnFetched(const char *whatHasHappened) const;
    [[noreturn]] void throwBindingIndexIsOutOfRange(const char *whatHasHappened) const;
    [[noreturn]] void throwWrongBingingName(const char *whatHasHappened) const;
    [[noreturn]] void throwUnknowError(const char *whatHasHappened) const;
    [[noreturn]] void throwBingingTooBig(const char *whatHasHappened) const;

    QString columnName(int column) const;

    Database &database() const;

private:
    std::unique_ptr<sqlite3_stmt, void (*)(sqlite3_stmt*)> m_compiledStatement;
    Database &m_database;
    int m_bindingParameterCount;
    int m_columnCount;
    mutable bool m_isReadyToFetchValues;
};

extern template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, int value);
extern template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, long value);
extern template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, long long value);
extern template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, double value);
extern template SQLITE_EXPORT void BaseStatement::bind(Utils::SmallStringView name, Utils::SmallStringView text);

template <> SQLITE_EXPORT int BaseStatement::fetchValue<int>(int column) const;
template <> SQLITE_EXPORT long BaseStatement::fetchValue<long>(int column) const;
template <> SQLITE_EXPORT long long BaseStatement::fetchValue<long long>(int column) const;
template <> SQLITE_EXPORT double BaseStatement::fetchValue<double>(int column) const;
144
extern template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(int column) const;
Marco Bubke's avatar
Marco Bubke committed
145 146 147 148 149 150 151 152 153 154
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>
class SQLITE_EXPORT StatementImplementation : public BaseStatement
{

public:
    using BaseStatement::BaseStatement;

Marco Bubke's avatar
Marco Bubke committed
155 156 157 158
    void bindValues()
    {
    }

Marco Bubke's avatar
Marco Bubke committed
159
    template<typename... ValueType>
160
    void bindValues(const ValueType&... values)
161 162 163 164
    {
        bindValuesByIndex(1, values...);
    }

Marco Bubke's avatar
Marco Bubke committed
165
    template<typename... ValueType>
166
    void write(const ValueType&... values)
167 168
    {
        bindValuesByIndex(1, values...);
Marco Bubke's avatar
Marco Bubke committed
169
        BaseStatement::execute();
170 171
    }

Marco Bubke's avatar
Marco Bubke committed
172
    template<typename... ValueType>
173
    void bindNameValues(const ValueType&... values)
174 175 176 177
    {
        bindValuesByName(values...);
    }

Marco Bubke's avatar
Marco Bubke committed
178
    template<typename... ValueType>
179
    void writeNamed(const ValueType&... values)
180 181
    {
        bindValuesByName(values...);
Marco Bubke's avatar
Marco Bubke committed
182
        BaseStatement::execute();
183 184
    }

Marco Bubke's avatar
Marco Bubke committed
185
    template <typename ResultType,
186 187
              int ResultTypeCount = 1>
    std::vector<ResultType> values(std::size_t reserveSize)
Marco Bubke's avatar
Marco Bubke committed
188
    {
Marco Bubke's avatar
Marco Bubke committed
189
        Resetter resetter{*this};
190
        std::vector<ResultType> resultValues;
Marco Bubke's avatar
Marco Bubke committed
191 192
        resultValues.reserve(reserveSize);

Marco Bubke's avatar
Marco Bubke committed
193
        while (BaseStatement::next())
194
           emplaceBackValues<ResultTypeCount>(resultValues);
Marco Bubke's avatar
Marco Bubke committed
195 196 197 198 199

        return resultValues;
    }

    template <typename ResultType,
200
              int ResultTypeCount = 1,
Marco Bubke's avatar
Marco Bubke committed
201
              typename... QueryTypes>
202
    std::vector<ResultType> values(std::size_t reserveSize, const QueryTypes&... queryValues)
Marco Bubke's avatar
Marco Bubke committed
203
    {
Marco Bubke's avatar
Marco Bubke committed
204
        Resetter resetter{*this};
205
        std::vector<ResultType> resultValues;
Marco Bubke's avatar
Marco Bubke committed
206 207 208 209
        resultValues.reserve(reserveSize);

        bindValues(queryValues...);

Marco Bubke's avatar
Marco Bubke committed
210
        while (BaseStatement::next())
211
           emplaceBackValues<ResultTypeCount>(resultValues);
Marco Bubke's avatar
Marco Bubke committed
212 213 214 215 216

        return resultValues;
    }

    template <typename ResultType,
217
              int ResultTypeCount = 1,
Marco Bubke's avatar
Marco Bubke committed
218
              typename QueryElementType>
219
    std::vector<ResultType> values(std::size_t reserveSize,
Marco Bubke's avatar
Marco Bubke committed
220
                                   const std::vector<QueryElementType> &queryValues)
Marco Bubke's avatar
Marco Bubke committed
221
    {
222
        std::vector<ResultType> resultValues;
Marco Bubke's avatar
Marco Bubke committed
223 224 225
        resultValues.reserve(reserveSize);

        for (const QueryElementType &queryValue : queryValues) {
Marco Bubke's avatar
Marco Bubke committed
226
            Resetter resetter{*this};
Marco Bubke's avatar
Marco Bubke committed
227 228
            bindValues(queryValue);

Marco Bubke's avatar
Marco Bubke committed
229
            while (BaseStatement::next())
230
                emplaceBackValues<ResultTypeCount>(resultValues);
Marco Bubke's avatar
Marco Bubke committed
231 232 233 234 235 236
        }

        return resultValues;
    }

    template <typename ResultType,
237
              int ResultTypeCount = 1,
Marco Bubke's avatar
Marco Bubke committed
238
              typename... QueryElementTypes>
239
    std::vector<ResultType> values(std::size_t reserveSize,
Marco Bubke's avatar
Marco Bubke committed
240
                                   const std::vector<std::tuple<QueryElementTypes...>> &queryTuples)
Marco Bubke's avatar
Marco Bubke committed
241 242 243 244 245 246
    {
        using Container = std::vector<ResultType>;
        Container resultValues;
        resultValues.reserve(reserveSize);

        for (const auto &queryTuple : queryTuples) {
Marco Bubke's avatar
Marco Bubke committed
247
            Resetter resetter{*this};
Marco Bubke's avatar
Marco Bubke committed
248 249
            bindTupleValues(queryTuple);

Marco Bubke's avatar
Marco Bubke committed
250
            while (BaseStatement::next())
251
                emplaceBackValues<ResultTypeCount>(resultValues);
Marco Bubke's avatar
Marco Bubke committed
252 253 254 255 256 257
        }

        return resultValues;
    }

    template <typename ResultType,
258
              int ResultTypeCount = 1,
Marco Bubke's avatar
Marco Bubke committed
259
              typename... QueryTypes>
Marco Bubke's avatar
Marco Bubke committed
260
    Utils::optional<ResultType> value(const QueryTypes&... queryValues)
Marco Bubke's avatar
Marco Bubke committed
261
    {
Marco Bubke's avatar
Marco Bubke committed
262
        Resetter resetter{*this};
263
        Utils::optional<ResultType> resultValue;
Marco Bubke's avatar
Marco Bubke committed
264 265 266

        bindValues(queryValues...);

Marco Bubke's avatar
Marco Bubke committed
267
        if (BaseStatement::next())
268
           resultValue = assignValue<Utils::optional<ResultType>, ResultTypeCount>();
Marco Bubke's avatar
Marco Bubke committed
269

270
        return resultValue;
Marco Bubke's avatar
Marco Bubke committed
271
    }
272 273

    template <typename Type>
Marco Bubke's avatar
Marco Bubke committed
274 275 276
    static Type toValue(Utils::SmallStringView sqlStatement, Database &database)
    {
        StatementImplementation statement(sqlStatement, database);
277

Marco Bubke's avatar
Marco Bubke committed
278
        statement.next();
279

Marco Bubke's avatar
Marco Bubke committed
280 281
        return statement.template fetchValue<Type>(0);
    }
282 283


Marco Bubke's avatar
Marco Bubke committed
284 285 286 287 288 289
private:
    struct Resetter
    {
        Resetter(StatementImplementation &statement)
            : statement(statement)
        {}
290

Marco Bubke's avatar
Marco Bubke committed
291 292 293 294
        ~Resetter()
        {
            statement.reset();
        }
295

Marco Bubke's avatar
Marco Bubke committed
296 297
        StatementImplementation &statement;
    };
298

Marco Bubke's avatar
Marco Bubke committed
299
    struct ValueGetter
300
    {
Marco Bubke's avatar
Marco Bubke committed
301
        ValueGetter(StatementImplementation &statement, int column)
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
            : statement(statement),
              column(column)
        {}

        operator int()
        {
            return statement.fetchIntValue(column);
        }

        operator long()
        {
            return statement.fetchLongValue(column);
        }

        operator long long()
        {
            return statement.fetchLongLongValue(column);
        }

        operator double()
        {
            return statement.fetchDoubleValue(column);
        }

326
        operator Utils::SmallStringView()
327
        {
328
            return statement.fetchSmallStringViewValue(column);
329 330
        }

Marco Bubke's avatar
Marco Bubke committed
331
        StatementImplementation &statement;
332 333 334
        int column;
    };

Marco Bubke's avatar
Marco Bubke committed
335 336
    template <typename ContainerType,
              int... ColumnIndices>
337
    void emplaceBackValues(ContainerType &container, std::integer_sequence<int, ColumnIndices...>)
Marco Bubke's avatar
Marco Bubke committed
338
    {
339
        container.emplace_back(ValueGetter(*this, ColumnIndices)...);
Marco Bubke's avatar
Marco Bubke committed
340 341
    }

342 343 344
    template <int ResultTypeCount,
              typename ContainerType>
    void emplaceBackValues(ContainerType &container)
Marco Bubke's avatar
Marco Bubke committed
345
    {
346
        emplaceBackValues(container, std::make_integer_sequence<int, ResultTypeCount>{});
Marco Bubke's avatar
Marco Bubke committed
347 348
    }

349
    template <typename ResultOptionalType,
Marco Bubke's avatar
Marco Bubke committed
350
              int... ColumnIndices>
351
    ResultOptionalType assignValue(std::integer_sequence<int, ColumnIndices...>)
Marco Bubke's avatar
Marco Bubke committed
352
    {
353
        return ResultOptionalType(Utils::in_place, ValueGetter(*this, ColumnIndices)...);
Marco Bubke's avatar
Marco Bubke committed
354 355
    }

356 357 358
    template <typename ResultOptionalType,
              int ResultTypeCount>
    ResultOptionalType assignValue()
Marco Bubke's avatar
Marco Bubke committed
359
    {
360
        return assignValue<ResultOptionalType>(std::make_integer_sequence<int, ResultTypeCount>{});
Marco Bubke's avatar
Marco Bubke committed
361 362
    }

Marco Bubke's avatar
Marco Bubke committed
363
    template<typename ValueType>
364
    void bindValuesByIndex(int index, const ValueType &value)
365
    {
Marco Bubke's avatar
Marco Bubke committed
366
        BaseStatement::bind(index, value);
367 368
    }

Marco Bubke's avatar
Marco Bubke committed
369
    template<typename ValueType, typename... ValueTypes>
370
    void bindValuesByIndex(int index, const ValueType &value, const ValueTypes&... values)
371
    {
Marco Bubke's avatar
Marco Bubke committed
372
        BaseStatement::bind(index, value);
373 374 375
        bindValuesByIndex(index + 1, values...);
    }

Marco Bubke's avatar
Marco Bubke committed
376
    template<typename ValueType>
377
    void bindValuesByName(Utils::SmallStringView name, const ValueType &value)
378
    {
Marco Bubke's avatar
Marco Bubke committed
379
       BaseStatement::bind(BaseStatement::bindingIndexForName(name), value);
380 381
    }

Marco Bubke's avatar
Marco Bubke committed
382
    template<typename ValueType, typename... ValueTypes>
383
    void bindValuesByName(Utils::SmallStringView name, const ValueType &value, const ValueTypes&... values)
384
    {
Marco Bubke's avatar
Marco Bubke committed
385
       BaseStatement::bind(BaseStatement::bindingIndexForName(name), value);
386 387 388
       bindValuesByName(values...);
    }

Marco Bubke's avatar
Marco Bubke committed
389 390
    template <typename TupleType, std::size_t... ColumnIndices>
    void bindTupleValuesElement(const TupleType &tuple, std::index_sequence<ColumnIndices...>)
Marco Bubke's avatar
Marco Bubke committed
391
    {
Marco Bubke's avatar
Marco Bubke committed
392
        bindValues(std::get<ColumnIndices>(tuple)...);
Marco Bubke's avatar
Marco Bubke committed
393 394 395
    }

    template <typename TupleType,
Marco Bubke's avatar
Marco Bubke committed
396
              typename ColumnIndices = std::make_index_sequence<std::tuple_size<TupleType>::value>>
Marco Bubke's avatar
Marco Bubke committed
397 398
    void bindTupleValues(const TupleType &element)
    {
Marco Bubke's avatar
Marco Bubke committed
399
        bindTupleValuesElement(element, ColumnIndices());
Marco Bubke's avatar
Marco Bubke committed
400 401
    }

402
};
Marco Bubke's avatar
Marco Bubke committed
403 404

} // namespace Sqlite