FOSSology  4.4.0
Open Source License Compliance by Open Source Software
libfossdbmanager.c
1 /*
2  Author: Daniele Fognini
3  SPDX-FileCopyrightText: © 2014-2015 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 
8 #include <glib.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <libfossdbmanager.h>
15 #include <libfossdb.h>
16 
18 typedef struct
19 {
20  int type;
21  char* name;
22  char* fmt;
23 } param;
24 
27 {
30  char* name;
31  int paramc;
32 };
33 
36 {
37  PGconn* dbConnection;
38  GHashTable* cachedPrepared;
39  char* dbConf;
40  FILE* logFile;
42 };
43 
45 #define LOG(level, str, ...) \
46  do {\
47  FILE* logFile = dbManager->logFile; \
48  if (logFile) \
49  fprintf(logFile, level ": " str, __VA_ARGS__); \
50  else \
51  printf(level ": " str, __VA_ARGS__); \
52  } while(0)
53 
55 #define LOG_ERROR(str, ...) LOG("ERROR", str, __VA_ARGS__)
57 #define LOG_FATAL(str, ...) LOG("FATAL", str, __VA_ARGS__)
58 
59 #ifdef DEBUG
61 #define LOG_DEBUG(str, ...) LOG("DEBUG", str, __VA_ARGS__)
62 #else
63 #define LOG_DEBUG(str, ...) \
64  do {\
65  } while(0)
66 #endif
67 
74 static void cachedPrepared_free(gpointer ptr)
75 {
77  free(stmt->name);
78  free(stmt->params);
79  free(stmt);
80 }
81 
87 static void noticeReceiver(void* arg, const PGresult* res) {
88  fo_dbManager* dbManager = arg;
89  char* message = PQresultErrorMessage(res);
90 
91  if (!dbManager->ignoreWarns)
92  LOG("NOTICE", "%s", message);
93 };
94 
102 fo_dbManager* fo_dbManager_new_withConf(PGconn* dbConnection, const char* dbConf)
103 {
104  fo_dbManager* dbManager = fo_dbManager_new(dbConnection);
105  dbManager->dbConf = g_strdup(dbConf);
106  return dbManager;
107 }
108 
114 fo_dbManager* fo_dbManager_new(PGconn* dbConnection)
115 {
116  fo_dbManager* result = malloc(sizeof(fo_dbManager));
117 
118  result->cachedPrepared = g_hash_table_new_full(
119  g_str_hash,
120  g_str_equal,
121  NULL, // the key is the same pointer as statement->name
122  cachedPrepared_free
123  );
124 
125  result->dbConnection = dbConnection;
126  result->logFile = NULL;
127  result->dbConf = NULL;
128  result->ignoreWarns = 0;
129 
130  PQsetNoticeReceiver(dbConnection, noticeReceiver, result);
131 
132  return result;
133 }
134 
159 fo_dbManager* fo_dbManager_fork(fo_dbManager* dbManager)
160 {
161  fo_dbManager* result = NULL;
162  char* error = NULL;
163  PGconn* newDbConnection = fo_dbconnect(dbManager->dbConf, &error);
164  if (newDbConnection)
165  {
166  result = fo_dbManager_new_withConf(newDbConnection, dbManager->dbConf);
167  } else
168  {
169  LOG_FATAL("Can not open connection\n%s\nWhile forking dbManager using config: '%s'\n",
170  error, dbManager->dbConf);
171  free(error);
172  }
173  return result;
174 }
175 
182 int fo_dbManager_setLogFile(fo_dbManager* dbManager, const char* logFileName)
183 {
184  if (dbManager->logFile)
185  fclose(dbManager->logFile);
186 
187  if (logFileName)
188  {
189  dbManager->logFile = fopen(logFileName, "a");
190  return dbManager->logFile != NULL;
191  } else
192  {
193  dbManager->logFile = NULL;
194  return 1;
195  }
196 }
197 
203 void fo_dbManager_ignoreWarnings(fo_dbManager* dbManager, int ignoreWarns)
204 {
205  dbManager->ignoreWarns = ignoreWarns;
206 }
207 
213 PGconn* fo_dbManager_getWrappedConnection(fo_dbManager* dbManager)
214 {
215  return dbManager->dbConnection;
216 }
217 
228 {
229  g_hash_table_unref(dbManager->cachedPrepared);
230  if (dbManager->dbConf)
231  free(dbManager->dbConf);
232  if (dbManager->logFile)
233  fclose(dbManager->logFile);
234  free(dbManager);
235 }
236 
244 void fo_dbManager_finish(fo_dbManager* dbManager)
245 {
246  PQfinish(dbManager->dbConnection);
248 }
249 
258 static char* array_print(char** parameters, int count)
259 {
260  GString* resultCreator = g_string_new("{");
261  int i;
262  for (i = 0; i < count; i++)
263  {
264  if (i > 0)
265  g_string_append(resultCreator, ", ");
266  g_string_append_printf(resultCreator, "[%d]='%s'", i, parameters[i]);
267  }
268  g_string_append(resultCreator, "}");
269  return g_string_free(resultCreator, FALSE);
270 }
271 
277 static void array_free(char** parameters, int count)
278 {
279  int i;
280  for (i = 0; i < count; i++)
281  free(parameters[i]);
282  free(parameters);
283 }
284 
289 param supported[] = {
290 #define ADDSUPPORTED(n, type, fmt) \
291  {n, #type, fmt},
292  ADDSUPPORTED(0, long, "%ld")
293  ADDSUPPORTED(1, int, "%d")
294  ADDSUPPORTED(2, char*, "%s")
295  ADDSUPPORTED(3, size_t, "%zu")
296  ADDSUPPORTED(4, unsigned, "%u")
297  ADDSUPPORTED(5, unsigned int, "%u")
298  ADDSUPPORTED(6, unsigned long, "%lu")
299 #undef ADDSUPPORTED
300  {0, NULL, NULL},
301 };
302 
310 static inline char** buildStringArray(int paramCount, param* params, va_list vars)
311 {
312  char** result = malloc(sizeof(char*) * paramCount);
313  int i;
314  for (i = 0; i < paramCount; i++)
315  {
316  param currentParamDesc = params[i];
317  int type = currentParamDesc.type;
318  char* format = currentParamDesc.fmt;
319  switch (type)
320  {
321 #define ADDCASE(n, type) \
322  case n:\
323  { \
324  type t = va_arg(vars, type);\
325  result[i] = g_strdup_printf(format, t);\
326  }\
327  break;
328  ADDCASE(0, long)
329  ADDCASE(1, int)
330  ADDCASE(2, char*)
331  ADDCASE(3, size_t)
332  ADDCASE(4, unsigned)
333  ADDCASE(5, unsigned int)
334  ADDCASE(6, unsigned long)
335 #undef ADDCASE
336  default:
337  printf("internal error on typeid=%d\n", type);
338  array_free(result, i - 1);
339  return NULL;
340  }
341  }
342 
343  return result;
344 }
345 
359 char* fo_dbManager_printStatement(fo_dbManager_PreparedStatement* preparedStatement)
360 {
361  GString* resultCreator = g_string_new("");
362  g_string_append_printf(resultCreator,
363  "{ name: '%s', parameterTypes: [",
364  preparedStatement->name);
365  int i;
366  for (i = 0; i < preparedStatement->paramc; i++)
367  {
368  param current = preparedStatement->params[i];
369  if (i > 0) g_string_append(resultCreator, ", ");
370  g_string_append_printf(resultCreator,
371  "[%d]={%s, %s}",
372  i, current.name, current.fmt);
373  }
374  g_string_append_printf(resultCreator, "]}");
375  return g_string_free(resultCreator, FALSE);
376 }
377 
384 int fo_dbManager_begin(fo_dbManager* dbManager)
385 {
386  int result = 0;
387  PGresult* queryResult = fo_dbManager_Exec_printf(dbManager, "BEGIN");
388  if (queryResult)
389  {
390  result = 1;
391  PQclear(queryResult);
392  }
393  return result;
394 }
395 
402 int fo_dbManager_commit(fo_dbManager* dbManager)
403 {
404  int result = 0;
405  PGresult* queryResult = fo_dbManager_Exec_printf(dbManager, "COMMIT");
406  if (queryResult)
407  {
408  result = 1;
409  PQclear(queryResult);
410  }
411  return result;
412 }
413 
420 int fo_dbManager_rollback(fo_dbManager* dbManager)
421 {
422  int result = 0;
423  PGresult* queryResult = fo_dbManager_Exec_printf(dbManager, "ROLLBACK");
424  if (queryResult)
425  {
426  result = 1;
427  PQclear(queryResult);
428  }
429  return result;
430 }
431 
440 int fo_dbManager_tableExists(fo_dbManager* dbManager, const char* tableName)
441 {
442  return fo_dbManager_exists(dbManager, "table", tableName);
443 }
444 
454 int fo_dbManager_exists(fo_dbManager* dbManager, const char* type, const char* name)
455 {
456  int result = 0;
457 
458  char* escapedName = fo_dbManager_StringEscape(dbManager, name);
459 
460  if (escapedName)
461  {
462  PGresult* queryResult = fo_dbManager_Exec_printf(
463  dbManager,
464  "select count(*) from information_schema.%ss where %s_catalog='%s' and %s_name='%s'",
465  type, type,
466  PQdb(dbManager->dbConnection),
467  type,
468  escapedName
469  );
470 
471  if (queryResult)
472  {
473  if (PQntuples(queryResult) == 1)
474  {
475  if (atol(PQgetvalue(queryResult, 0, 0)) == 1)
476  {
477  result = 1;
478  }
479  }
480  PQclear(queryResult);
481  }
482  free(escapedName);
483  }
484 
485  return result;
486 }
487 
507 PGresult* fo_dbManager_Exec_printf(fo_dbManager* dbManager, const char* sqlQueryStringFormat, ...)
508 {
509  char* sqlQueryString;
510  PGconn* dbConnection = dbManager->dbConnection;
511 
512  va_list argptr;
513  va_start(argptr, sqlQueryStringFormat);
514  sqlQueryString = g_strdup_vprintf(sqlQueryStringFormat, argptr);
515  va_end(argptr);
516  if (sqlQueryString == NULL)
517  {
518  return NULL;
519  }
520 
521  PGresult* result = PQexec(dbConnection, sqlQueryString);
522 
523  if (!result)
524  {
525  LOG_FATAL("%sOn: %s\n", PQerrorMessage(dbConnection), sqlQueryString);
526  g_free(sqlQueryString);
527  PQclear(result);
528  return NULL;
529  }
530  if (PQresultStatus(result) == PGRES_FATAL_ERROR)
531  {
532  LOG_ERROR("%sOn: %s\n", PQresultErrorMessage(result), sqlQueryString);
533  g_free(sqlQueryString);
534  PQclear(result);
535  return NULL;
536  }
537  g_free(sqlQueryString);
538 
539  return result;
540 }
541 
550 char* fo_dbManager_StringEscape(fo_dbManager* dbManager, const char* string)
551 {
552  size_t length = strlen(string);
553  char* dest = malloc(2 * length + 1);
554 
555  int err;
556  PQescapeStringConn(dbManager->dbConnection, dest, string, length, &err);
557  if (err == 0)
558  {
559  return dest;
560  } else
561  {
562  free(dest);
563  return NULL;
564  }
565 }
566 
573 PGresult* fo_dbManager_ExecPrepared(fo_dbManager_PreparedStatement* preparedStatement, ...)
574 {
575  if (!preparedStatement)
576  {
577  return NULL;
578  }
579  va_list vars;
580  va_start(vars, preparedStatement);
581  PGresult* result = fo_dbManager_ExecPreparedv(preparedStatement, vars);
582  va_end(vars);
583 
584  return result;
585 }
586 
594 PGresult* fo_dbManager_ExecPreparedv(fo_dbManager_PreparedStatement* preparedStatement, va_list args)
595 {
596  if (!preparedStatement)
597  {
598  return NULL;
599  }
600 
601  char** parameters = buildStringArray(preparedStatement->paramc, preparedStatement->params, args);
602 
603  fo_dbManager* dbManager = preparedStatement->dbManager;
604  PGconn* dbConnection = dbManager->dbConnection;
605 
606 #ifdef DEBUG
607  char* printedStatement = fo_dbManager_printStatement(preparedStatement);
608  char* params = array_print(parameters, preparedStatement->paramc);
609  LOG_DEBUG("Exec prepared '%s' with params '%s'\n",
610  printedStatement,
611  params);
612  g_free(printedStatement);
613  g_free(params);
614 #endif
615  PGresult* result = PQexecPrepared(dbConnection,
616  preparedStatement->name,
617  preparedStatement->paramc,
618  (const char* const*) parameters,
619  NULL,
620  NULL,
621  0);
622 
623  if (!result)
624  {
625  char* printedStatement = fo_dbManager_printStatement(preparedStatement);
626  char* params = array_print(parameters, preparedStatement->paramc);
627  LOG_FATAL("%sExecuting prepared '%s' with params %s\n",
628  PQerrorMessage(dbConnection),
629  printedStatement,
630  params);
631  g_free(printedStatement);
632  g_free(params);
633  } else if (PQresultStatus(result) == PGRES_FATAL_ERROR)
634  {
635  char* printedStatement = fo_dbManager_printStatement(preparedStatement);
636  char* params = array_print(parameters, preparedStatement->paramc);
637  LOG_ERROR("%sExecuting prepared '%s' with params %s\n",
638  PQresultErrorMessage(result),
639  printedStatement,
640  params);
641  g_free(printedStatement);
642  g_free(params);
643 
644  PQclear(result);
645  result = NULL;
646  }
647 
648  array_free(parameters, preparedStatement->paramc);
649 
650  return result;
651 }
652 
660 static inline int parseParamStr_equals(const char* a, const char* b, size_t bLength)
661 {
662  const char* ptrA = a;
663  const char* ptrB = b;
664  size_t lenB = 0;
665 
666  while (*ptrA && lenB < bLength)
667  {
668  if (isspace(*ptrA))
669  {
670  if (!isspace(*ptrB))
671  return 0;
672  while (isspace(*ptrB) && lenB < bLength)
673  {
674  ++ptrB;
675  ++lenB;
676  }
677  ++ptrA;
678  }
679  if ((lenB == bLength) || (*ptrB != *ptrA))
680  return 0;
681  ++ptrA;
682  ++ptrB;
683  ++lenB;
684  }
685 
686  return (!(*ptrA) && (lenB == bLength));
687 }
688 
697 static inline int parseParamStr_set(const char* type, size_t length, param* dest)
698 {
699  param* ptr = supported;
700  while (ptr->fmt)
701  {
702  if (parseParamStr_equals(ptr->name, type, length))
703  {
704  *dest = *ptr;
705  return 1;
706  }
707  ptr++;
708  }
709  return 0;
710 }
711 
719 int fo_dbManager_parseParamStr(const char* paramtypes, GArray** params)
720 {
721  *params = g_array_new(TRUE, FALSE, sizeof(param));
722  GArray* paramsG = *params;
723 
724  const char* ptr = paramtypes;
725  size_t currentLength = 0;
726  const char* currentStart;
727  const char* nextStart = ptr;
728  int success = 1;
729  while (*ptr)
730  {
731  // eat all starting whitespace
732  while (*ptr && (isspace(*ptr)))
733  ++ptr;
734  currentStart = ptr;
735  currentLength = 0;
736  // go till the next comma
737  while (*ptr && *ptr != ',')
738  {
739  ++currentLength;
740  ++ptr;
741  }
742  nextStart = ptr;
743 
744  // if this token in empty we are done
745  if (ptr == currentStart)
746  break;
747 
748  --ptr;
749  while (ptr != currentStart && isspace(*ptr))
750  {
751  --currentLength;
752  --ptr;
753  }
754 
755  // we found a real token: add it
756  {
757  param next;
758  if (parseParamStr_set(currentStart, currentLength, &next))
759  {
760  g_array_append_val(paramsG, next);
761  } else
762  {
763  success = 0;
764  break;
765  }
766  }
767 
768  // now go to the next token, nextStart is at the comma (or end)
769  ptr = nextStart;
770  if (*ptr)
771  ptr++;
772  }
773 
774  // parsing terminated too early
775  if (*nextStart)
776  success = 0;
777 
778  if (!success)
779  g_array_set_size(paramsG, 0);
780 
781  return success;
782 }
783 
791 static inline int parseParamStr(fo_dbManager_PreparedStatement* statement, const char* paramtypes)
792 {
793  GArray* paramsG;
794  int success = fo_dbManager_parseParamStr(paramtypes, &paramsG);
795 
796  statement->paramc = paramsG->len;
797  statement->params = (param*) g_array_free(paramsG, FALSE);
798  return success;
799 }
800 
812  fo_dbManager* dbManager, const char* name, const char* query, const char* paramtypes
813 )
814 {
815  GHashTable* cachedPrepared = dbManager->cachedPrepared;
816  fo_dbManager_PreparedStatement* cached = g_hash_table_lookup(cachedPrepared, name);
817 
818  if (cached)
819  {
820  LOG_DEBUG("returning cached statement '%s'\n", cached->name);
821  return cached;
822  }
823 
825 
826  PGconn* dbConnection = dbManager->dbConnection;
827 
828  result->dbManager = dbManager;
829  result->name = g_strdup(name);
830 
831  int failure = 0;
832  if (parseParamStr(result, paramtypes))
833  {
834  PGresult* prepareResult = PQprepare(dbConnection, result->name, query, 0, NULL);
835 
836  if (!prepareResult)
837  {
838  char* printedStatement = fo_dbManager_printStatement(result);
839  LOG_FATAL("%sPreparing of '%s' AS '%s'\n",
840  PQerrorMessage(dbConnection),
841  printedStatement,
842  query);
843  free(printedStatement);
844  failure = 1;
845  } else
846  {
847  if (PQresultStatus(prepareResult) != PGRES_COMMAND_OK)
848  {
849  char* printedStatement = fo_dbManager_printStatement(result);
850  LOG_ERROR("%sPreparing of '%s' AS '%s'\n",
851  PQresultErrorMessage(prepareResult),
852  printedStatement,
853  query);
854  free(printedStatement);
855  failure = 1;
856  }
857  PQclear(prepareResult);
858  }
859  } else
860  {
861  LOG_FATAL("dbManager could not comprehend parameter types '%s'\n"
862  "Trying to prepare '%s' as '%s'\n", paramtypes, name, query);
863  failure = 1;
864  }
865 
866  if (failure)
867  {
868  cachedPrepared_free(result);
869  result = NULL;
870  } else
871  {
872  g_hash_table_insert(cachedPrepared, result->name, result);
873  }
874 
875  return result;
876 }
int s
The socket that the CLI will use to communicate.
Definition: fo_cli.c:37
PGconn * fo_dbconnect(char *DBConfFile, char **ErrorBuf)
Connect to a database. The default is Db.conf.
Definition: libfossdb.c:29
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
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:35
PGresult * fo_dbManager_ExecPrepared(fo_dbManager_PreparedStatement *preparedStatement,...)
Execute a prepared statement.
Definition: standalone.c:36
fo_dbManager * fo_dbManager_new(PGconn *dbConnection)
Create and initialize new fo_dbManager object.
Definition: standalone.c:33
void fo_dbManager_free(fo_dbManager *dbManager)
Un-allocate the memory from a DB manager.
Definition: standalone.c:34
int paramc
Number of paramenters.
param * params
Query parameters.
char * name
Name of the prepared statement.
fo_dbManager * dbManager
DB manager.
PGconn * dbConnection
Postgres database connection object.
GHashTable * cachedPrepared
Hash table of prepared statements.
int ignoreWarns
Set to ignore warnings from logging.
char * dbConf
DB conf file location.
FILE * logFile
FOSSology log file pointer.
int type
Type of parameter, check buildStringArray() for more.
char * fmt
Printf format string for the parameter.
char * name
Name of the parameter.