GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/zserio/SqliteConnection.h Lines: 39 39 100.0 %
Date: 2023-12-13 14:51:09 Branches: 19 28 67.9 %

Line Branch Exec Source
1
#ifndef ZSERIO_SQL_CONNECTION_H_INC
2
#define ZSERIO_SQL_CONNECTION_H_INC
3
4
#include "sqlite3.h"
5
6
#include <memory>
7
8
#include "zserio/StringView.h"
9
#include "zserio/SqliteException.h"
10
#include "zserio/SqliteFinalizer.h"
11
12
namespace zserio
13
{
14
15
/**
16
 * Helper class to keep sqlite3 connection and ensure its safe destruction.
17
 *
18
 * The class also provides simple interface to execute SQLite queries.
19
 */
20
class SqliteConnection
21
{
22
public:
23
    /**
24
     * Connection type.
25
     */
26
    enum ConnectionType
27
    {
28
        INTERNAL_CONNECTION, /**< Internal connection which must be released in destructor. */
29
        EXTERNAL_CONNECTION /**< External connection managed from outside. */
30
    };
31
32
    /**
33
     * Constructor.
34
     *
35
     * \param connection Pointer to the SQLite connection.
36
     * \param connectionType Type of the connection. Default is INTERNAL_CONNECTION.
37
     */
38
    explicit SqliteConnection(sqlite3* connection = nullptr,
39
            ConnectionType connectionType = INTERNAL_CONNECTION);
40
41
    /**
42
     * Destructor.
43
     */
44
    ~SqliteConnection();
45
46
    /**
47
     * Copying and moving is disallowed!
48
     * \{
49
     */
50
    SqliteConnection(const SqliteConnection&) = delete;
51
    SqliteConnection& operator=(const SqliteConnection&) = delete;
52
53
    SqliteConnection(SqliteConnection&&) = delete;
54
    SqliteConnection& operator=(SqliteConnection&&) = delete;
55
    /** \} */
56
57
    /**
58
     * Resets the connection.
59
     *
60
     * \param connection New connection to set. Default is NULL - i.e. unset.
61
     * \param connectionType Type of the new connection.
62
     */
63
    void reset(sqlite3* connection = nullptr, ConnectionType connectionType = INTERNAL_CONNECTION);
64
65
    /**
66
     * Gets the current connection type.
67
     *
68
     * When connection is NULL, the connection type is insignificant.
69
     *
70
     * \return Connection type.
71
     */
72
    ConnectionType getConnectionType() const;
73
74
    /**
75
     * Gets the current connection.
76
     *
77
     * \return SQLite connection.
78
     */
79
    sqlite3* getConnection();
80
81
    /**
82
     * Executes a single query which doesn't need to return anything - e.g. DML.
83
     *
84
     * \param sqlQuery The query string.
85
     */
86
    void executeUpdate(StringView sqlQuery);
87
88
    /**
89
     * Prepares the SQLite statement for the given query.
90
     *
91
     * Note that the user is responsible to proper statement finalization using sqlite3_finalize!
92
     *
93
     * \param sqlQuery The query string.
94
     *
95
     * \return Prepared SQLite statement.
96
     */
97
    sqlite3_stmt* prepareStatement(StringView sqlQuery);
98
99
    /**
100
     * Starts a new transaction if a transaction is not already started.
101
     *
102
     * \return True when the new transaction was started. False when a transaction is already started.
103
     */
104
    bool startTransaction();
105
106
    /**
107
     * Terminates the current transaction.
108
     *
109
     * The parameter wasTransactionStarted is used for convenience since it's then easier to write code
110
     * which uses transactions.
111
     *
112
     * \code{.cpp}
113
     * bool wasTransactionStarted = connection.startTransaction(); // transaction may be already started
114
     * // execute queries
115
     * // ...
116
     * // terminates the transaction only if it was started by the corresponding startTransaction call.
117
     * connection.endTransaction(wasTransactionStarted);
118
     * \endcode
119
     *
120
     * \param wasTransactionStarted When false, the call does actually nothing.
121
     */
122
    void endTransaction(bool wasTransactionStarted);
123
124
private:
125
    sqlite3* m_connection;
126
    ConnectionType m_connectionType;
127
};
128
129
20
inline SqliteConnection::SqliteConnection(sqlite3* connection, ConnectionType connectionType)
130
20
:   m_connection(connection), m_connectionType(connectionType)
131
20
{}
132
133
40
inline SqliteConnection::~SqliteConnection()
134
{
135
20
    reset();
136
20
}
137
138
40
inline void SqliteConnection::reset(sqlite3* connection, ConnectionType connectionType)
139
{
140
    // close connection only if it is internal
141
40
    if (m_connectionType == INTERNAL_CONNECTION)
142
35
        sqlite3_close_v2(m_connection); // sqlite3_close_v2(NULL) is a harmless no-op
143
144
40
    m_connection = connection;
145
40
    m_connectionType = connectionType;
146
40
}
147
148
12
inline SqliteConnection::ConnectionType SqliteConnection::getConnectionType() const
149
{
150
12
    return m_connectionType;
151
}
152
153
22
inline sqlite3* SqliteConnection::getConnection()
154
{
155
22
    return m_connection;
156
}
157
158
17
inline void SqliteConnection::executeUpdate(StringView sqlQuery)
159
{
160
34
    std::unique_ptr<sqlite3_stmt, SqliteFinalizer> statement(prepareStatement(sqlQuery));
161
17
    int result = sqlite3_step(statement.get());
162
17
    if (result != SQLITE_DONE)
163
    {
164
        throw SqliteException("SqliteConnection::executeUpdate(): sqlite3_step failed: ") <<
165

1
                SqliteErrorCode(result);
166
    }
167
16
}
168
169
42
inline sqlite3_stmt* SqliteConnection::prepareStatement(StringView sqlQuery)
170
{
171
42
    sqlite3_stmt* statement = nullptr;
172
42
    const int result = sqlite3_prepare_v2(m_connection, sqlQuery.data(), static_cast<int>(sqlQuery.size()),
173
42
            &statement, nullptr);
174
42
    if (result != SQLITE_OK)
175
    {
176
        throw SqliteException("SqliteConnection::prepareStatement(): sqlite3_prepare_v2() failed: ") <<
177

7
                SqliteErrorCode(result);
178
    }
179
180
35
    return statement;
181
}
182
183
2
inline bool SqliteConnection::startTransaction()
184
{
185
2
    bool wasTransactionStarted = false;
186
2
    if (sqlite3_get_autocommit(m_connection) != 0)
187
    {
188
1
        executeUpdate("BEGIN;");
189
1
        wasTransactionStarted = true;
190
    }
191
192
2
    return wasTransactionStarted;
193
}
194
195
2
inline void SqliteConnection::endTransaction(bool wasTransactionStarted)
196
{
197
2
    if (wasTransactionStarted)
198
1
        executeUpdate("COMMIT;");
199
2
}
200
201
} // namespace zserio
202
203
#endif // ZSERIO_SQL_CONNECTION_H_INC