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

Marco Bubke's avatar
Marco Bubke committed
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 {

Marco Bubke's avatar
Marco Bubke committed
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;

Marco Bubke's avatar
Marco Bubke committed
63 64 65 66 67 68
    int fetchIntValue(int column) const;
    long fetchLongValue(int column) const;
    long long fetchLongLongValue(int column) const;
    double fetchDoubleValue(int column) const;
    Utils::SmallString fetchSmallStringValue(int column) const;
    Utils::PathString fetchPathStringValue(int column) const;
69
    template<typename Type>
Marco Bubke's avatar
Marco Bubke committed
70
    Type fetchValue(int column) const;
71
    int columnCount() const;
72
    Utils::SmallStringVector columnNames() const;
73

Marco Bubke's avatar
Marco Bubke committed
74 75 76 77
    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);
78

Marco Bubke's avatar
Marco Bubke committed
79 80 81 82 83 84 85 86 87 88
    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
89 90 91 92 93 94 95 96 97 98 99 100 101 102 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 144 145 146 147 148 149 150 151 152 153
    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();

    bool checkForStepError(int resultCode) const;
    void checkForPrepareError(int resultCode) const;
    void checkForBindingError(int resultCode) const;
    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;
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
154 155 156 157
    void bindValues()
    {
    }

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

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

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

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

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

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

        return resultValues;
    }

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

        bindValues(queryValues...);

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

        return resultValues;
    }

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

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

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

        return resultValues;
    }

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

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

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

        return resultValues;
    }

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

        bindValues(queryValues...);

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

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

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

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

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


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

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

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

Marco Bubke's avatar
Marco Bubke committed
298
    struct ValueGetter
Marco Bubke's avatar
Marco Bubke committed
299
    {
Marco Bubke's avatar
Marco Bubke committed
300
        ValueGetter(StatementImplementation &statement, int column)
Marco Bubke's avatar
Marco Bubke committed
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
            : 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);
        }

        operator  Utils::SmallString()
        {
            return statement.fetchSmallStringValue(column);
        }

        operator  Utils::PathString()
        {
            return statement.fetchPathStringValue(column);
        }

Marco Bubke's avatar
Marco Bubke committed
335
        StatementImplementation &statement;
Marco Bubke's avatar
Marco Bubke committed
336 337 338
        int column;
    };

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

Marco Bubke's avatar
Marco Bubke committed
346 347 348
    template <int ResultTypeCount,
              typename ContainerType>
    void emplaceBackValues(ContainerType &container)
Marco Bubke's avatar
Marco Bubke committed
349
    {
Marco Bubke's avatar
Marco Bubke committed
350
        emplaceBackValues(container, std::make_integer_sequence<int, ResultTypeCount>{});
Marco Bubke's avatar
Marco Bubke committed
351 352
    }

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

Marco Bubke's avatar
Marco Bubke committed
360 361 362
    template <typename ResultOptionalType,
              int ResultTypeCount>
    ResultOptionalType assignValue()
Marco Bubke's avatar
Marco Bubke committed
363
    {
Marco Bubke's avatar
Marco Bubke committed
364
        return assignValue<ResultOptionalType>(std::make_integer_sequence<int, ResultTypeCount>{});
Marco Bubke's avatar
Marco Bubke committed
365 366
    }

Marco Bubke's avatar
Marco Bubke committed
367
    template<typename ValueType>
368
    void bindValuesByIndex(int index, const ValueType &value)
369
    {
Marco Bubke's avatar
Marco Bubke committed
370
        BaseStatement::bind(index, value);
371 372
    }

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

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

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

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

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

406
};
Marco Bubke's avatar
Marco Bubke committed
407 408

} // namespace Sqlite