Line data Source code
1 : /***********************************************************************\
2 : * gnc-sql-backend.hpp: Qof Backend for SQL Databases *
3 : * *
4 : * Copyright 2016 John Ralls <jralls@ceridwen.us> *
5 : * *
6 : * This program is free software; you can redistribute it and/or *
7 : * modify it under the terms of the GNU General Public License as *
8 : * published by the Free Software Foundation; either version 2 of *
9 : * the License, or (at your option) any later version. *
10 : * *
11 : * This program is distributed in the hope that it will be useful, *
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 : * GNU General Public License for more details. *
15 : * *
16 : * You should have received a copy of the GNU General Public License *
17 : * along with this program; if not, contact: *
18 : * *
19 : * Free Software Foundation Voice: +1-617-542-5942 *
20 : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21 : * Boston, MA 02110-1301, USA gnu@gnu.org *
22 : \***********************************************************************/
23 :
24 : #ifndef __GNC_SQL_BACKEND_HPP__
25 : #define __GNC_SQL_BACKEND_HPP__
26 :
27 : #include <qof.h>
28 : #include <Account.h>
29 :
30 : #include <memory>
31 : #include <exception>
32 : #include <sstream>
33 : #include <vector>
34 : #include <qof-backend.hpp>
35 :
36 : class GncSqlColumnTableEntry;
37 : using GncSqlColumnTableEntryPtr = std::shared_ptr<GncSqlColumnTableEntry>;
38 : using EntryVec = std::vector<GncSqlColumnTableEntryPtr>;
39 : class GncSqlObjectBackend;
40 : using GncSqlObjectBackendPtr = std::shared_ptr<GncSqlObjectBackend>;
41 : using OBEEntry = std::tuple<std::string, GncSqlObjectBackendPtr>;
42 : using OBEVec = std::vector<OBEEntry>;
43 : class GncSqlConnection;
44 : class GncSqlStatement;
45 : using GncSqlStatementPtr = std::unique_ptr<GncSqlStatement>;
46 : class GncSqlResult;
47 : using GncSqlResultPtr = GncSqlResult*;
48 : using VersionPair = std::pair<const std::string, unsigned int>;
49 : using VersionVec = std::vector<VersionPair>;
50 : using uint_t = unsigned int;
51 :
52 : typedef enum
53 : {
54 : OP_DB_INSERT,
55 : OP_DB_UPDATE,
56 : OP_DB_DELETE
57 : } E_DB_OPERATION;
58 :
59 : /**
60 : *
61 : * Main SQL backend structure.
62 : */
63 : class GncSqlBackend : public QofBackend
64 : {
65 : public:
66 : GncSqlBackend(GncSqlConnection *conn, QofBook* book);
67 : virtual ~GncSqlBackend();
68 : /**
69 : * Load the contents of an SQL database into a book.
70 : *
71 : * @param book Book to be loaded
72 : */
73 : void load(QofBook*, QofBackendLoadType) override;
74 : /**
75 : * Save the contents of a book to an SQL database.
76 : *
77 : * @param book Book to be saved
78 : */
79 : void sync(QofBook*) override;
80 : /**
81 : * An object is about to be edited.
82 : *
83 : * @param inst Object being edited
84 : */
85 : void begin(QofInstance*) override;
86 : /**
87 : * Object editing is complete and the object should be saved.
88 : *
89 : * @param inst Object being edited
90 : */
91 : void commit(QofInstance*) override;
92 : /**
93 : * Object editing has been cancelled.
94 : *
95 : * @param inst Object being edited
96 : */
97 : void rollback(QofInstance*) override;
98 : /** Connect the backend to a GncSqlConnection.
99 : * Sets up version info. Calling with nullptr clears the connection and
100 : * destroys the version info.
101 : */
102 : void connect(GncSqlConnection *conn) noexcept;
103 : /**
104 : * Initializes DB table version information.
105 : */
106 : void init_version_info() noexcept;
107 : bool reset_version_info() noexcept;
108 : /**
109 : * Finalizes DB table version information.
110 : */
111 : void finalize_version_info() noexcept;
112 : /* FIXME: These are just pass-throughs of m_conn functions. */
113 : GncSqlStatementPtr create_statement_from_sql(const std::string& str) const noexcept;
114 : /** Executes an SQL SELECT statement and returns the result rows. If an
115 : * error occurs, an entry is added to the log, an error status is returned
116 : * to qof and nullptr is returned.
117 : *
118 : * @param statement Statement
119 : * @return Results, or nullptr if an error has occurred
120 : */
121 : GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr& stmt) const noexcept;
122 : int execute_nonselect_statement(const GncSqlStatementPtr& stmt) const noexcept;
123 : std::string quote_string(const std::string&) const noexcept;
124 : /**
125 : * Creates a table in the database
126 : *
127 : * @param table_name Table name
128 : * @param col_table DB table description
129 : * @return TRUE if successful, FALSE if unsuccessful
130 : */
131 : bool create_table(const std::string& table_name, const EntryVec& col_table) const noexcept;
132 : /**
133 : * Creates a table in the database and sets its version
134 : *
135 : * @param table_name Table name
136 : * @param table_version Table version
137 : * @param col_table DB table description
138 : * @return TRUE if successful, FALSE if unsuccessful
139 : */
140 : bool create_table(const std::string& table_name, int table_version,
141 : const EntryVec& col_table) noexcept;
142 : /**
143 : * Create/update all tables in the database
144 : */
145 : void create_tables() noexcept;
146 :
147 : /**
148 : * Creates an index in the database
149 : *
150 : * @param index_name Index name
151 : * @param table_name Table name
152 : * @param col_table Columns that the index should index
153 : * @return TRUE if successful, FALSE if unsuccessful
154 : */
155 : bool create_index(const std::string& index_name,
156 : const std::string& table_name,
157 : const EntryVec& col_table) const noexcept;
158 : /**
159 : * Adds one or more columns to an existing table.
160 : *
161 : * @param table_name SQL table name
162 : * @param new_col_table Column table for new columns
163 : * @return TRUE if successful, FALSE if unsuccessful
164 : */
165 : bool add_columns_to_table(const std::string& table_name,
166 : const EntryVec& col_table) const noexcept;
167 : /**
168 : * Upgrades a table to a new structure.
169 : *
170 : * The upgrade is done by creating a new table with the new structure,
171 : * SELECTing the old data into the new table, deleting the old table, then
172 : * renaming the new table. Therefore, this will only work if the new table
173 : * structure is similar enough to the old table that the SELECT will work.
174 : *
175 : * @param table_name SQL table name
176 : * @param col_table Column table
177 : */
178 : void upgrade_table (const std::string& table_name,
179 : const EntryVec& col_table) noexcept;
180 : /**
181 : * Returns the version number for a DB table.
182 : *
183 : * @param table_name Table name
184 : * @return Version number, or 0 if the table does not exist
185 : */
186 : uint_t get_table_version(const std::string& table_name) const noexcept;
187 : bool set_table_version (const std::string& table_name, uint_t version) noexcept;
188 : /**
189 : * Register a commodity to be committed after loading is complete.
190 : *
191 : * Necessary to save corrections made while loading.
192 : * @param comm The commodity item to be committed.
193 : */
194 : void commodity_for_postload_processing(gnc_commodity*);
195 : /**
196 : * Get the GncSqlObjectBackend for the indicated type.
197 : *
198 : * Required because we need to pass a pointer to this to a callback via a C
199 : * function.
200 : * @param type: The QofInstance type constant to select the object backend.
201 : */
202 : GncSqlObjectBackendPtr get_object_backend(const std::string& type) const noexcept;
203 : /**
204 : * Checks whether an object is in the database or not.
205 : *
206 : * @param table_name DB table name
207 : * @param obj_name QOF object type name
208 : * @param pObject Object to be checked
209 : * @param table DB table description
210 : * @return TRUE if the object is in the database, FALSE otherwise
211 : */
212 : bool object_in_db (const char* table_name, QofIdTypeConst obj_name,
213 : const gpointer pObject, const EntryVec& table ) const noexcept;
214 : /**
215 : * Performs an operation on the database.
216 : *
217 : * @param op Operation type
218 : * @param table_name SQL table name
219 : * @param obj_name QOF object type name
220 : * @param pObject Gnucash object
221 : * @param table DB table description
222 : * @return TRUE if successful, FALSE if not
223 : */
224 : bool do_db_operation (E_DB_OPERATION op, const char* table_name,
225 : QofIdTypeConst obj_name, gpointer pObject,
226 : const EntryVec& table) const noexcept;
227 : /**
228 : * Ensure that a commodity referenced in another object is in fact saved
229 : * in the database.
230 : *
231 : * @param comm The commodity in question
232 : * @return true if the commodity needed to be saved.
233 : */
234 : bool save_commodity(gnc_commodity* comm) noexcept;
235 508 : QofBook* book() const noexcept { return m_book; }
236 0 : void set_loading(bool loading) noexcept { m_loading = loading; }
237 214 : bool pristine() const noexcept { return m_is_pristine_db; }
238 : void update_progress(double pct) const noexcept;
239 : void finish_progress() const noexcept;
240 :
241 : protected:
242 : GncSqlConnection* m_conn = nullptr; /**< SQL connection */
243 : QofBook* m_book = nullptr; /**< The primary, main open book */
244 : bool m_loading; /**< We are performing an initial load */
245 : bool m_in_query; /**< We are processing a query */
246 : bool m_is_pristine_db; /**< Are we saving to a new pristine db? */
247 : const char* m_time_format = nullptr; /**< Server-specific date-time string format */
248 : VersionVec m_versions; /**< Version number for each table */
249 : private:
250 : bool write_account_tree(Account*);
251 : bool write_accounts();
252 : bool write_transactions();
253 : bool write_template_transactions();
254 : bool write_schedXactions();
255 : GncSqlStatementPtr build_insert_statement (const char* table_name,
256 : QofIdTypeConst obj_name,
257 : gpointer pObject,
258 : const EntryVec& table) const noexcept;
259 : GncSqlStatementPtr build_update_statement (const gchar* table_name,
260 : QofIdTypeConst obj_name,
261 : gpointer pObject,
262 : const EntryVec& table) const noexcept;
263 : GncSqlStatementPtr build_delete_statement (const char* table_name,
264 : QofIdTypeConst obj_name,
265 : gpointer pObject,
266 : const EntryVec& table) const noexcept;
267 :
268 : class ObjectBackendRegistry
269 : {
270 : public:
271 : ObjectBackendRegistry();
272 : ObjectBackendRegistry(const ObjectBackendRegistry&) = delete;
273 : ObjectBackendRegistry(const ObjectBackendRegistry&&) = delete;
274 : ObjectBackendRegistry operator=(const ObjectBackendRegistry&) = delete;
275 : ObjectBackendRegistry operator=(const ObjectBackendRegistry&&) = delete;
276 15 : ~ObjectBackendRegistry() = default;
277 : void register_backend(OBEEntry&& entry) noexcept;
278 : void register_backend(GncSqlObjectBackendPtr obe) noexcept;
279 : GncSqlObjectBackendPtr get_object_backend(const std::string& type) const;
280 : void load_remaining(GncSqlBackend*);
281 19 : OBEVec::iterator begin() { return m_registry.begin(); }
282 19 : OBEVec::iterator end() { return m_registry.end(); }
283 7 : OBEVec::size_type size() { return m_registry.size(); }
284 : private:
285 : OBEVec m_registry;
286 : };
287 : ObjectBackendRegistry m_backend_registry;
288 : std::vector<gnc_commodity*> m_postload_commodities;
289 : };
290 :
291 : #endif //__GNC_SQL_BACKEND_HPP__
|