Browse Source

Refactoring and update.

master
Robin Krahl 4 years ago
parent
commit
56b56f46f1

+ 20
- 0
.travis.yml View File

@@ -0,0 +1,20 @@
1
+language: cpp
2
+
3
+sudo: false
4
+
5
+compiler:
6
+  - gcc
7
+  - clang
8
+
9
+addons:
10
+  apt:
11
+    packages:
12
+      - libsqlite3-dev
13
+      - libgtest-dev
14
+
15
+script:
16
+  - mkdir bin && cd bin
17
+  - cmake ..
18
+  - make
19
+  - make test
20
+  - make check

+ 29
- 14
CMakeLists.txt View File

@@ -1,32 +1,47 @@
1
-cmake_minimum_required(VERSION 2.8)
1
+cmake_minimum_required(VERSION 3.0)
2 2
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/")
3
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
3
+add_definitions(-std=c++11)
4 4
 
5 5
 project(sqlitepp)
6
+enable_testing()
6 7
 
7
-set(SOURCES src/sqlitepp.cpp)
8
+set(SOURCES src/sqlitepp/sqlitepp.cc)
9
+set(TEST_SOURCES src/sqlitepp/sqlitepp_test.cc)
10
+set(LINT_FILES include/sqlitepp/sqlitepp.h ${SOURCES} ${TEST_SOURCES})
8 11
 set(INCLUDES include)
9 12
 
13
+include(StyleCheck)
14
+
10 15
 include_directories(${INCLUDES})
11 16
 
12 17
 add_library(sqlitepp ${SOURCES})
13
-add_executable(sqlitepptest src/sqlitepptest.cpp)
14
-
15
-add_custom_target(check COMMAND sqlitepptest)
18
+add_executable(sqlitepp_test ${TEST_SOURCES})
16 19
 
17
-set(Boost_USE_MULTITHREADED OFF)
18
-find_package(Boost 1.54.0 REQUIRED COMPONENTS unit_test_framework)
20
+find_package(Doxygen)
21
+find_package(GTest REQUIRED)
19 22
 find_package(Sqlite3 REQUIRED)
20 23
 
21 24
 set(DEP_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIRS})
22 25
 set(DEP_LIBRARIES ${SQLITE3_LIBRARIES})
23
-set(TEST_INCLUDE_DIRS ${Boost_UNIT_TEST_FRAMEWORK_INCLUDE_DIRS})
24
-set(TEST_LIBRARIES ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES} sqlitepp)
26
+set(TEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIRS})
27
+set(TEST_LIBRARIES ${GTEST_BOTH_LIBRARIES} pthread sqlitepp)
25 28
 
26 29
 include_directories(${DEP_INCLUDE_DIRS})
27 30
 include_directories(${TEST_INCLUDE_DIRS})
28 31
 target_link_libraries(sqlitepp ${DEP_LIBRARIES})
29
-target_link_libraries(sqlitepptest ${TEST_LIBRARIES})
30
-
31
-# add_subdirectory(tests)
32
-
32
+target_link_libraries(sqlitepp_test ${TEST_LIBRARIES})
33
+
34
+set(GTEST_ARGS "")
35
+gtest_add_tests(sqlitepp_test "${GTEST_ARGS}" ${TEST_SOURCES})
36
+
37
+add_style_check_target(check "${LINT_FILES}")
38
+
39
+if(DOXYGEN_FOUND)
40
+  configure_file(${CMAKE_MODULE_PATH}/sqlitepp.doxyfile
41
+    ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
42
+  add_custom_target(doc ${DOXYGEN_EXECUTABLE}
43
+    ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
44
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
45
+    COMMENT "Generating API documentation with Doxygen"
46
+    VERBATIM)
47
+endif(DOXYGEN_FOUND)

+ 53
- 0
CMakeModules/StyleCheck.cmake View File

@@ -0,0 +1,53 @@
1
+
2
+# Copyright (C) 2013 Daniel Scharrer
3
+#
4
+# This software is provided 'as-is', without any express or implied
5
+# warranty.  In no event will the author(s) be held liable for any damages
6
+# arising from the use of this software.
7
+#
8
+# Permission is granted to anyone to use this software for any purpose,
9
+# including commercial applications, and to alter it and redistribute it
10
+# freely, subject to the following restrictions:
11
+#
12
+# 1. The origin of this software must not be misrepresented; you must not
13
+#    claim that you wrote the original software. If you use this software
14
+#    in a product, an acknowledgment in the product documentation would be
15
+#    appreciated but is not required.
16
+# 2. Altered source versions must be plainly marked as such, and must not be
17
+#    misrepresented as being the original software.
18
+# 3. This notice may not be removed or altered from any source distribution.
19
+#
20
+#
21
+# Modified 2015 by Robin Krahl -- removed "--filter" and "--project", added
22
+# "--root"
23
+# Original source: https://github.com/dscharrer/innoextract/blob/master/cmake/StyleCheck.cmake
24
+
25
+find_package(PythonInterp)
26
+
27
+# Add a target that runs cpplint.py
28
+#
29
+# Parameters:
30
+# - TARGET_NAME the name of the target to add
31
+# - SOURCES_LIST a complete list of source and include files to check
32
+function(add_style_check_target TARGET_NAME SOURCES_LIST)
33
+	
34
+	if(NOT PYTHONINTERP_FOUND)
35
+		return()
36
+	endif()
37
+	
38
+	list(SORT SOURCES_LIST)
39
+	list(REMOVE_DUPLICATES SOURCES_LIST)
40
+	
41
+	add_custom_target(${TARGET_NAME}
42
+		COMMAND "${CMAKE_COMMAND}" -E chdir
43
+			"${CMAKE_SOURCE_DIR}"
44
+			"${PYTHON_EXECUTABLE}"
45
+			"${CMAKE_MODULE_PATH}/cpplint.py"
46
+      "--root=include"
47
+			${SOURCES_LIST}
48
+		DEPENDS ${SOURCES_LIST}
49
+		COMMENT "Checking code style."
50
+		VERBATIM
51
+	)
52
+	
53
+endfunction(add_style_check_target)

+ 6323
- 0
CMakeModules/cpplint.py
File diff suppressed because it is too large
View File


+ 2385
- 0
CMakeModules/sqlitepp.doxyfile
File diff suppressed because it is too large
View File


+ 1
- 1
LICENSE View File

@@ -1,6 +1,6 @@
1 1
 The MIT License (MIT)
2 2
 
3
-Copyright (c) 2014 Robin Krahl
3
+Copyright (c) 2014--2015 Robin Krahl
4 4
 
5 5
 Permission is hereby granted, free of charge, to any person obtaining a copy
6 6
 of this software and associated documentation files (the "Software"), to deal

+ 0
- 91
include/sqlitepp.h View File

@@ -1,91 +0,0 @@
1
-/*
2
- * (C) 2014 Robin Krahl
3
- * MIT license -- http://opensource.org/licenses/MIT
4
- */
5
-
6
-#ifndef __SQLITEPP_H
7
-#define __SQLITEPP_H
8
-
9
-#include <memory>
10
-#include <string>
11
-
12
-#include <boost/noncopyable.hpp>
13
-#include <sqlite3.h>
14
-
15
-namespace sqlitepp {
16
-    class Openable {
17
-    public:
18
-        const bool isOpen() const;
19
-
20
-    protected:
21
-        Openable(const bool open, const std::string & name);
22
-
23
-        void requireOpen() const;
24
-        void setOpen(const bool open);
25
-
26
-    private:
27
-        bool m_open;
28
-        const std::string & m_name;
29
-    };
30
-
31
-    class DatabaseError : public std::runtime_error {
32
-    public:
33
-        DatabaseError(const int errorCode);
34
-        DatabaseError(const int errorCode, const std::string & errorMessage);
35
-
36
-        const int errorCode() const;
37
-    private:
38
-        const int m_errorCode;
39
-
40
-        static const std::string getErrorMessage(const int errorCode, const std::string & errorMessage);
41
-    };
42
-
43
-    class Statement;
44
-
45
-    class Database : private boost::noncopyable, public Openable {
46
-        friend class Statement;
47
-    public:
48
-        Database();
49
-        Database(const std::string & file);
50
-        ~Database();
51
-
52
-        void close();
53
-        void execute(const std::string & sql);
54
-        void open(const std::string & file);
55
-        std::shared_ptr<Statement> prepare(const std::string & sql);
56
-
57
-    private:
58
-        sqlite3 * m_handle;
59
-    };
60
-
61
-    class Statement : private boost::noncopyable, public Openable {
62
-    public:
63
-        Statement(Database & database, const std::string & statement);
64
-        ~Statement();
65
-
66
-        void bindDouble(const int index, const double value);
67
-        void bindDouble(const std::string & name, const double value);
68
-        void bindInt(const int index, const int value);
69
-        void bindInt(const std::string & name, const int value);
70
-        void bindString(const int index, const std::string & value);
71
-        void bindString(const std::string & name, const std::string & value);
72
-        const bool canRead() const;
73
-        const int columnCount() const;
74
-        const double readDouble(const int column) const;
75
-        const int readInt(const int column) const;
76
-        const std::string readString(const int column) const;
77
-        const bool step();
78
-        void finalize();
79
-        const bool reset();
80
-
81
-    private:
82
-        sqlite3_stmt * m_handle;
83
-        bool m_canRead;
84
-
85
-        int getParameterIndex(const std::string & name) const;
86
-        void handleBindResult(const int index, const int result) const;
87
-        void requireCanRead() const;
88
-    };
89
-}
90
-
91
-#endif

+ 491
- 0
include/sqlitepp/sqlitepp.h View File

@@ -0,0 +1,491 @@
1
+// Copyright (C) 2014--2015 Robin Krahl <robin.krahl@ireas.org>
2
+// MIT license -- http://opensource.org/licenses/MIT
3
+
4
+#ifndef SQLITEPP_SQLITEPP_H_
5
+#define SQLITEPP_SQLITEPP_H_
6
+
7
+#include <sqlite3.h>
8
+#include <stdexcept>
9
+#include <memory>
10
+#include <string>
11
+
12
+/// \file
13
+/// \brief Defines all classes of the sqlitepp library in the namespace
14
+///        sqlitepp.
15
+
16
+/// \mainpage sqlitepp -- C++ wrapper for SQLite3
17
+/// **sqlitepp** is a C++ wrapper for the official SQLite3 C API.
18
+///
19
+/// \section compile Compiling sqlitepp
20
+/// sqlitepp uses CMake as a build tool. To build sqlitepp from source,
21
+/// download the source from GitHub and then execute these commands:
22
+/// \code
23
+/// $ mkdir bin && cd bin
24
+/// $ cmake .. && make
25
+/// \endcode
26
+///
27
+/// \section using Using sqlitepp
28
+///
29
+/// \subsection connect Connecting to a database
30
+/// To connect to a SQLite database, you just have to create a new
31
+/// sqlitepp::Database object.
32
+/// \code{.cpp}
33
+/// sqlitepp::Database database("/path/to/database.sqlite");
34
+/// \endcode
35
+/// This snippet is equivalent to:
36
+/// \code{.cpp}
37
+/// sqlitepp::Database database;
38
+/// database.open("/path/to/database.sqlite");
39
+/// \endcode
40
+/// If the database file does not already exist, it is created. If an error
41
+/// occurs during the creation of the database, a sqlitepp::DatabaseError
42
+/// is thrown.
43
+///
44
+/// \subsection execute Executing a simple statement
45
+/// To execute a simple statement, use sqlitepp::Database::execute:
46
+/// \code{.cpp}
47
+/// sqlitepp::Database database("/path/to/database.sqlite");
48
+/// database.execute("CREATE TABLE test (id, value);");
49
+/// \endcode
50
+///
51
+/// \subsection prepare Executing complex statements
52
+/// If you want to execute more complex statements, for example selection or
53
+/// insertion, use prepared statements. You can prepare a statement using
54
+/// sqlitepp::Database::prepare. You can then bind values (if necessary) using
55
+/// the `bind` methods of sqlitepp::Statement and execute the statement using
56
+/// sqlitepp::Statement::execute. `execute` returns a sqlitepp::ResultSet that
57
+/// stores the returned values (if any).
58
+///
59
+/// \subsubsection insert Example 1: insert
60
+/// The recommended way to handle insertions are named bindings:
61
+/// \code{.cpp}
62
+/// sqlitepp::Database database("/path/to/database.sqlite");
63
+/// std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
64
+///     "INSERT INTO test (id, value) VALUES (:id, :value);");
65
+///
66
+/// // insert (1, "test value")
67
+/// statement->bind(":id", 1);
68
+/// statement->bind(":value", "test value");
69
+/// statement->execute();
70
+/// statement->reset();
71
+///
72
+/// // insert (2, "other value")
73
+/// statement->bind(":id", 2);
74
+/// statement->bind(":value", "other value");
75
+/// statement->execute();
76
+/// \endcode
77
+///
78
+/// \subsubsection select Example 2: select
79
+/// \code{.cpp}
80
+/// sqlitepp::Database database("/path/to/database.sqlite");
81
+/// std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
82
+///     "SELECT id, value FROM test;");
83
+/// ResultSet resultSet = statement.execute();
84
+/// while (resultSet.canRead()) {
85
+///   std::cout << "ID: " << resultSet.readInt(0) << "\tvalue: "
86
+///       << resultSet.readString(1) << std::endl;
87
+///   resultSet.next();
88
+/// }
89
+/// \endcode
90
+///
91
+/// \section concepts Concepts
92
+/// \subsection error Error handling
93
+/// If an error occurs during an operation, an exception is thrown. All
94
+/// SQLite3 database errors are wrapped in sqlitepp::DatabaseError. If a
95
+/// method returns, it was successful (if not stated otherwise in the method
96
+/// documentation).
97
+///
98
+/// \subsection resources Resources
99
+/// sqlitepp uses RAII. This means that the destructors of sqlitepp::Database
100
+/// and sqlitepp::Statement take care of freeing their resources once they
101
+/// are destroyed. You can force them to free their resources using the
102
+/// `close` methods.
103
+
104
+/// \brief Contains all classes of the sqlitepp library.
105
+namespace sqlitepp {
106
+
107
+/// \brief A class that forbids copying and assignments for all subclasses.
108
+///
109
+/// This class defines a private, unimplemented copy constructor and assignment
110
+/// method so that copies and assignments fail at compile-time. This class is
111
+/// inspired by Scott Meyers, <em>Effective C++</em>, 3rd Edition, Item 6.
112
+class Uncopyable {
113
+ protected:
114
+  Uncopyable() {}
115
+  ~Uncopyable() {}
116
+
117
+ private:
118
+  Uncopyable(const Uncopyable&);
119
+  Uncopyable& operator=(const Uncopyable&);
120
+};
121
+
122
+/// \brief An element that has the two states *open* and *closed*.
123
+///
124
+/// Subclasses of this class may define methods that require the object to be
125
+/// in a specific state. Refer to the implementing class&rsquo;s documentation
126
+/// for more information about the methods that require a specific state.
127
+///
128
+/// The default state depends on the implementation. You can check the state of
129
+/// an object using the isOpen() method.
130
+///
131
+/// Implementing classes may use setOpen() to change the state and
132
+/// requireOpen() to throw a std::logic_error if the object is currently not
133
+/// open.
134
+class Openable {
135
+ public:
136
+  /// \brief Checks whether this object is open.
137
+  ///
138
+  /// \returns `true` if this object is open; `false` if it is closed
139
+  bool isOpen() const;
140
+
141
+ protected:
142
+  /// \brief Creates a new Openable.
143
+  ///
144
+  /// \param open `true` if the objet should be open per default; `false` if
145
+  ///        it shoukd be closed per defaut
146
+  /// \param name the name of the implementing class used in error messages
147
+  Openable(const bool open, const std::string& name);
148
+
149
+  /// \brief Requires this object to be open and throws an exception if it is
150
+  ///        not.
151
+  ///
152
+  /// This method should be used at the beginning of other subclass methods
153
+  /// that require this object to be open. The error message of the exception
154
+  /// will contain the class name passed to the constructor.
155
+  ///
156
+  /// \throws std::logic_error if this object is not open
157
+  void requireOpen() const;
158
+
159
+  /// \brief Changes the state of this object.
160
+  ///
161
+  /// \param open the new state of this object (`true` if it should be opened;
162
+  ///        `false` if it should be closed)
163
+  void setOpen(const bool open);
164
+
165
+ private:
166
+  bool m_open;
167
+  const std::string& m_name;
168
+};
169
+
170
+/// \brief An error that occurred during a database operation.
171
+///
172
+/// This error class is only used for errors that occured in the SQLite3
173
+/// library and that are related to database operations. If there are other
174
+/// problems, for example wrong states or illegal arguments, appropriate other
175
+/// exceptions are thrown.
176
+///
177
+/// This exception class stores the SQLite3 error code and the error message.
178
+///
179
+/// \sa [SQLite Result Codes](https://www.sqlite.org/c3ref/c_abort.html)
180
+class DatabaseError : public std::runtime_error {
181
+ public:
182
+  /// \brief Creates a new DatabaseError with the given code and the default
183
+  ///        message.
184
+  ///
185
+  /// The message is retrieved from the default SQLite3 error messages.
186
+  ///
187
+  /// \param errorCode the SQLite3 error code
188
+  /// \sa [SQLite Result Codes](https://www.sqlite.org/c3ref/c_abort.html)
189
+  explicit DatabaseError(const int errorCode);
190
+
191
+  /// \brief Creates a new DatabaseError with the given code and message.
192
+  ///
193
+  /// \param errorCode the SQLite3 error code
194
+  /// \param errorMessage the according error message
195
+  /// \sa [SQLite Result Codes](https://www.sqlite.org/c3ref/c_abort.html)
196
+  DatabaseError(const int errorCode, const std::string& errorMessage);
197
+
198
+  /// \brief Returns the SQLite3 error code for this error.
199
+  ///
200
+  /// \sa [SQLite Result Codes](https://www.sqlite.org/c3ref/c_abort.html)
201
+  int errorCode() const;
202
+
203
+ private:
204
+  const int m_errorCode;
205
+
206
+  static std::string getErrorMessage(const int errorCode,
207
+                                     const std::string& errorMessage);
208
+};
209
+
210
+class Database;
211
+class ResultSet;
212
+
213
+/// \brief A handle for a SQLite3 statement.
214
+///
215
+/// This class stores a reference to a prepared SQLite3 statement and provides
216
+/// methods to bind parameters to the query, execute it and read the results.
217
+/// If a database operation fails, a DatabaseError is thrown.
218
+///
219
+/// Use Database::prepare to obtain instances of this class.
220
+class Statement : private Uncopyable, public Openable {
221
+ public:
222
+  /// \brief Deconstructs this object and finalizes the statement.
223
+  ///
224
+  /// Errors that occur when the statement is finalized are ignored as they
225
+  /// already occured during the last operation.
226
+  ~Statement();
227
+
228
+  /// \brief Binds the given double value to the column with the given index.
229
+  ///
230
+  /// \param index the index of the column to bind the value to
231
+  /// \param value the value to bind to that column
232
+  /// \throws std::logic_error if the statement is not open
233
+  /// \throws std::out_of_range if the given index is out of range
234
+  /// \throws std::runtime_error if there is not enough memory to bind the
235
+  ///         value
236
+  /// \throws DatabaseError if an database error occured during the binding
237
+  void bind(const int index, const double value);
238
+
239
+  /// \brief Binds the given double value to the column with the given name.
240
+  ///
241
+  /// \param index the name of the column to bind the value to
242
+  /// \param value the value to bind to that column
243
+  /// \throws std::logic_error if the statement is not open
244
+  /// \throws std::invalid_argument if there is no column witht the given name
245
+  /// \throws std::runtime_error if there is not enough memory to bind the
246
+  ///         value
247
+  /// \throws DatabaseError if an database error occured during the binding
248
+  void bind(const std::string& name, const double value);
249
+
250
+  /// \brief Binds the given integer value to the column with the given index.
251
+  ///
252
+  /// \param index the index of the column to bind the value to
253
+  /// \param value the value to bind to that column
254
+  /// \throws std::logic_error if the statement is not open
255
+  /// \throws std::out_of_range if the given index is out of range
256
+  /// \throws std::runtime_error if there is not enough memory to bind the
257
+  ///         value
258
+  /// \throws DatabaseError if an database error occured during the binding
259
+  void bind(const int index, const int value);
260
+
261
+  /// \brief Binds the given integer value to the column with the given name.
262
+  ///
263
+  /// \param index the name of the column to bind the value to
264
+  /// \param value the value to bind to that column
265
+  /// \throws std::logic_error if the statement is not open
266
+  /// \throws std::invalid_argument if there is no column witht the given name
267
+  /// \throws std::runtime_error if there is not enough memory to bind the
268
+  ///         value
269
+  /// \throws DatabaseError if an database error occured during the binding
270
+  void bind(const std::string& name, const int value);
271
+
272
+  /// \brief Binds the given string value to the column with the given index.
273
+  ///
274
+  /// \param index the index of the column to bind the value to
275
+  /// \param value the value to bind to that column
276
+  /// \throws std::logic_error if the statement is not open
277
+  /// \throws std::out_of_range if the given index is out of range
278
+  /// \throws std::runtime_error if there is not enough memory to bind the
279
+  ///         value
280
+  /// \throws DatabaseError if an database error occured during the binding
281
+  void bind(const int index, const std::string& value);
282
+
283
+  /// \brief Binds the given string value to the column with the given name.
284
+  ///
285
+  /// \param index the name of the column to bind the value to
286
+  /// \param value the value to bind to that column
287
+  /// \throws std::logic_error if the statement is not open
288
+  /// \throws std::invalid_argument if there is no column witht the given name
289
+  /// \throws std::runtime_error if there is not enough memory to bind the
290
+  ///         value
291
+  /// \throws DatabaseError if an database error occured during the binding
292
+  void bind(const std::string& name, const std::string& value);
293
+
294
+  /// \brief Closes this statement.
295
+  ///
296
+  /// Once you closed this statement, you may no longer access it. Any errors
297
+  /// that occur during finalization are ignored as they already occurred
298
+  /// during the last operation.
299
+  void close();
300
+
301
+  /// \brief Executes this statement and returns the result (if any).
302
+  ///
303
+  /// \returns the result returned from the query (empty if there was no result)
304
+  /// \throws std::logic_error if the statement is not open
305
+  /// \throws DatabaseError if a database error occurs during the query
306
+  ///         execution
307
+  ResultSet execute();
308
+
309
+  /// \brief Resets the statement.
310
+  ///
311
+  /// Resets the statement so that it can be re-executed. Bindings are not
312
+  /// resetted.
313
+  ///
314
+  /// \returns `true` if the reset was successful; otherwise `false`
315
+  /// \throws std::logic_error if the statement is not open
316
+  bool reset();
317
+
318
+ private:
319
+  explicit Statement(sqlite3_stmt* handle);
320
+
321
+  int getParameterIndex(const std::string& name) const;
322
+  void handleBindResult(const int index, const int result) const;
323
+  void requireCanRead() const;
324
+  void setInstancePointer(const std::weak_ptr<Statement>& instancePointer);
325
+  bool step();
326
+
327
+  sqlite3_stmt* m_handle;
328
+  bool m_canRead;
329
+  std::weak_ptr<Statement> m_instancePointer;
330
+
331
+  friend class Database;
332
+  friend class ResultSet;
333
+};
334
+
335
+/// \brief A handle for a SQLite3 database.
336
+///
337
+/// This class stores a reference to a SQLite3 database and provides methods
338
+/// to open, query and change this database. After you successfully opened a
339
+/// database (using the constructor Database(const std::string&) or using the
340
+/// open(const std::string&)  method), you can execute and prepare statements.
341
+/// You can check whether the database is currently open using isOpen().
342
+///
343
+/// If you try to call a method that queries or updates the database and the
344
+/// database is not open, a std::logic_error is thrown. If a database operation
345
+/// fails, a DatabaseError is thrown.
346
+class Database : private Uncopyable, public Openable {
347
+ public:
348
+  /// \brief Creates a new closed database.
349
+  ///
350
+  /// Before you can access this database, you have to open a database file
351
+  /// using open(std::string&).
352
+  Database();
353
+
354
+  /// \brief Creates a new database and opens the given file.
355
+  ///
356
+  /// The given file must either be a valid SQLite3 database file or may not
357
+  /// exist yet. This constructor is an abbreviation for:
358
+  /// \code{.cpp}
359
+  /// Database database;
360
+  /// database.open(file);
361
+  /// \endcode
362
+  ///
363
+  /// \param file the name of the database file (not required to exist)
364
+  /// \throws std::runtime_error if there is not enough memory to create a
365
+  ///         database connection
366
+  /// \throws DatabaseError if the SQLite3 database could not be opened
367
+  explicit Database(const std::string& file);
368
+
369
+  /// \brief Destructs this object and closes the database connection.
370
+  ///
371
+  /// Errors that occur closing the database are ignored.
372
+  ~Database();
373
+
374
+  /// \brief Closes the database if it is open.
375
+  ///
376
+  /// \throws DatabaseError if the database cannot be closed
377
+  void close();
378
+
379
+  /// \brief Executes the given SQL string.
380
+  ///
381
+  /// You can only call this method if there is an open database connection.
382
+  /// If you want to access the values returned by a SQL statement, use
383
+  /// prepare(const std::string&) instead.
384
+  ///
385
+  /// \param sql the SQL statement to execute
386
+  /// \throws std::logic_error if the database is not open
387
+  /// \throws DatabaseError if an error occurred during the execution
388
+  void execute(const std::string& sql);
389
+
390
+  /// \brief Opens the given database file.
391
+  ///
392
+  /// The given file must either be a valid SQLite3 database file or may not
393
+  /// exist yet. You can only open a new connection when the previous
394
+  /// connection has been closed (if any).
395
+  ///
396
+  /// \param file the name of the database file (not required to exist)
397
+  /// \throws std::logic_error if the database is already open
398
+  /// \throws std::runtime_error if there is not enough memory to create a
399
+  ///         database connection
400
+  /// \throws DatabaseError if the SQLite3 database could not be opened
401
+  void open(const std::string& file);
402
+
403
+  /// \brief Prepares a statement and returns a pointer to it.
404
+  ///
405
+  /// You can either pass a complete SQL statement or a statement with
406
+  /// wildcards. If you use wildcards, you can bind them to a value using the
407
+  /// returned Statement.
408
+  ///
409
+  /// \param sql the SQL statement to prepare (may contain wildcards)
410
+  /// \returns a pointer to the prepared statement
411
+  /// \throws std::logic_error if the database is not open
412
+  /// \throws DatabaseError if an error occurred during the preparation
413
+  std::shared_ptr<Statement> prepare(const std::string& sql);
414
+
415
+ private:
416
+  sqlite3* m_handle;
417
+};
418
+
419
+/// \brief A result set returned from a SQL query.
420
+///
421
+/// As long as there is data (`canRead()`), you can read it using the
422
+/// `read*Type*` methods. To advance to the next row, use `next()`.
423
+class ResultSet {
424
+ public:
425
+  /// \brief Checks whether there is data to read.
426
+  ///
427
+  /// \returns `true` if there is data to read; otherwise `false`
428
+  bool canRead() const;
429
+
430
+  /// \brief Returns the column count of the result data.
431
+  ///
432
+  /// You may only call this method when there is data to read (canRead()).
433
+  ///
434
+  /// \returns the column count of the result
435
+  /// \throws std::logic_error if the statement is not open or there is no
436
+  ///         data to read
437
+  int columnCount() const;
438
+
439
+  /// \brief Steps to the next row of the result (if there is one).
440
+  ///
441
+  /// \returns `true` if there is new data to read or `false` if there are
442
+  ///          no more results
443
+  /// \throws std::logic_error if the statement is not open
444
+  /// \throws DatabaseError if a database error occurs during the query
445
+  ///         execution
446
+  bool next();
447
+
448
+  /// \brief Returns the current double value of the result column with the
449
+  ///        given index.
450
+  ///
451
+  /// You may only call this metod when there is data to read (canRead()).
452
+  ///
453
+  /// \param column the index of the column to read from
454
+  /// \returns the current value of the result column with the given index
455
+  /// \throws std::logic_error if the statement is not open or there is no
456
+  ///         data to read
457
+  double readDouble(const int column) const;
458
+
459
+  /// \brief Returns the current integer value of the result column with the
460
+  ///        given index.
461
+  ///
462
+  /// You may only call this metod when there is data to read (canRead()).
463
+  ///
464
+  /// \param column the index of the column to read from
465
+  /// \returns the current value of the result column with the given index
466
+  /// \throws std::logic_error if the statement is not open or there is no
467
+  ///         data to read
468
+  int readInt(const int column) const;
469
+
470
+  /// \brief Returns the current string value of the result column with the
471
+  ///        given index.
472
+  ///
473
+  /// You may only call this metod when there is data to read (canRead()).
474
+  ///
475
+  /// \param column the index of the column to read from
476
+  /// \returns the current value of the result column with the given index
477
+  /// \throws std::logic_error if the statement is not open or there is no
478
+  ///         data to read
479
+  std::string readString(const int column) const;
480
+
481
+ private:
482
+  explicit ResultSet(const std::shared_ptr<Statement> statement);
483
+
484
+  const std::shared_ptr<Statement> m_statement;
485
+
486
+  friend class Statement;
487
+};
488
+
489
+}  // namespace sqlitepp
490
+
491
+#endif  // SQLITEPP_SQLITEPP_H_

+ 0
- 240
src/sqlitepp.cpp View File

@@ -1,240 +0,0 @@
1
-/*
2
- * (C) 2014 Robin Krahl
3
- * MIT license -- http://opensource.org/licenses/MIT
4
- */
5
-
6
-#include "sqlitepp.h"
7
-
8
-#include <exception>
9
-#include <iostream>
10
-#include <sstream>
11
-
12
-sqlitepp::Openable::Openable(const bool open, const std::string & name) :
13
-        m_open(open), m_name(name) {
14
-}
15
-
16
-const bool sqlitepp::Openable::isOpen() const {
17
-    return m_open;
18
-}
19
-
20
-void sqlitepp::Openable::requireOpen() const {
21
-    if (!m_open) {
22
-        throw std::logic_error(m_name + " is not open.");
23
-    }
24
-}
25
-
26
-void sqlitepp::Openable::setOpen(const bool open) {
27
-    m_open = open;
28
-}
29
-
30
-const std::string sqlitepp::DatabaseError::getErrorMessage(const int errorCode, const std::string & errorMessage) {
31
-    std::ostringstream stringStream;
32
-    stringStream << "Caught SQLite3 error " << errorCode << " meaning: " << errorMessage;
33
-    return stringStream.str();
34
-}
35
-
36
-sqlitepp::DatabaseError::DatabaseError(const int errorCode) :
37
-        std::runtime_error(getErrorMessage(errorCode, sqlite3_errstr(errorCode))), m_errorCode(errorCode) {
38
-}
39
-
40
-sqlitepp::DatabaseError::DatabaseError(const int errorCode, const std::string & errorMessage) :
41
-        std::runtime_error(getErrorMessage(errorCode, errorMessage)), m_errorCode(errorCode)
42
-{
43
-}
44
-
45
-const int sqlitepp::DatabaseError::errorCode() const {
46
-    return m_errorCode;
47
-}
48
-
49
-sqlitepp::Statement::Statement(sqlitepp::Database & database, const std::string & statement) :
50
-        Openable(true, "Statement"), m_canRead(false) {
51
-    database.requireOpen();
52
-    int result = sqlite3_prepare_v2(database.m_handle, statement.c_str(), -1, &m_handle, NULL);
53
-    if (result != SQLITE_OK) {
54
-        throw DatabaseError(result, sqlite3_errmsg(database.m_handle));
55
-    }
56
-    if (m_handle == NULL) {
57
-        throw std::runtime_error("Statement handle is NULL");
58
-    }
59
-}
60
-
61
-sqlitepp::Statement::~Statement() {
62
-    if (isOpen()) {
63
-        // errors that could occur during finalizing are ignored as they have
64
-        // already been handled!
65
-        sqlite3_finalize(m_handle);
66
-        setOpen(false);
67
-    }
68
-}
69
-
70
-void sqlitepp::Statement::bindDouble(const int index, const double value) {
71
-    requireOpen();
72
-    handleBindResult(index, sqlite3_bind_double(m_handle, index, value));
73
-}
74
-
75
-void sqlitepp::Statement::bindDouble(const std::string & name, const double value) {
76
-    bindDouble(getParameterIndex(name), value);
77
-}
78
-
79
-void sqlitepp::Statement::bindInt(const int index, const int value) {
80
-    requireOpen();
81
-    handleBindResult(index, sqlite3_bind_int(m_handle, index, value));
82
-}
83
-
84
-void sqlitepp::Statement::bindInt(const std::string & name, const int value) {
85
-    bindInt(getParameterIndex(name), value);
86
-}
87
-
88
-void sqlitepp::Statement::bindString(const int index, const std::string & value) {
89
-    requireOpen();
90
-    handleBindResult(index, sqlite3_bind_text(m_handle, index, value.c_str(), -1, NULL));
91
-}
92
-
93
-void sqlitepp::Statement::bindString(const std::string & name, const std::string & value) {
94
-    bindString(getParameterIndex(name), value);
95
-}
96
-
97
-const bool sqlitepp::Statement::canRead() const {
98
-    return m_canRead;
99
-}
100
-
101
-const int sqlitepp::Statement::columnCount() const {
102
-    requireOpen();
103
-    requireCanRead();
104
-    return sqlite3_column_count(m_handle);
105
-}
106
-
107
-const double sqlitepp::Statement::readDouble(const int column) const {
108
-    requireOpen();
109
-    requireCanRead();
110
-    return sqlite3_column_double(m_handle, column);
111
-}
112
-
113
-const int sqlitepp::Statement::readInt(const int column) const {
114
-    requireOpen();
115
-    requireCanRead();
116
-    return sqlite3_column_int(m_handle, column);
117
-}
118
-
119
-const std::string sqlitepp::Statement::readString(const int column) const {
120
-    requireOpen();
121
-    requireCanRead();
122
-    return std::string((const char *) sqlite3_column_text(m_handle, column));
123
-}
124
-
125
-void sqlitepp::Statement::requireCanRead() const {
126
-    if (!m_canRead) {
127
-        throw std::logic_error("Trying to read from statement without data");
128
-    }
129
-}
130
-
131
-const bool sqlitepp::Statement::step() {
132
-    requireOpen();
133
-    int result = sqlite3_step(m_handle);
134
-    if (result == SQLITE_ROW) {
135
-        m_canRead = true;
136
-    } else if (result == SQLITE_DONE) {
137
-        m_canRead = false;
138
-    } else {
139
-        throw DatabaseError(result);
140
-    }
141
-    return m_canRead;
142
-}
143
-
144
-void sqlitepp::Statement::finalize() {
145
-    if (isOpen()) {
146
-        // errors that could occur during finalizing are ignored as they have
147
-        // already been handled!
148
-        sqlite3_finalize(m_handle);
149
-        setOpen(false);
150
-    }
151
-}
152
-
153
-const bool sqlitepp::Statement::reset() {
154
-    requireOpen();
155
-    return sqlite3_reset(m_handle) == SQLITE_OK;
156
-}
157
-
158
-int sqlitepp::Statement::getParameterIndex(const std::string & name) const {
159
-    requireOpen();
160
-    int index = sqlite3_bind_parameter_index(m_handle, name.c_str());
161
-    if (index == 0) {
162
-        throw std::invalid_argument("No such parameter: " + name);
163
-    }
164
-    return index;
165
-}
166
-
167
-void sqlitepp::Statement::handleBindResult(const int index, const int result) const {
168
-    switch (result) {
169
-        case SQLITE_OK:
170
-            break;
171
-        case SQLITE_RANGE:
172
-            throw std::out_of_range("Bind index out of range: " + index);
173
-        case SQLITE_NOMEM:
174
-            throw std::runtime_error("No memory to bind parameter");
175
-        default:
176
-            throw DatabaseError(result);
177
-    }
178
-}
179
-
180
-sqlitepp::Database::Database() : Openable(false, "Database") {
181
-}
182
-
183
-sqlitepp::Database::Database(const std::string & file) : Openable(false, "Database") {
184
-    open(file);
185
-}
186
-
187
-sqlitepp::Database::~Database() {
188
-    if (isOpen()) {
189
-        int result = sqlite3_close(m_handle);
190
-        if (result != SQLITE_OK) {
191
-            std::cerr << "sqlitepp::Database::~Database(): Close failed with code "
192
-                    << result << " meaning: " << sqlite3_errstr(result);
193
-            std::abort();
194
-        } else {
195
-            setOpen(false);
196
-        }
197
-    }
198
-    // m_handle is deleted by sqlite3_close
199
-}
200
-
201
-void sqlitepp::Database::close() {
202
-    if (isOpen()) {
203
-        int result = sqlite3_close(m_handle);
204
-        if (result == SQLITE_OK) {
205
-            setOpen(false);
206
-        } else {
207
-            throw sqlitepp::DatabaseError(result);
208
-        }
209
-    }
210
-}
211
-
212
-void sqlitepp::Database::execute(const std::string & sql) {
213
-    requireOpen();
214
-    Statement statement(*this, sql);
215
-    statement.step();
216
-    statement.finalize();
217
-}
218
-
219
-void sqlitepp::Database::open(const std::string & file) {
220
-    if (isOpen()) {
221
-        throw std::logic_error("sqlitepp::Database::open(std::string&): Database already open");
222
-    }
223
-    int result = sqlite3_open(file.c_str(), & m_handle);
224
-
225
-    if (m_handle == NULL) {
226
-        throw std::runtime_error("sqlitepp::Database::open(std::string&): Can't allocate memory");
227
-    }
228
-
229
-    if (result == SQLITE_OK) {
230
-        setOpen(true);
231
-    } else {
232
-        std::string errorMessage = sqlite3_errmsg(m_handle);
233
-        sqlite3_close(m_handle);
234
-        throw sqlitepp::DatabaseError(result, errorMessage);
235
-    }
236
-}
237
-
238
-std::shared_ptr<sqlitepp::Statement> sqlitepp::Database::prepare(const std::string & sql) {
239
-    return std::shared_ptr<sqlitepp::Statement>(new sqlitepp::Statement(*this, sql));
240
-}

+ 264
- 0
src/sqlitepp/sqlitepp.cc View File

@@ -0,0 +1,264 @@
1
+// Copyright (C) 2014--2015 Robin Krahl <robin.krahl@ireas.org>
2
+// MIT license -- http://opensource.org/licenses/MIT
3
+
4
+#include "sqlitepp/sqlitepp.h"
5
+#include <exception>
6
+#include <iostream>
7
+#include <sstream>
8
+#include <string>
9
+
10
+namespace sqlitepp {
11
+
12
+Openable::Openable(const bool open, const std::string& name)
13
+    : m_open(open), m_name(name) {
14
+}
15
+
16
+bool Openable::isOpen() const {
17
+  return m_open;
18
+}
19
+
20
+void Openable::requireOpen() const {
21
+  if (!m_open) {
22
+    throw std::logic_error(m_name + " is not open.");
23
+  }
24
+}
25
+
26
+void Openable::setOpen(const bool open) {
27
+    m_open = open;
28
+}
29
+
30
+std::string DatabaseError::getErrorMessage(const int errorCode,
31
+    const std::string& errorMessage) {
32
+  std::ostringstream stringStream;
33
+  stringStream << "Caught SQLite3 error " << errorCode << " meaning: "
34
+      << errorMessage;
35
+  return stringStream.str();
36
+}
37
+
38
+DatabaseError::DatabaseError(const int errorCode)
39
+    : DatabaseError(errorCode, sqlite3_errstr(errorCode)) {
40
+}
41
+
42
+DatabaseError::DatabaseError(const int errorCode,
43
+                             const std::string& errorMessage)
44
+    : std::runtime_error(getErrorMessage(errorCode, errorMessage)),
45
+      m_errorCode(errorCode) {
46
+}
47
+
48
+int DatabaseError::errorCode() const {
49
+  return m_errorCode;
50
+}
51
+
52
+Statement::Statement(sqlite3_stmt* handle)
53
+    : Openable(true, "Statement"), m_canRead(false), m_handle(handle) {
54
+}
55
+
56
+Statement::~Statement() {
57
+  if (isOpen()) {
58
+    // errors that could occur during finalizing are ignored as they have
59
+    // already been handled!
60
+    sqlite3_finalize(m_handle);
61
+    setOpen(false);
62
+  }
63
+}
64
+
65
+void Statement::bind(const int index, const double value) {
66
+  requireOpen();
67
+  handleBindResult(index, sqlite3_bind_double(m_handle, index, value));
68
+}
69
+
70
+void Statement::bind(const std::string& name, const double value) {
71
+  bind(getParameterIndex(name), value);
72
+}
73
+
74
+void Statement::bind(const int index, const int value) {
75
+  requireOpen();
76
+  handleBindResult(index, sqlite3_bind_int(m_handle, index, value));
77
+}
78
+
79
+void Statement::bind(const std::string& name, const int value) {
80
+  bind(getParameterIndex(name), value);
81
+}
82
+
83
+void Statement::bind(const int index, const std::string& value) {
84
+  requireOpen();
85
+  handleBindResult(index, sqlite3_bind_text(m_handle, index, value.c_str(),
86
+      value.size(), NULL));
87
+}
88
+
89
+void Statement::bind(const std::string& name, const std::string& value) {
90
+  bind(getParameterIndex(name), value);
91
+}
92
+
93
+ResultSet Statement::execute() {
94
+  step();
95
+  return ResultSet(m_instancePointer.lock());
96
+}
97
+
98
+void Statement::requireCanRead() const {
99
+  if (!m_canRead) {
100
+    throw std::logic_error("Trying to read from statement without data");
101
+  }
102
+}
103
+
104
+void Statement::setInstancePointer(
105
+    const std::weak_ptr<Statement>& instancePointer) {
106
+  m_instancePointer = instancePointer;
107
+}
108
+
109
+bool Statement::step() {
110
+  requireOpen();
111
+  int result = sqlite3_step(m_handle);
112
+  if (result == SQLITE_ROW) {
113
+    m_canRead = true;
114
+  } else if (result == SQLITE_DONE) {
115
+    m_canRead = false;
116
+  } else {
117
+    throw DatabaseError(result);
118
+  }
119
+  return m_canRead;
120
+}
121
+
122
+void Statement::close() {
123
+  if (isOpen()) {
124
+    // errors that could occur during finalizing are ignored as they have
125
+    // already been handled!
126
+    sqlite3_finalize(m_handle);
127
+    setOpen(false);
128
+  }
129
+}
130
+
131
+bool Statement::reset() {
132
+  requireOpen();
133
+  return sqlite3_reset(m_handle) == SQLITE_OK;
134
+}
135
+
136
+int Statement::getParameterIndex(const std::string& name) const {
137
+  requireOpen();
138
+  int index = sqlite3_bind_parameter_index(m_handle, name.c_str());
139
+  if (index == 0) {
140
+    throw std::invalid_argument("No such parameter: " + name);
141
+  }
142
+  return index;
143
+}
144
+
145
+void Statement::handleBindResult(const int index, const int result) const {
146
+  switch (result) {
147
+    case SQLITE_OK:
148
+      break;
149
+    case SQLITE_RANGE:
150
+      throw std::out_of_range("Bind index out of range: " + index);
151
+    case SQLITE_NOMEM:
152
+      throw std::runtime_error("No memory to bind parameter");
153
+    default:
154
+      throw DatabaseError(result);
155
+  }
156
+}
157
+
158
+Database::Database() : Openable(false, "Database") {
159
+}
160
+
161
+Database::Database(const std::string & file) : Database() {
162
+    open(file);
163
+}
164
+
165
+Database::~Database() {
166
+  if (isOpen()) {
167
+    sqlite3_close(m_handle);
168
+    setOpen(false);
169
+  }
170
+  // m_handle is deleted by sqlite3_close
171
+}
172
+
173
+void Database::close() {
174
+  if (isOpen()) {
175
+    int result = sqlite3_close(m_handle);
176
+    if (result == SQLITE_OK) {
177
+      setOpen(false);
178
+    } else {
179
+      throw sqlitepp::DatabaseError(result);
180
+    }
181
+  }
182
+}
183
+
184
+void Database::execute(const std::string& sql) {
185
+  requireOpen();
186
+  std::shared_ptr<Statement> statement = prepare(sql);
187
+  statement->step();
188
+}
189
+
190
+void Database::open(const std::string& file) {
191
+  if (isOpen()) {
192
+    throw std::logic_error("sqlitepp::Database::open(std::string&): "
193
+                           "Database already open");
194
+  }
195
+  int result = sqlite3_open(file.c_str(), &m_handle);
196
+
197
+  if (m_handle == NULL) {
198
+    throw std::runtime_error("sqlitepp::Database::open(std::string&): "
199
+                             "Can't allocate memory");
200
+  }
201
+
202
+  if (result == SQLITE_OK) {
203
+    setOpen(true);
204
+  } else {
205
+    std::string errorMessage = sqlite3_errmsg(m_handle);
206
+    sqlite3_close(m_handle);
207
+    throw sqlitepp::DatabaseError(result, errorMessage);
208
+  }
209
+}
210
+
211
+std::shared_ptr<Statement> Database::prepare(const std::string& sql) {
212
+  requireOpen();
213
+  sqlite3_stmt* statementHandle;
214
+  int result = sqlite3_prepare_v2(m_handle, sql.c_str(), sql.size(),
215
+                                  &statementHandle, NULL);
216
+  if (result != SQLITE_OK) {
217
+    throw DatabaseError(result, sqlite3_errmsg(m_handle));
218
+  }
219
+  if (statementHandle == NULL) {
220
+    throw std::runtime_error("Statement handle is NULL");
221
+  }
222
+  auto statement = std::shared_ptr<Statement>(new Statement(statementHandle));
223
+  statement->setInstancePointer(std::weak_ptr<Statement>(statement));
224
+  return statement;
225
+}
226
+
227
+ResultSet::ResultSet(const std::shared_ptr<Statement> statement)
228
+    : m_statement(statement) {
229
+}
230
+
231
+bool ResultSet::canRead() const {
232
+  return m_statement->m_canRead;
233
+}
234
+
235
+int ResultSet::columnCount() const {
236
+  m_statement->requireOpen();
237
+  m_statement->requireCanRead();
238
+  return sqlite3_column_count(m_statement->m_handle);
239
+}
240
+
241
+double ResultSet::readDouble(const int column) const {
242
+  m_statement->requireOpen();
243
+  m_statement->requireCanRead();
244
+  return sqlite3_column_double(m_statement->m_handle, column);
245
+}
246
+
247
+int ResultSet::readInt(const int column) const {
248
+  m_statement->requireOpen();
249
+  m_statement->requireCanRead();
250
+  return sqlite3_column_int(m_statement->m_handle, column);
251
+}
252
+
253
+std::string ResultSet::readString(const int column) const {
254
+  m_statement->requireOpen();
255
+  m_statement->requireCanRead();
256
+  return std::string((const char*) sqlite3_column_text(m_statement->m_handle,
257
+                                                       column));
258
+}
259
+
260
+bool ResultSet::next() {
261
+  return m_statement->step();
262
+}
263
+
264
+}  // namespace sqlitepp

+ 100
- 0
src/sqlitepp/sqlitepp_test.cc View File

@@ -0,0 +1,100 @@
1
+// Copyright (C) 2014--2015 Robin Krahl <robin.krahl@ireas.org>
2
+// MIT license -- http://opensource.org/licenses/MIT
3
+
4
+#include <stdexcept>
5
+#include <fstream>
6
+#include <iostream>
7
+#include "gtest/gtest.h"
8
+#include "sqlitepp/sqlitepp.h"
9
+
10
+TEST(Database, openClose) {
11
+  sqlitepp::Database database;
12
+  EXPECT_FALSE(database.isOpen());
13
+  database.open("/tmp/test.db");
14
+  EXPECT_TRUE(database.isOpen());
15
+  database.close();
16
+  EXPECT_FALSE(database.isOpen());
17
+  database.open("/tmp/test2.db");
18
+  EXPECT_TRUE(database.isOpen());
19
+  database.close();
20
+  EXPECT_FALSE(database.isOpen());
21
+  sqlitepp::Database database2("/tmp/test.db");
22
+  EXPECT_TRUE(database2.isOpen());
23
+  EXPECT_THROW(database2.open("/tmp/test2.db"), std::logic_error);
24
+  EXPECT_TRUE(database2.isOpen());
25
+  database2.close();
26
+  EXPECT_FALSE(database2.isOpen());
27
+
28
+  std::ifstream testStream("/tmp/test.db");
29
+  EXPECT_TRUE(testStream.good());
30
+  testStream.close();
31
+  testStream.open("/tmp/test2.db");
32
+  EXPECT_TRUE(testStream.good());
33
+  testStream.close();
34
+}
35
+
36
+TEST(Database, copy) {
37
+  sqlitepp::Database database;
38
+  // MUST NOT COMPILE:
39
+  // sqlitepp::Database database2 = database;
40
+  database.close();
41
+  sqlitepp::Database database3;
42
+  // MUST NOT COMPILE:
43
+  // database3 = database;
44
+  database3.close();
45
+}
46
+
47
+TEST(Database, prepare) {
48
+  sqlitepp::Database database("/tmp/test.db");
49
+  std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
50
+      "CREATE TABLE IF NOT EXISTS test (id, value);");
51
+  EXPECT_TRUE(statement->isOpen());
52
+  statement->close();
53
+  EXPECT_FALSE(statement->isOpen());
54
+  database.close();
55
+}
56
+
57
+TEST(Database, execute) {
58
+  sqlitepp::Database database("/tmp/test.db");
59
+  database.execute("CREATE TABLE IF NOT EXISTS test (id, value);");
60
+}
61
+
62
+TEST(Database, insert) {
63
+  sqlitepp::Database database("/tmp/test.db");
64
+  std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
65
+      "INSERT INTO test (id, value) VALUES (:id, ?)");
66
+  statement->bind(":id", 1);
67
+  statement->bind(2, "test value");
68
+  statement->execute();
69
+  statement->reset();
70
+  statement->bind(":id", 2);
71
+  statement->bind(2, "other value");
72
+  statement->execute();
73
+}
74
+
75
+TEST(Database, query) {
76
+  sqlitepp::Database database("/tmp/test.db");
77
+  std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
78
+      "SELECT id, value FROM test;");
79
+  sqlitepp::ResultSet resultSet = statement->execute();
80
+  EXPECT_TRUE(resultSet.canRead());
81
+  EXPECT_EQ(2, resultSet.columnCount());
82
+  int id = resultSet.readInt(0);
83
+  std::string value = resultSet.readString(1);
84
+  EXPECT_EQ(1, id);
85
+  EXPECT_EQ("test value", value);
86
+  EXPECT_TRUE(resultSet.next());
87
+  EXPECT_TRUE(resultSet.canRead());
88
+  id = resultSet.readInt(0);
89
+  value = resultSet.readString(1);
90
+  EXPECT_EQ(2, id);
91
+  EXPECT_EQ("other value", value);
92
+  EXPECT_FALSE(resultSet.next());
93
+  EXPECT_FALSE(resultSet.canRead());
94
+}
95
+
96
+TEST(Database, cleanup) {
97
+  sqlitepp::Database database("/tmp/test.db");
98
+  database.execute("DROP TABLE test;");
99
+  database.close();
100
+}

+ 0
- 108
src/sqlitepptest.cpp View File

@@ -1,108 +0,0 @@
1
-/*
2
- * (C) 2014 Robin Krahl
3
- * MIT license -- http://opensource.org/licenses/MIT
4
- */
5
-
6
-#include "sqlitepp.h"
7
-
8
-#define BOOST_TEST_DYN_LINK
9
-#define BOOST_TEST_MODULE SQLitePPTest
10
-#include <boost/test/unit_test.hpp>
11
-
12
-#include <fstream>
13
-#include <iostream>
14
-#include <stdexcept>
15
-
16
-BOOST_AUTO_TEST_CASE(openClose) {
17
-    sqlitepp::Database database;
18
-    BOOST_CHECK(!database.isOpen());
19
-    database.open("/tmp/test.db");
20
-    BOOST_CHECK(database.isOpen());
21
-    database.close();
22
-    BOOST_CHECK(!database.isOpen());
23
-    database.open("/tmp/test2.db");
24
-    BOOST_CHECK(database.isOpen());
25
-    database.close();
26
-    BOOST_CHECK(!database.isOpen());
27
-    sqlitepp::Database database2("/tmp/test.db");
28
-    BOOST_CHECK(database2.isOpen());
29
-    try {
30
-        database2.open("/tmp/test2.db");
31
-        BOOST_ERROR("Calling open() to an open database does not throw an exception.");
32
-    } catch (std::logic_error &) {
33
-        // everything fine
34
-    }
35
-    BOOST_CHECK(database2.isOpen());
36
-    database2.close();
37
-    BOOST_CHECK(!database2.isOpen());
38
-
39
-    std::ifstream testStream("/tmp/test.db");
40
-    BOOST_CHECK(testStream.good());
41
-    testStream.close();
42
-    testStream.open("/tmp/test2.db");
43
-    BOOST_CHECK(testStream.good());
44
-    testStream.close();
45
-}
46
-
47
-BOOST_AUTO_TEST_CASE(copy) {
48
-    sqlitepp::Database database;
49
-    // MUST NOT COMPILE:
50
-    // sqlitepp::Database database2 = database;
51
-    database.close();
52
-    sqlitepp::Database database3;
53
-    // MUST NOT COMPILE:
54
-    // database3 = database;
55
-    database3.close();
56
-}
57
-
58
-BOOST_AUTO_TEST_CASE(prepare) {
59
-    sqlitepp::Database database("/tmp/test.db");
60
-    sqlitepp::Statement statement(database, "CREATE TABLE IF NOT EXISTS test (id, value);");
61
-    // TODO check std::logic_error
62
-    BOOST_CHECK(statement.isOpen());
63
-    statement.finalize();
64
-    BOOST_CHECK(!statement.isOpen());
65
-    database.close();
66
-}
67
-
68
-BOOST_AUTO_TEST_CASE(execute) {
69
-    sqlitepp::Database database("/tmp/test.db");
70
-    database.execute("CREATE TABLE IF NOT EXISTS test (id, value);");
71
-    sqlitepp::Statement statement(database, "INSERT INTO test (id, value) VALUES (:id, ?)");
72
-    statement.bindInt(":id", 1);
73
-    statement.bindString(2, "test value");
74
-    statement.step();
75
-    statement.reset();
76
-    statement.bindInt(":id", 2);
77
-    statement.bindString(2, "other value");
78
-    statement.step();
79
-    statement.finalize();
80
-}
81
-
82
-BOOST_AUTO_TEST_CASE(query) {
83
-    sqlitepp::Database database("/tmp/test.db");
84
-    sqlitepp::Statement statement(database, "SELECT id, value FROM test;");
85
-    bool hasNext = statement.step();
86
-    BOOST_CHECK(hasNext);
87
-    BOOST_CHECK_EQUAL(statement.columnCount(), 2);
88
-    int id = statement.readInt(0);
89
-    std::string value = statement.readString(1);
90
-    BOOST_CHECK_EQUAL(id, 1);
91
-    BOOST_CHECK_EQUAL(value, "test value");
92
-    hasNext = statement.step();
93
-    BOOST_CHECK(hasNext);
94
-    id = statement.readInt(0);
95
-    value = statement.readString(1);
96
-    BOOST_CHECK_EQUAL(id, 2);
97
-    BOOST_CHECK_EQUAL(value, "other value");
98
-    hasNext = statement.step();
99
-    BOOST_CHECK(!hasNext);
100
-    statement.finalize();
101
-    database.close();
102
-}
103
-
104
-BOOST_AUTO_TEST_CASE(cleanup) {
105
-    sqlitepp::Database database("/tmp/test.db");
106
-    database.execute("DROP TABLE test;");
107
-    database.close();
108
-}

Loading…
Cancel
Save