Coverage Report

Created: 2024-07-18 11:41

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