GnuCash  5.6-150-g038405b370+
gnc-dbiproviderimpl.hpp
1 /************************************************************************
2  * gnc-dbiproviderimpl.hpp: Encapsulate differences among Dbi backends. *
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 #ifndef __GNC_DBISQLPROVIDERIMPL_HPP__
24 #define __GNC_DBISQLPROVIDERIMPL_HPP__
25 #include <guid.hpp>
26 #include <config.h>
27 
28 #include <string>
29 #include <algorithm>
30 #include <vector>
31 
32 #include "gnc-backend-dbi.hpp"
33 #include "gnc-dbiprovider.hpp"
34 #include "gnc-backend-dbi.h"
35 #include <gnc-sql-column-table-entry.hpp>
36 
37 using StrVec = std::vector<std::string>;
38 
39 template <DbType T>
41 {
42 public:
43  StrVec get_table_list(dbi_conn conn, const std::string& table);
44  void append_col_def(std::string& ddl, const GncSqlColumnInfo& info);
45  StrVec get_index_list (dbi_conn conn);
46  void drop_index(dbi_conn conn, const std::string& index);
47 };
48 
49 template <DbType T> GncDbiProviderPtr
50 make_dbi_provider()
51 {
52  return GncDbiProviderPtr(new GncDbiProviderImpl<T>);
53 }
54 
55 template<> void
57  const GncSqlColumnInfo& info)
58 {
59  const char* type_name = nullptr;
60 
61  if (info.m_type == BCT_INT)
62  {
63  type_name = "integer";
64  }
65  else if (info.m_type == BCT_INT64)
66  {
67  type_name = "bigint";
68  }
69  else if (info.m_type == BCT_DOUBLE)
70  {
71  type_name = "float8";
72  }
73  else if (info.m_type == BCT_STRING || info.m_type == BCT_DATE
74  || info.m_type == BCT_DATETIME)
75  {
76  type_name = "text";
77  }
78  else
79  {
80  PERR ("Unknown column type: %d\n", info.m_type);
81  type_name = "";
82  }
83  ddl += (info.m_name + " " + type_name);
84  if (info.m_size != 0)
85  {
86  ddl += "(" + std::to_string(info.m_size) + ")";
87  }
88  if (info.m_primary_key)
89  {
90  ddl += " PRIMARY KEY";
91  }
92  if (info.m_autoinc)
93  {
94  ddl += " AUTOINCREMENT";
95  }
96  if (info.m_not_null)
97  {
98  ddl += " NOT NULL";
99  }
100 }
101 
102 
103 template<> void
105  const GncSqlColumnInfo& info)
106 {
107  const char* type_name = nullptr;
108 
109  if (info.m_type == BCT_INT)
110  {
111  type_name = "integer";
112  }
113  else if (info.m_type == BCT_INT64)
114  {
115  type_name = "bigint";
116  }
117  else if (info.m_type == BCT_DOUBLE)
118  {
119  type_name = "double";
120  }
121  else if (info.m_type == BCT_STRING)
122  {
123  type_name = "varchar";
124  }
125  else if (info.m_type == BCT_DATE)
126  {
127  type_name = "date";
128  }
129  else if (info.m_type == BCT_DATETIME)
130  {
131  type_name = "DATETIME NULL DEFAULT '1970-01-01 00:00:00'";
132  }
133  else
134  {
135  PERR ("Unknown column type: %d\n", info.m_type);
136  type_name = "";
137  }
138  ddl += info.m_name + " " + type_name;
139  if (info.m_size != 0 && info.m_type == BCT_STRING)
140  {
141  ddl += "(" + std::to_string(info.m_size) + ")";
142  }
143  if (info.m_unicode)
144  {
145  ddl += " CHARACTER SET utf8";
146  }
147  if (info.m_primary_key)
148  {
149  ddl += " PRIMARY KEY";
150  }
151  if (info.m_autoinc)
152  {
153  ddl += " AUTO_INCREMENT";
154  }
155  if (info.m_not_null)
156  {
157  ddl += " NOT NULL";
158  }
159 }
160 
161 
162 template<> void
164  const GncSqlColumnInfo& info)
165 {
166  const char* type_name = nullptr;
167 
168  if (info.m_type == BCT_INT)
169  {
170  if (info.m_autoinc)
171  {
172  type_name = "serial";
173  }
174  else
175  {
176  type_name = "integer";
177  }
178  }
179  else if (info.m_type == BCT_INT64)
180  {
181  type_name = "int8";
182  }
183  else if (info.m_type == BCT_DOUBLE)
184 
185  {
186  type_name = "double precision";
187  }
188  else if (info.m_type == BCT_STRING)
189  {
190  type_name = "varchar";
191  }
192  else if (info.m_type == BCT_DATE)
193  {
194  type_name = "date";
195  }
196  else if (info.m_type == BCT_DATETIME)
197  {
198  type_name = "timestamp without time zone";
199  }
200  else
201  {
202  PERR ("Unknown column type: %d\n", info.m_type);
203  type_name = "";
204  }
205  ddl += info.m_name + " " + type_name;
206  if (info.m_size != 0 && info.m_type == BCT_STRING)
207  {
208  ddl += "(" + std::to_string(info.m_size) + ")";
209  }
210  if (info.m_primary_key)
211  {
212  ddl += " PRIMARY KEY";
213  }
214  if (info.m_not_null)
215  {
216  ddl += " NOT NULL";
217  }
218 }
219 
220 static StrVec
221 conn_get_table_list (dbi_conn conn, const std::string& dbname,
222  const std::string& table)
223 {
224  StrVec retval;
225  const char* tableptr = (table.empty() ? nullptr : table.c_str());
226  auto tables = dbi_conn_get_table_list (conn, dbname.c_str(), tableptr);
227  while (dbi_result_next_row (tables) != 0)
228  {
229  std::string table_name {dbi_result_get_string_idx (tables, 1)};
230  retval.push_back(table_name);
231  }
232  dbi_result_free (tables);
233  return retval;
234 }
235 
236 template<> StrVec
238  const std::string& table)
239 {
240  /* Return the list, but remove the tables that sqlite3 adds for
241  * its own use. */
242  std::string dbname (dbi_conn_get_option (conn, "dbname"));
243  auto list = conn_get_table_list (conn, dbname, table);
244  auto end = std::remove(list.begin(), list.end(), "sqlite_sequence");
245  list.erase(end, list.end());
246  return list;
247 }
248 
249 template<> StrVec
251  const std::string& table)
252 {
253  std::string dbname (dbi_conn_get_option (conn, "dbname"));
254  dbname.insert((std::string::size_type)0, 1, '`');
255  dbname += '`';
256  return conn_get_table_list (conn, dbname, table);
257 }
258 
259 template<> StrVec
261  const std::string& table)
262 {
263  const char* query_no_regex = "SELECT relname FROM pg_class WHERE relname"
264  "!~ '^(pg|sql)_' AND relkind = 'r' ORDER BY relname";
265  std::string query_with_regex = "SELECT relname FROM pg_class WHERE relname LIKE '";
266  query_with_regex += table + "' AND relkind = 'r' ORDER BY relname";
267  dbi_result result;
268  if (table.empty())
269  result = dbi_conn_query (conn, query_no_regex);
270  else
271  result = dbi_conn_query (conn, query_with_regex.c_str());
272 
273  StrVec list;
274  const char* errmsg;
275  if (dbi_conn_error (conn, &errmsg) != DBI_ERROR_NONE)
276  {
277  PWARN ("Table List Retrieval Error: %s\n", errmsg);
278  return list;
279  }
280 
281  while (dbi_result_next_row (result) != 0)
282  {
283  std::string index_name {dbi_result_get_string_idx (result, 1)};
284  list.push_back(index_name);
285  }
286  dbi_result_free (result);
287  return list;
288 }
289 
290 template<> StrVec
292 {
293  StrVec retval;
294  const char* errmsg;
295  dbi_result result = dbi_conn_query (conn,
296  "SELECT name FROM sqlite_master WHERE type = 'index' AND name NOT LIKE 'sqlite_autoindex%'");
297  if (dbi_conn_error (conn, &errmsg) != DBI_ERROR_NONE)
298  {
299  PWARN ("Index Table Retrieval Error: %s\n", errmsg);
300  return retval;
301  }
302  while (dbi_result_next_row (result) != 0)
303  {
304  std::string index_name {dbi_result_get_string_idx (result, 1)};
305  retval.push_back(index_name);
306  }
307  dbi_result_free (result);
308  return retval;
309 }
310 
311 template<> StrVec
313 {
314  StrVec retval;
315  const char* errmsg;
316  auto tables = get_table_list(conn, "");
317  for (auto table_name : tables)
318  {
319  auto result = dbi_conn_queryf (conn,
320  "SHOW INDEXES IN %s WHERE Key_name != 'PRIMARY'",
321  table_name.c_str());
322  if (dbi_conn_error (conn, &errmsg) != DBI_ERROR_NONE)
323  {
324  PWARN ("Index Table Retrieval Error: %s on table %s\n",
325  errmsg, table_name.c_str());
326  continue;
327  }
328 
329  while (dbi_result_next_row (result) != 0)
330  {
331  std::string index_name {dbi_result_get_string_idx (result, 3)};
332  retval.push_back(index_name + " " + table_name);
333  }
334  dbi_result_free (result);
335  }
336 
337  return retval;
338 }
339 
340 template<> StrVec
342 {
343  StrVec retval;
344  const char* errmsg;
345  PINFO ("Retrieving postgres index list\n");
346  auto result = dbi_conn_query (conn,
347  "SELECT relname FROM pg_class AS a INNER JOIN pg_index AS b ON (b.indexrelid = a.oid) INNER JOIN pg_namespace AS c ON (a.relnamespace = c.oid) WHERE reltype = '0' AND indisprimary = 'f' AND nspname = 'public'");
348  if (dbi_conn_error (conn, &errmsg) != DBI_ERROR_NONE)
349  {
350  PWARN("Index Table Retrieval Error: %s\n", errmsg);
351  return retval;
352  }
353  while (dbi_result_next_row (result) != 0)
354  {
355  std::string index_name {dbi_result_get_string_idx (result, 1)};
356  retval.push_back(index_name);
357  }
358  dbi_result_free (result);
359  return retval;
360 }
361 
362 template <DbType P> void
363 GncDbiProviderImpl<P>::drop_index(dbi_conn conn, const std::string& index)
364 {
365  dbi_result result = dbi_conn_queryf (conn, "DROP INDEX %s", index.c_str());
366  if (result)
367  dbi_result_free (result);
368 }
369 
370 template<> void
371 GncDbiProviderImpl<DbType::DBI_MYSQL>::drop_index (dbi_conn conn, const std::string& index)
372 {
373 
374  auto sep = index.find(' ', 0);
375  if (index.find(' ', sep + 1) != std::string::npos)
376  {
377  PWARN("Drop index error: invalid MySQL index format (<index> <table>): %s",
378  index.c_str());
379  return;
380  }
381 
382  auto result = dbi_conn_queryf (conn, "DROP INDEX %s ON %s",
383  index.substr(0, sep).c_str(),
384  index.substr(sep + 1).c_str());
385  if (result)
386  dbi_result_free (result);
387 }
388 #endif //__GNC_DBISQLPROVIDERIMPL_HPP__
information required to create a column in a table.
load and save data to SQL via libdbi
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250