Coverage Report

Created: 2023-12-13 14:58

test/zserio/SqliteConnectionTest.cpp
Line
Count
Source
1
#include "zserio/SqliteConnection.h"
2
#include "zserio/SqliteException.h"
3
4
#include <string>
5
#include <vector>
6
#include <algorithm>
7
8
#include "sqlite3.h"
9
10
#include "gtest/gtest.h"
11
12
namespace zserio
13
{
14
15
namespace
16
{
17
    extern "C" int sqliteResultAccumulatorCallback(void *data, int nColumns, char** colValues, char** colNames);
18
19
    class SqliteResultAccumulator
20
    {
21
    public:
22
        using TRow = std::vector<std::string>;
23
        using TResult = std::vector<TRow>;
24
25
        TResult const& getResult() const
26
2
        {
27
2
            return result;
28
2
        }
29
30
        int callback(size_t nColumns, char** colValues, char**)
31
2
        {
32
2
            auto colValuesSpan = Span<char*>(colValues, nColumns);
33
2
            TRow row;
34
2
            row.reserve(nColumns);
35
2
            for (const char* colValue : colValuesSpan)
36
2
                row.emplace_back(colValue);
37
2
            result.push_back(row);
38
2
            return 0; // continue
39
2
        }
40
41
        TResult result;
42
    };
43
44
    int sqliteResultAccumulatorCallback(void *data, int nColumns, char** colValues, char** colNames)
45
2
    {
46
2
        SqliteResultAccumulator *self = static_cast<SqliteResultAccumulator*>(data);
47
2
        return self->callback(static_cast<size_t>(nColumns), colValues, colNames);
48
2
    }
49
} // namespace
50
51
static const char* const SQLITE3_MEM_DB = ":memory:";
52
53
TEST(SqliteConnectionTest, emptyConstructor)
54
1
{
55
1
    SqliteConnection db;
56
1
    ASSERT_EQ(nullptr, db.getConnection());
57
1
}
58
59
TEST(SqliteConnectionTest, externalConstructor)
60
1
{
61
1
    sqlite3 *externalConnection = nullptr;
62
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &externalConnection);
63
1
    ASSERT_EQ(SQLITE_OK, result);
64
65
1
    SqliteConnection db(externalConnection, SqliteConnection::EXTERNAL_CONNECTION);
66
1
    ASSERT_EQ(externalConnection, db.getConnection());
67
1
    ASSERT_EQ(SqliteConnection::EXTERNAL_CONNECTION, db.getConnectionType());
68
69
1
    db.reset();
70
71
1
    result = sqlite3_close(externalConnection);
72
1
    ASSERT_EQ(SQLITE_OK, result);
73
74
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
75
1
}
76
77
TEST(SqliteConnectionTest, internalConstructor)
78
1
{
79
1
    sqlite3* internalConnection = nullptr;
80
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
81
1
    ASSERT_EQ(SQLITE_OK, result);
82
83
1
    SqliteConnection db(internalConnection, SqliteConnection::INTERNAL_CONNECTION);
84
1
    ASSERT_EQ(internalConnection, db.getConnection());
85
1
    ASSERT_EQ(SqliteConnection::INTERNAL_CONNECTION, db.getConnectionType());
86
87
1
    db.reset();
88
1
    ASSERT_EQ(nullptr, db.getConnection());
89
90
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
91
1
}
92
93
TEST(SqliteConnectionTest, defaultInternalConstructor)
94
1
{
95
1
    sqlite3* internalConnection = nullptr;
96
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
97
1
    ASSERT_EQ(SQLITE_OK, result);
98
99
1
    SqliteConnection db(internalConnection, SqliteConnection::INTERNAL_CONNECTION);
100
1
    ASSERT_EQ(internalConnection, db.getConnection());
101
1
    ASSERT_EQ(SqliteConnection::INTERNAL_CONNECTION, db.getConnectionType());
102
103
1
    db.reset();
104
1
    ASSERT_EQ(nullptr, db.getConnection());
105
106
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
107
1
}
108
109
TEST(SqliteConnectionTest, resetExternal)
110
1
{
111
1
    sqlite3 *externalConnection = nullptr;
112
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &externalConnection);
113
1
    ASSERT_EQ(SQLITE_OK, result);
114
115
1
    SqliteConnection db;
116
1
    db.reset(externalConnection, SqliteConnection::EXTERNAL_CONNECTION);
117
1
    ASSERT_EQ(externalConnection, db.getConnection());
118
1
    ASSERT_EQ(SqliteConnection::EXTERNAL_CONNECTION, db.getConnectionType());
119
120
1
    db.reset();
121
1
    ASSERT_EQ(nullptr, db.getConnection());
122
123
1
    result = sqlite3_close(externalConnection);
124
1
    ASSERT_EQ(SQLITE_OK, result);
125
126
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
127
1
}
128
129
TEST(SqliteConnectionTest, resetInternal)
130
1
{
131
1
    sqlite3* internalConnection = nullptr;
132
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
133
1
    ASSERT_EQ(SQLITE_OK, result);
134
135
1
    SqliteConnection db;
136
1
    db.reset(internalConnection, SqliteConnection::INTERNAL_CONNECTION);
137
1
    ASSERT_EQ(internalConnection, db.getConnection());
138
1
    ASSERT_EQ(SqliteConnection::INTERNAL_CONNECTION, db.getConnectionType());
139
140
1
    db.reset();
141
1
    ASSERT_EQ(nullptr, db.getConnection());
142
143
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
144
1
}
145
146
TEST(SqliteConnectionTest, resetDefaultInternal)
147
1
{
148
1
    sqlite3* internalConnection = nullptr;
149
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
150
1
    ASSERT_EQ(SQLITE_OK, result);
151
152
1
    SqliteConnection db;
153
1
    db.reset(internalConnection);
154
1
    ASSERT_EQ(internalConnection, db.getConnection());
155
1
    ASSERT_EQ(SqliteConnection::INTERNAL_CONNECTION, db.getConnectionType());
156
157
1
    db.reset();
158
1
    ASSERT_EQ(nullptr, db.getConnection());
159
160
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
161
1
}
162
163
TEST(SqliteConnectionTest, doubleResetExternal)
164
1
{
165
1
    sqlite3 *externalConnection1 = nullptr;
166
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &externalConnection1);
167
1
    ASSERT_EQ(SQLITE_OK, result);
168
169
1
    sqlite3 *externalConnection2 = nullptr;
170
1
    result = sqlite3_open(SQLITE3_MEM_DB, &externalConnection2);
171
1
    ASSERT_EQ(SQLITE_OK, result);
172
173
1
    {
174
1
        SqliteConnection db;
175
1
        db.reset(externalConnection1, SqliteConnection::EXTERNAL_CONNECTION);
176
1
        ASSERT_EQ(externalConnection1, db.getConnection());
177
1
        ASSERT_EQ(SqliteConnection::EXTERNAL_CONNECTION, db.getConnectionType());
178
179
1
        db.reset(externalConnection2, SqliteConnection::EXTERNAL_CONNECTION);
180
1
        ASSERT_EQ(externalConnection2, db.getConnection());
181
1
        ASSERT_EQ(SqliteConnection::EXTERNAL_CONNECTION, db.getConnectionType());
182
1
    } // db dtor
183
184
1
    result = sqlite3_close(externalConnection1);
185
1
    ASSERT_EQ(SQLITE_OK, result);
186
187
1
    result = sqlite3_close(externalConnection2);
188
1
    ASSERT_EQ(SQLITE_OK, result);
189
190
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
191
1
}
192
193
TEST(SqliteConnectionTest, doubleResetInternal)
194
1
{
195
1
    sqlite3 *internalConnection1 = nullptr;
196
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection1);
197
1
    ASSERT_EQ(SQLITE_OK, result);
198
199
1
    sqlite3 *internalConnection2 = nullptr;
200
1
    result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection2);
201
1
    ASSERT_EQ(SQLITE_OK, result);
202
203
1
    {
204
1
        SqliteConnection db;
205
1
        db.reset(internalConnection1);
206
1
        ASSERT_EQ(internalConnection1, db.getConnection());
207
1
        ASSERT_EQ(SqliteConnection::INTERNAL_CONNECTION, db.getConnectionType());
208
209
1
        db.reset(internalConnection2);
210
1
        ASSERT_EQ(internalConnection2, db.getConnection());
211
1
        ASSERT_EQ(SqliteConnection::INTERNAL_CONNECTION, db.getConnectionType());
212
1
    } // db dtor
213
214
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
215
1
}
216
217
TEST(SqliteConnectionTest, getConnection)
218
1
{
219
1
    sqlite3 *internalConnection = nullptr;
220
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
221
1
    ASSERT_EQ(SQLITE_OK, result);
222
223
1
    SqliteConnection db(internalConnection);
224
1
    ASSERT_EQ(internalConnection, db.getConnection());
225
226
1
    db.reset();
227
1
    ASSERT_EQ(nullptr, db.getConnection());
228
229
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
230
1
}
231
232
TEST(SqliteConnectionTest, getConnectionType)
233
1
{
234
1
    sqlite3 *internalConnection = nullptr;
235
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
236
1
    ASSERT_EQ(SQLITE_OK, result);
237
238
1
    SqliteConnection db(internalConnection);
239
1
    ASSERT_EQ(SqliteConnection::INTERNAL_CONNECTION, db.getConnectionType());
240
241
1
    sqlite3 *externalConnection = nullptr;
242
1
    result = sqlite3_open(SQLITE3_MEM_DB, &externalConnection);
243
1
    ASSERT_EQ(SQLITE_OK, result);
244
245
1
    db.reset(externalConnection, SqliteConnection::EXTERNAL_CONNECTION);
246
1
    ASSERT_EQ(SqliteConnection::EXTERNAL_CONNECTION, db.getConnectionType());
247
248
1
    ASSERT_EQ(SQLITE_OK, sqlite3_close(externalConnection));
249
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
250
1
}
251
252
TEST(SqliteConnectionTest, reset)
253
1
{
254
1
    sqlite3 *internalConnection = nullptr;
255
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
256
1
    ASSERT_EQ(SQLITE_OK, result);
257
258
1
    SqliteConnection db;
259
1
    db.reset(internalConnection);
260
1
    ASSERT_EQ(internalConnection, db.getConnection());
261
262
1
    db.reset();
263
1
    ASSERT_EQ(nullptr, db.getConnection());
264
265
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
266
1
}
267
268
TEST(SqliteConnectionTest, executeUpdate)
269
1
{
270
1
    sqlite3 *internalConnection = nullptr;
271
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
272
1
    ASSERT_EQ(SQLITE_OK, result);
273
274
1
    SqliteConnection db(internalConnection);
275
276
1
    const std::string query("CREATE TABLE Foo AS SELECT 1"); // check that generic string version works
277
1
    db.executeUpdate(query);
278
279
1
    sqlite3* const dbConnection = db.getConnection();
280
1
    SqliteResultAccumulator resultAcc;
281
1
    sqlite3_exec(dbConnection, "SELECT * FROM Foo", sqliteResultAccumulatorCallback, &resultAcc, nullptr);
282
283
1
    SqliteResultAccumulator::TResult const& accResult = resultAcc.getResult();
284
1
    ASSERT_EQ(1, accResult.size());
285
1
    SqliteResultAccumulator::TRow const& row = accResult.front();
286
1
    ASSERT_EQ(1, row.size());
287
1
    ASSERT_EQ("1", row.front());
288
289
1
    db.reset();
290
291
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
292
1
}
293
294
TEST(SqliteConnectionTest, executeUpdateOnReadOnlyDatabase)
295
1
{
296
1
    sqlite3 *internalConnection = nullptr;
297
1
    int result = sqlite3_open_v2(SQLITE3_MEM_DB, &internalConnection, SQLITE_OPEN_READONLY, nullptr);
298
1
    SqliteConnection db(internalConnection);
299
1
    ASSERT_EQ(SQLITE_OK, result);
300
1
    ASSERT_THROW(db.executeUpdate("CREATE TABLE Foo AS SELECT 1"), SqliteException);
301
1
}
302
303
TEST(SqliteConnectionTest, prepareStatement)
304
1
{
305
1
    sqlite3 *internalConnection = nullptr;
306
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
307
1
    ASSERT_EQ(SQLITE_OK, result);
308
309
1
    SqliteConnection db(internalConnection);
310
1
    db.executeUpdate("CREATE TABLE Foo AS SELECT 1");
311
312
1
    sqlite3_stmt* const statement = db.prepareStatement("SELECT 1");
313
1
    ASSERT_TRUE(statement != nullptr);
314
315
1
    result = sqlite3_step(statement);
316
1
    ASSERT_EQ(SQLITE_ROW, result);
317
318
1
    ASSERT_EQ(1, sqlite3_column_count(statement));
319
1
    const std::string resultString(reinterpret_cast<const char*>(sqlite3_column_text(statement, 0)));
320
1
    ASSERT_EQ("1", resultString);
321
322
1
    result = sqlite3_step(statement);
323
1
    ASSERT_EQ(SQLITE_DONE, result);
324
325
1
    result = sqlite3_finalize(statement);
326
1
    ASSERT_EQ(SQLITE_OK, result);
327
328
1
    ASSERT_THROW(db.prepareStatement("SOME RUBBISH"), SqliteException);
329
330
1
    db.reset();
331
332
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
333
1
}
334
335
TEST(SqliteConnectionTest, startEndTransaction)
336
1
{
337
1
    sqlite3 *internalConnection = nullptr;
338
1
    int result = sqlite3_open(SQLITE3_MEM_DB, &internalConnection);
339
1
    ASSERT_EQ(SQLITE_OK, result);
340
341
1
    SqliteConnection db(internalConnection);
342
343
1
    const bool wasTransactionStarted = db.startTransaction();
344
1
    const std::string query("CREATE TABLE Foo AS SELECT 1"); // check that generic string version works
345
1
    db.executeUpdate(query);
346
1
    db.endTransaction(wasTransactionStarted);
347
348
1
    sqlite3* const dbConnection = db.getConnection();
349
1
    SqliteResultAccumulator resultAcc;
350
1
    sqlite3_exec(dbConnection, "SELECT * FROM Foo", sqliteResultAccumulatorCallback, &resultAcc, nullptr);
351
352
1
    SqliteResultAccumulator::TResult const& accResult = resultAcc.getResult();
353
1
    ASSERT_EQ(1, accResult.size());
354
1
    SqliteResultAccumulator::TRow const& row = accResult.front();
355
1
    ASSERT_EQ(1, row.size());
356
1
    ASSERT_EQ("1", row.front());
357
358
1
    db.executeUpdate("BEGIN");
359
1
    const bool transactionNotStarted = db.startTransaction();
360
1
    ASSERT_FALSE(transactionNotStarted);
361
1
    db.endTransaction(transactionNotStarted);
362
363
1
    db.reset();
364
1
    ASSERT_EQ(SQLITE_OK, sqlite3_shutdown());
365
1
}
366
367
} // namespace zserio