FOSSology  3.2.0rc1
Open Source License Compliance by Open Source Software
libfossdbmanager.c
1 /*
2 Author: Daniele Fognini
3 Copyright (C) 2014-2015, Siemens AG
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 version 2 as published by the Free Software Foundation.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 
19 #include <glib.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <libfossdbmanager.h>
26 #include <libfossdb.h>
27 
29 typedef struct
30 {
31  int type;
32  char* name;
33  char* fmt;
34 } param;
35 
38 {
41  char* name;
42  int paramc;
43 };
44 
47 {
48  PGconn* dbConnection;
49  GHashTable* cachedPrepared;
50  char* dbConf;
51  FILE* logFile;
53 };
54 
56 #define LOG(level, str, ...) \
57  do {\
58  FILE* logFile = dbManager->logFile; \
59  if (logFile) \
60  fprintf(logFile, level ": " str, __VA_ARGS__); \
61  else \
62  printf(level ": " str, __VA_ARGS__); \
63  } while(0)
64 
66 #define LOG_ERROR(str, ...) LOG("ERROR", str, __VA_ARGS__)
67 
68 #define LOG_FATAL(str, ...) LOG("FATAL", str, __VA_ARGS__)
69 
70 #ifdef DEBUG
71 
72 #define LOG_DEBUG(str, ...) LOG("DEBUG", str, __VA_ARGS__)
73 #else
74 #define LOG_DEBUG(str, ...) \
75  do {\
76  } while(0)
77 #endif
78 
85 static void cachedPrepared_free(gpointer ptr)
86 {
88  free(stmt->name);
89  free(stmt->params);
90  free(stmt);
91 }
92 
98 static void noticeReceiver(void* arg, const PGresult* res) {
99  fo_dbManager* dbManager = arg;
100  char* message = PQresultErrorMessage(res);
101 
102  if (!dbManager->ignoreWarns)
103  LOG("NOTICE", "%s", message);
104 };
105 
113 fo_dbManager* fo_dbManager_new_withConf(PGconn* dbConnection, const char* dbConf)
114 {
115  fo_dbManager* dbManager = fo_dbManager_new(dbConnection);
116  dbManager->dbConf = g_strdup(dbConf);
117  return dbManager;
118 }
119 
125 fo_dbManager* fo_dbManager_new(PGconn* dbConnection)
126 {
127  fo_dbManager* result = malloc(sizeof(fo_dbManager));
128 
129  result->cachedPrepared = g_hash_table_new_full(
130  g_str_hash,
131  g_str_equal,
132  NULL, // the key is the same pointer as statement->name
133  cachedPrepared_free
134  );
135 
136  result->dbConnection = dbConnection;
137  result->logFile = NULL;
138  result->dbConf = NULL;
139  result->ignoreWarns = 0;
140 
141  PQsetNoticeReceiver(dbConnection, noticeReceiver, result);
142 
143  return result;
144 }
145 
170 fo_dbManager* fo_dbManager_fork(fo_dbManager* dbManager)
171 {
172  fo_dbManager* result = NULL;
173  char* error = NULL;
174  PGconn* newDbConnection = fo_dbconnect(dbManager->dbConf, &error);
175  if (newDbConnection)
176  {
177  result = fo_dbManager_new_withConf(newDbConnection, dbManager->dbConf);
178  } else
179  {
180  LOG_FATAL("Can not open connection\n%s\nWhile forking dbManager using config: '%s'\n",
181  error, dbManager->dbConf);
182  free(error);
183  }
184  return result;
185 }
186 
193 int fo_dbManager_setLogFile(fo_dbManager* dbManager, const char* logFileName)
194 {
195  if (dbManager->logFile)
196  fclose(dbManager->logFile);
197 
198  if (logFileName)
199  {
200  dbManager->logFile = fopen(logFileName, "a");
201  return dbManager->logFile != NULL;
202  } else
203  {
204  dbManager->logFile = NULL;
205  return 1;
206  }
207 }
208 
214 void fo_dbManager_ignoreWarnings(fo_dbManager* dbManager, int ignoreWarns)
215 {
216  dbManager->ignoreWarns = ignoreWarns;
217 }
218 
224 PGconn* fo_dbManager_getWrappedConnection(fo_dbManager* dbManager)
225 {
226  return dbManager->dbConnection;
227 }
228 
238 void fo_dbManager_free(fo_dbManager* dbManager)
239 {
240  g_hash_table_unref(dbManager->cachedPrepared);
241  if (dbManager->dbConf)
242  free(dbManager->dbConf);
243  if (dbManager->logFile)
244  fclose(dbManager->logFile);
245  free(dbManager);
246 }
247 
255 void fo_dbManager_finish(fo_dbManager* dbManager)
256 {
257  PQfinish(dbManager->dbConnection);
258  fo_dbManager_free(dbManager);
259 }
260 
269 static char* array_print(char** parameters, int count)
270 {
271  GString* resultCreator = g_string_new("{");
272  int i;
273  for (i = 0; i < count; i++)
274  {
275  if (i > 0)
276  g_string_append(resultCreator, ", ");
277  g_string_append_printf(resultCreator, "[%d]='%s'", i, parameters[i]);
278  }
279  g_string_append(resultCreator, "}");
280  return g_string_free(resultCreator, FALSE);
281 }
282 
288 static void array_free(char** parameters, int count)
289 {
290  int i;
291  for (i = 0; i < count; i++)
292  free(parameters[i]);
293  free(parameters);
294 }
295 
300 param supported[] = {
301 #define ADDSUPPORTED(n, type, fmt) \
302  {n, #type, fmt},
303  ADDSUPPORTED(0, long, "%ld")
304  ADDSUPPORTED(1, int, "%d")
305  ADDSUPPORTED(2, char*, "%s")
306  ADDSUPPORTED(3, size_t, "%zu")
307  ADDSUPPORTED(4, unsigned, "%u")
308  ADDSUPPORTED(5, unsigned int, "%u")
309  ADDSUPPORTED(6, unsigned long, "%lu")
310 #undef ADDSUPPORTED
311  {0, NULL, NULL},
312 };
313 
321 static inline char** buildStringArray(int paramCount, param* params, va_list vars)
322 {
323  char** result = malloc(sizeof(char*) * paramCount);
324  int i;
325  for (i = 0; i < paramCount; i++)
326  {
327  param currentParamDesc = params[i];
328  int type = currentParamDesc.type;
329  char* format = currentParamDesc.fmt;
330  switch (type)
331  {
332 #define ADDCASE(n, type) \
333  case n:\
334  { \
335  type t = va_arg(vars, type);\
336  result[i] = g_strdup_printf(format, t);\
337  }\
338  break;
339  ADDCASE(0, long)
340  ADDCASE(1, int)
341  ADDCASE(2, char*)
342  ADDCASE(3, size_t)
343  ADDCASE(4, unsigned)
344  ADDCASE(5, unsigned int)
345  ADDCASE(6, unsigned long)
346 #undef ADDCASE
347  default:
348  printf("internal error on typeid=%d\n", type);
349  array_free(result, i - 1);
350  return NULL;
351  }
352  }
353 
354  return result;
355 }
356 
370 char* fo_dbManager_printStatement(fo_dbManager_PreparedStatement* preparedStatement)
371 {
372  GString* resultCreator = g_string_new("");
373  g_string_append_printf(resultCreator,
374  "{ name: '%s', parameterTypes: [",
375  preparedStatement->name);
376  int i;
377  for (i = 0; i < preparedStatement->paramc; i++)
378  {
379  param current = preparedStatement->params[i];
380  if (i > 0) g_string_append(resultCreator, ", ");
381  g_string_append_printf(resultCreator,
382  "[%d]={%s, %s}",
383  i, current.name, current.fmt);
384  }
385  g_string_append_printf(resultCreator, "]}");
386  return g_string_free(resultCreator, FALSE);
387 }
388 
395 int fo_dbManager_begin(fo_dbManager* dbManager)
396 {
397  int result = 0;
398  PGresult* queryResult = fo_dbManager_Exec_printf(dbManager, "BEGIN");
399  if (queryResult)
400  {
401  result = 1;
402  PQclear(queryResult);
403  }
404  return result;
405 }
406 
413 int fo_dbManager_commit(fo_dbManager* dbManager)
414 {
415  int result = 0;
416  PGresult* queryResult = fo_dbManager_Exec_printf(dbManager, "COMMIT");
417  if (queryResult)
418  {
419  result = 1;
420  PQclear(queryResult);
421  }
422  return result;
423 }
424 
431 int fo_dbManager_rollback(fo_dbManager* dbManager)
432 {
433  int result = 0;
434  PGresult* queryResult = fo_dbManager_Exec_printf(dbManager, "ROLLBACK");
435  if (queryResult)
436  {
437  result = 1;
438  PQclear(queryResult);
439  }
440  return result;
441 }
442 
451 int fo_dbManager_tableExists(fo_dbManager* dbManager, const char* tableName)
452 {
453  return fo_dbManager_exists(dbManager, "table", tableName);
454 }
455 
465 int fo_dbManager_exists(fo_dbManager* dbManager, const char* type, const char* name)
466 {
467  int result = 0;
468 
469  char* escapedName = fo_dbManager_StringEscape(dbManager, name);
470 
471  if (escapedName)
472  {
473  PGresult* queryResult = fo_dbManager_Exec_printf(
474  dbManager,
475  "select count(*) from information_schema.%ss where %s_catalog='%s' and %s_name='%s'",
476  type, type,
477  PQdb(dbManager->dbConnection),
478  type,
479  escapedName
480  );
481 
482  if (queryResult)
483  {
484  if (PQntuples(queryResult) == 1)
485  {
486  if (atol(PQgetvalue(queryResult, 0, 0)) == 1)
487  {
488  result = 1;
489  }
490  }
491  PQclear(queryResult);
492  }
493  free(escapedName);
494  }
495 
496  return result;
497 }
498 
518 PGresult* fo_dbManager_Exec_printf(fo_dbManager* dbManager, const char* sqlQueryStringFormat, ...)
519 {
520  char* sqlQueryString;
521  PGconn* dbConnection = dbManager->dbConnection;
522 
523  va_list argptr;
524  va_start(argptr, sqlQueryStringFormat);
525  sqlQueryString = g_strdup_vprintf(sqlQueryStringFormat, argptr);
526  va_end(argptr);
527  if (sqlQueryString == NULL)
528  {
529  return NULL;
530  }
531 
532  PGresult* result = PQexec(dbConnection, sqlQueryString);
533 
534  if (!result)
535  {
536  LOG_FATAL("%sOn: %s\n", PQerrorMessage(dbConnection), sqlQueryString);
537  g_free(sqlQueryString);
538  PQclear(result);
539  return NULL;
540  }
541  if (PQresultStatus(result) == PGRES_FATAL_ERROR)
542  {
543  LOG_ERROR("%sOn: %s\n", PQresultErrorMessage(result), sqlQueryString);
544  g_free(sqlQueryString);
545  PQclear(result);
546  return NULL;
547  }
548  g_free(sqlQueryString);
549 
550  return result;
551 }
552 
561 char* fo_dbManager_StringEscape(fo_dbManager* dbManager, const char* string)
562 {
563  size_t length = strlen(string);
564  char* dest = malloc(2 * length + 1);
565 
566  int err;
567  PQescapeStringConn(dbManager->dbConnection, dest, string, length, &err);
568  if (err == 0)
569  {
570  return dest;
571  } else
572  {
573  free(dest);
574  return NULL;
575  }
576 }
577 
584 PGresult* fo_dbManager_ExecPrepared(fo_dbManager_PreparedStatement* preparedStatement, ...)
585 {
586  if (!preparedStatement)
587  {
588  return NULL;
589  }
590  va_list vars;
591  va_start(vars, preparedStatement);
592  PGresult* result = fo_dbManager_ExecPreparedv(preparedStatement, vars);
593  va_end(vars);
594 
595  return result;
596 }
597 
605 PGresult* fo_dbManager_ExecPreparedv(fo_dbManager_PreparedStatement* preparedStatement, va_list args)
606 {
607  if (!preparedStatement)
608  {
609  return NULL;
610  }
611 
612  char** parameters = buildStringArray(preparedStatement->paramc, preparedStatement->params, args);
613 
614  fo_dbManager* dbManager = preparedStatement->dbManager;
615  PGconn* dbConnection = dbManager->dbConnection;
616 
617 #ifdef DEBUG
618  char* printedStatement = fo_dbManager_printStatement(preparedStatement);
619  char* params = array_print(parameters, preparedStatement->paramc);
620  LOG_DEBUG("Exec prepared '%s' with params '%s'\n",
621  printedStatement,
622  params);
623  g_free(printedStatement);
624  g_free(params);
625 #endif
626  PGresult* result = PQexecPrepared(dbConnection,
627  preparedStatement->name,
628  preparedStatement->paramc,
629  (const char* const*) parameters,
630  NULL,
631  NULL,
632  0);
633 
634  if (!result)
635  {
636  char* printedStatement = fo_dbManager_printStatement(preparedStatement);
637  char* params = array_print(parameters, preparedStatement->paramc);
638  LOG_FATAL("%sExecuting prepared '%s' with params %s\n",
639  PQerrorMessage(dbConnection),
640  printedStatement,
641  params);
642  g_free(printedStatement);
643  g_free(params);
644  } else if (PQresultStatus(result) == PGRES_FATAL_ERROR)
645  {
646  char* printedStatement = fo_dbManager_printStatement(preparedStatement);
647  char* params = array_print(parameters, preparedStatement->paramc);
648  LOG_ERROR("%sExecuting prepared '%s' with params %s\n",
649  PQresultErrorMessage(result),
650  printedStatement,
651  params);
652  g_free(printedStatement);
653  g_free(params);
654 
655  PQclear(result);
656  result = NULL;
657  }
658 
659  array_free(parameters, preparedStatement->paramc);
660 
661  return result;
662 }
663 
671 static inline int parseParamStr_equals(const char* a, const char* b, size_t bLength)
672 {
673  const char* ptrA = a;
674  const char* ptrB = b;
675  size_t lenB = 0;
676 
677  while (*ptrA && lenB < bLength)
678  {
679  if (isspace(*ptrA))
680  {
681  if (!isspace(*ptrB))
682  return 0;
683  while (isspace(*ptrB) && lenB < bLength)
684  {
685  ++ptrB;
686  ++lenB;
687  }
688  ++ptrA;
689  }
690  if ((lenB == bLength) || (*ptrB != *ptrA))
691  return 0;
692  ++ptrA;
693  ++ptrB;
694  ++lenB;
695  }
696 
697  return (!(*ptrA) && (lenB == bLength));
698 }
699 
708 static inline int parseParamStr_set(const char* type, size_t length, param* dest)
709 {
710  param* ptr = supported;
711  while (ptr->fmt)
712  {
713  if (parseParamStr_equals(ptr->name, type, length))
714  {
715  *dest = *ptr;
716  return 1;
717  }
718  ptr++;
719  }
720  return 0;
721 }
722 
730 int fo_dbManager_parseParamStr(const char* paramtypes, GArray** params)
731 {
732  *params = g_array_new(TRUE, FALSE, sizeof(param));
733  GArray* paramsG = *params;
734 
735  const char* ptr = paramtypes;
736  size_t currentLength = 0;
737  const char* currentStart;
738  const char* nextStart = ptr;
739  int success = 1;
740  while (*ptr)
741  {
742  // eat all starting whitespace
743  while (*ptr && (isspace(*ptr)))
744  ++ptr;
745  currentStart = ptr;
746  currentLength = 0;
747  // go till the next comma
748  while (*ptr && *ptr != ',')
749  {
750  ++currentLength;
751  ++ptr;
752  }
753  nextStart = ptr;
754 
755  // if this token in empty we are done
756  if (ptr == currentStart)
757  break;
758 
759  --ptr;
760  while (ptr != currentStart && isspace(*ptr))
761  {
762  --currentLength;
763  --ptr;
764  }
765 
766  // we found a real token: add it
767  {
768  param next;
769  if (parseParamStr_set(currentStart, currentLength, &next))
770  {
771  g_array_append_val(paramsG, next);
772  } else
773  {
774  success = 0;
775  break;
776  }
777  }
778 
779  // now go to the next token, nextStart is at the comma (or end)
780  ptr = nextStart;
781  if (*ptr)
782  ptr++;
783  }
784 
785  // parsing terminated too early
786  if (*nextStart)
787  success = 0;
788 
789  if (!success)
790  g_array_set_size(paramsG, 0);
791 
792  return success;
793 }
794 
802 static inline int parseParamStr(fo_dbManager_PreparedStatement* statement, const char* paramtypes)
803 {
804  GArray* paramsG;
805  int success = fo_dbManager_parseParamStr(paramtypes, &paramsG);
806 
807  statement->paramc = paramsG->len;
808  statement->params = (param*) g_array_free(paramsG, FALSE);
809  return success;
810 }
811 
823  fo_dbManager* dbManager, const char* name, const char* query, const char* paramtypes
824 )
825 {
826  GHashTable* cachedPrepared = dbManager->cachedPrepared;
827  fo_dbManager_PreparedStatement* cached = g_hash_table_lookup(cachedPrepared, name);
828 
829  if (cached)
830  {
831  LOG_DEBUG("returning cached statement '%s'\n", cached->name);
832  return cached;
833  }
834 
836 
837  PGconn* dbConnection = dbManager->dbConnection;
838 
839  result->dbManager = dbManager;
840  result->name = g_strdup(name);
841 
842  int failure = 0;
843  if (parseParamStr(result, paramtypes))
844  {
845  PGresult* prepareResult = PQprepare(dbConnection, result->name, query, 0, NULL);
846 
847  if (!prepareResult)
848  {
849  char* printedStatement = fo_dbManager_printStatement(result);
850  LOG_FATAL("%sPreparing of '%s' AS '%s'\n",
851  PQerrorMessage(dbConnection),
852  printedStatement,
853  query);
854  free(printedStatement);
855  failure = 1;
856  } else
857  {
858  if (PQresultStatus(prepareResult) != PGRES_COMMAND_OK)
859  {
860  char* printedStatement = fo_dbManager_printStatement(result);
861  LOG_ERROR("%sPreparing of '%s' AS '%s'\n",
862  PQresultErrorMessage(prepareResult),
863  printedStatement,
864  query);
865  free(printedStatement);
866  failure = 1;
867  }
868  PQclear(prepareResult);
869  }
870  } else
871  {
872  LOG_FATAL("dbManager could not comprehend parameter types '%s'\n"
873  "Trying to prepare '%s' as '%s'\n", paramtypes, name, query);
874  failure = 1;
875  }
876 
877  if (failure)
878  {
879  cachedPrepared_free(result);
880  result = NULL;
881  } else
882  {
883  g_hash_table_insert(cachedPrepared, result->name, result);
884  }
885 
886  return result;
887 }
fo_dbManager_PreparedStatement * fo_dbManager_PrepareStamement_str(fo_dbManager *dbManager, const char *name, const char *query, const char *paramtypes)
Create a prepared statement.
Definition: standalone.c:30
char * fmt
Printf format string for the parameter.
fo_dbManager * dbManager
DB manager.
PGresult * fo_dbManager_ExecPrepared(fo_dbManager_PreparedStatement *preparedStatement,...)
Execute a prepared statement.
Definition: standalone.c:31
int ignoreWarns
Set to ignore warnings from logging.
param * params
Query parameters.
char * name
Name of the prepared statement.
char * dbConf
DB conf file location.
fo_dbManager * fo_dbManager_new(PGconn *dbConnection)
Create and initialize new fo_dbManager object.
Definition: standalone.c:28
FILE * logFile
FOSSology log file pointer.
PGconn * dbConnection
Postgres database connection object.
int paramc
Number of paramenters.
char * name
Name of the parameter.
void fo_dbManager_free(fo_dbManager *dbManager)
Un-allocate the memory from a DB manager.
Definition: standalone.c:29
int type
Type of parameter, check buildStringArray() for more.
PGconn * fo_dbconnect(char *DBConfFile, char **ErrorBuf)
Connect to a database. The default is Db.conf.
Definition: libfossdb.c:40
GHashTable * cachedPrepared
Hash table of prepared statements.