libdsdb3  3.0
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups
dqrdb.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2011 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision: 8058 $
16 * $Author: ermold $
17 * $Date: 2011-09-22 03:14:35 +0000 (Thu, 22 Sep 2011) $
18 * $Version: $
19 *
20 ********************************************************************************
21 *
22 * NOTE: DOXYGEN is used to generate documentation for this file.
23 *
24 *******************************************************************************/
25 
26 /** @file dqrdb.c
27  * Data Quality Report Database Interface.
28  */
29 
30 #include <unistd.h>
31 
32 #include "dsdb3.h"
33 
34 /**
35  * @defgroup DQRDB DQR Database
36  */
37 /*@{*/
38 
39 /*******************************************************************************
40  * Private Functions
41  */
42 /** @privatesection */
43 
44 static void _dqrdb_destroy_dqr(DQR *dqr)
45 {
46  if (dqr) {
47  if (dqr->id) free((void *)dqr->id);
48  if (dqr->desc) free((void *)dqr->desc);
49  if (dqr->ds_name) free((void *)dqr->ds_name);
50  if (dqr->var_name) free((void *)dqr->var_name);
51  if (dqr->color) free((void *)dqr->color);
52  if (dqr->code_desc) free((void *)dqr->code_desc);
53  free(dqr);
54  }
55 }
56 
57 static DQR *_dqrdb_create_dqr(
58  DQRDB *dqrdb,
59  const char *id,
60  const char *desc,
61  const char *ds_name,
62  const char *var_name,
63  const char *code,
64  const char *color,
65  const char *code_desc,
66  const char *start,
67  const char *end)
68 {
69  DQR *dqr = (DQR *)calloc(1, sizeof(DQR));
70 
71  if (!dqr) {
72  return((DQR *)NULL);
73  }
74 
75  if (!id) id = "NULL";
76  if (!desc) desc = "NULL";
77  if (!ds_name) ds_name = "NULL";
78  if (!var_name) var_name = "NULL";
79  if (!color) color = "NULL";
80  if (!code_desc) code_desc = "NULL";
81 
82  if (!(dqr->id = strdup(id)) ||
83  !(dqr->desc = strdup(desc)) ||
84  !(dqr->ds_name = strdup(ds_name)) ||
85  !(dqr->var_name = strdup(var_name)) ||
86  !(dqr->color = strdup(color)) ||
87  !(dqr->code_desc = strdup(code_desc))) {
88 
89  _dqrdb_destroy_dqr(dqr);
90  return((DQR *)NULL);
91  }
92 
93  if (code) {
94  dqr->code = atoi(code);
95  }
96  else {
97  dqr->code = 0;
98  }
99 
100  if (start) {
101  dbconn_text_to_time(dqrdb->dbconn, start, &dqr->start);
102  }
103  else {
104  dqr->start = 0;
105  }
106 
107  if (end) {
108  dbconn_text_to_time(dqrdb->dbconn, end, &dqr->end);
109  }
110  else {
111  dqr->end = 0;
112  }
113 
114  return(dqr);
115 }
116 
117 /*******************************************************************************
118  * Public Functions
119  */
120 /** @publicsection */
121 
122 /**
123  * Create a new DQRDB connection.
124  *
125  * This function will first check the current working directory
126  * and then the users home directory for the .db_connect file.
127  *
128  * Error messages from this function are sent to the message
129  * handler (see msngr_init_log() and msngr_init_mail()).
130  *
131  * @param db_alias - database connection alias from the .db_connect file
132  * (default: dqrdb)
133  *
134  * @return
135  * - pointer to the database connection
136  * - NULL if an error occurred
137  */
138 DQRDB *dqrdb_create(const char *db_alias)
139 {
140  DQRDB *dqrdb;
141 
142  /* Allocate memory for the new DQRDB struct */
143 
144  dqrdb = (DQRDB *)calloc(1, sizeof(DQRDB));
145  if (!dqrdb) {
146 
147  ERROR( DSDB_LIB_NAME,
148  "Could not create DQRDB connection\n"
149  " -> memory allocation error\n");
150 
151  return((DQRDB *)NULL);
152  }
153 
154  /* Create the database connection */
155 
156  if (!db_alias) {
157  db_alias = "dqrdb";
158  }
159 
160  dqrdb->dbconn = dbconn_create(db_alias);
161 
162  if (!dqrdb->dbconn) {
163  free(dqrdb);
164  return((DQRDB *)NULL);
165  }
166 
167  /* Set default connection retry values */
168 
169  dqrdb->max_retries = 5;
170  dqrdb->retry_interval = 5;
171 
172  return(dqrdb);
173 }
174 
175 /**
176  * Destroy a DQRDB connection.
177  *
178  * This function will close the database connection and
179  * free all memory associated with the DQRDB structure.
180  *
181  * @param dqrdb - pointer to the database connection
182  */
183 void dqrdb_destroy(DQRDB *dqrdb)
184 {
185  if (dqrdb) {
186  if (dqrdb->dbconn) dbconn_destroy(dqrdb->dbconn);
187  free(dqrdb);
188  }
189 }
190 
191 /**
192  * Connect to the DQRDB.
193  *
194  * If the database connection has already been opened, this function
195  * will only increment the connection counter. This allows nested
196  * functions to repeatedly call this function without actually
197  * reconnecting to the database.
198  *
199  * To insure the database connection is not held open longer than
200  * necessary it is important that every call to dqrdb_connect() is
201  * followed by a call to dqrdb_disconnect().
202  *
203  * Error messages from this function are sent to the message
204  * handler (see msngr_init_log() and msngr_init_mail()).
205  *
206  * @param dqrdb - pointer to the database connection
207  *
208  * @return
209  * - the number of attempts it took to connect to the database
210  * - 0 if an error occurred
211  *
212  * @see dqrdb_disconnect()
213  */
214 int dqrdb_connect(DQRDB *dqrdb)
215 {
216  int attempts;
217 
218  /* Check if the database connection is already open */
219 
220  if (dqrdb->nreconnect) {
221 
222  if (dbconn_is_connected(dqrdb->dbconn)) {
223  dqrdb->nreconnect++;
224  return(1);
225  }
226  else if (dbconn_reset(dqrdb->dbconn) == DB_NO_ERROR) {
227  dqrdb->nreconnect++;
228  return(1);
229  }
230  }
231 
232  /* Connect to the database */
233 
234  for (attempts = 1;; attempts++) {
235 
236  if (dbconn_connect(dqrdb->dbconn) == DB_NO_ERROR) {
237  break;
238  }
239  else if (attempts > dqrdb->max_retries) {
240 
241  ERROR( DSDB_LIB_NAME,
242  "Could not connect to DQRDB\n"
243  " -> exceeded maximum number of retry attempts: %d\n",
244  dqrdb->max_retries);
245 
246  return(0);
247  }
248 
249  sleep(dqrdb->retry_interval);
250  }
251 
252  dqrdb->nreconnect++;
253 
254  return(attempts);
255 }
256 
257 /**
258  * Disconnect from the database.
259  *
260  * This function will only decrement the connection counter until it
261  * reaches 0. Once the connection counter reaches zero the database
262  * connection will be closed.
263  *
264  * To insure the database connection is not held open longer than
265  * necessary it is important that every call to dqrdb_connect() is
266  * followed by a call to dqrdb_disconnect().
267  *
268  * @param dqrdb - pointer to the database connection
269  *
270  * @see dqrdb_connect()
271  */
273 {
274  if (dqrdb->nreconnect > 0) {
275 
276  dqrdb->nreconnect--;
277 
278  if (dqrdb->nreconnect == 0) {
279  dbconn_disconnect(dqrdb->dbconn);
280  }
281  }
282 }
283 
284 /**
285  * Check the database connection.
286  *
287  * @param dqrdb - pointer to the database connection
288  *
289  * @return
290  * - 1 if connected
291  * - 0 if not connected
292  */
294 {
295  return(dbconn_is_connected(dqrdb->dbconn));
296 }
297 
298 /**
299  * Set the maximum number of times to retry a failed database connection.
300  *
301  * If this value is not set the default value of 5 will be used.
302  *
303  * @param dqrdb - pointer to the database connection
304  * @param max_retries - number of times to retry a database connection
305  */
306 void dqrdb_set_max_retries(DQRDB *dqrdb, int max_retries)
307 {
308  dqrdb->max_retries = max_retries;
309 }
310 
311 /**
312  * Set the retry interval for a failed database connection.
313  *
314  * If this value is not set the default value of 5 will be used.
315  *
316  * @param dqrdb - pointer to the database connection
317  * @param retry_interval - retry interval in seconds
318  */
319 void dqrdb_set_retry_interval(DQRDB *dqrdb, int retry_interval)
320 {
321  dqrdb->retry_interval = retry_interval;
322 }
323 
324 /**
325  * Free all memory used by an array of pointers to DQR structures.
326  *
327  * @param dqrs - pointer to the array of pointers to DQR structures
328  */
329 void dqrdb_free_dqrs(DQR **dqrs)
330 {
331  int i;
332 
333  if (dqrs) {
334  for (i = 0; dqrs[i]; i++) {
335  _dqrdb_destroy_dqr(dqrs[i]);
336  }
337  free(dqrs);
338  }
339 }
340 
341 /**
342  * Get the DQRs for a datastream.
343  *
344  * The memory used by the output array is dynamically allocated.
345  * It is the responsibility of the calling process to free this memory
346  * when it is no longer needed (see dqrdb_free_dqrs()).
347  *
348  * Error messages from this function are sent to the message
349  * handler (see msngr_init_log() and msngr_init_mail()).
350  *
351  * Null results from the database are not reported as errors.
352  * It is the responsibility of the calling process to report
353  * these as errors if necessary.
354  *
355  * @param dqrdb - pointer to the database connection
356  * @param site - site name
357  * @param facility - facility name
358  * @param dsc_name - datastream class name
359  * @param dsc_level - datastream class level
360  * @param var_name - variable name or NULL for all variables
361  * @param start_time - start time in seconds since 1970
362  * @param end_time - end time in seconds since 1970
363  * @param dqrs - output: pointer to the array of pointers
364  * to the DQR structures.
365  *
366  * @result
367  * - number of DQRs returned
368  * - 0 if no DQRs were found
369  * - -1 if an error occurred
370  */
372  DQRDB *dqrdb,
373  const char *site,
374  const char *facility,
375  const char *dsc_name,
376  const char *dsc_level,
377  const char *var_name,
378  time_t start_time,
379  time_t end_time,
380  DQR ***dqrs)
381 {
382  const char *command = "SELECT * FROM get_dqrs($1,$2,$3,$4,$5,$6,$7)";
383  const char *params[7];
384  char start[32];
385  char end[32];
386  DBStatus status;
387  DBResult *dbres;
388  int ndqrs;
389  int row;
390 
391  ndqrs = 0;
392  *dqrs = (DQR **)NULL;
393 
394  params[0] = dsc_name;
395  params[1] = dsc_level;
396  params[2] = site;
397  params[3] = facility;
398  params[4] = var_name;
399 
400  if (start_time) {
401  params[5] = dbconn_time_to_text(dqrdb->dbconn, start_time, start);
402  if (!params[5]) {
403  return(-1);
404  }
405  }
406  else {
407  params[5] = (const char *)NULL;
408  }
409 
410  if (end_time) {
411  params[6] = dbconn_time_to_text(dqrdb->dbconn, end_time, end);
412  if (!params[6]) {
413  return(-1);
414  }
415  }
416  else {
417  params[6] = (const char *)NULL;
418  }
419 
420  status = dbconn_query(dqrdb->dbconn, command, 7, params, &dbres);
421 
422  if (status == DB_NO_ERROR) {
423 
424  *dqrs = (DQR **)calloc(dbres->nrows + 1, sizeof(DQR *));
425  if (!*dqrs) {
426 
427  ERROR( DSDB_LIB_NAME,
428  "Could not get list of DQRs for: %s%s%s.%s:%s\n"
429  " -> memory allocation error\n",
430  site, dsc_name, facility, dsc_level, var_name);
431 
432  dbres->free(dbres);
433  return(-1);
434  }
435 
436  for (row = 0; row < dbres->nrows; row++) {
437 
438  (*dqrs)[row] = _dqrdb_create_dqr(
439  dqrdb,
440  DB_RESULT(dbres,row,0),
441  DB_RESULT(dbres,row,1),
442  DB_RESULT(dbres,row,2),
443  DB_RESULT(dbres,row,3),
444  DB_RESULT(dbres,row,4),
445  DB_RESULT(dbres,row,5),
446  DB_RESULT(dbres,row,6),
447  DB_RESULT(dbres,row,7),
448  DB_RESULT(dbres,row,8));
449 
450  if (!(*dqrs)[row]) {
451 
452  ERROR( DSDB_LIB_NAME,
453  "Could not get list of DQRs for: %s%s%s.%s:%s\n"
454  " -> memory allocation error\n",
455  site, dsc_name, facility, dsc_level, var_name);
456 
457  dqrdb_free_dqrs(*dqrs);
458  *dqrs = (DQR **)NULL;
459 
460  dbres->free(dbres);
461  return(-1);
462  }
463 
464  ndqrs++;
465  }
466 
467  (*dqrs)[dbres->nrows] = (DQR *)NULL;
468 
469  dbres->free(dbres);
470  return(ndqrs);
471  }
472  else if (status == DB_NULL_RESULT) {
473  return(0);
474  }
475 
476  return(-1);
477 }
478 
479 /**
480  * Print an arrary of DQRs.
481  *
482  * @param fp - pointer to the output stream
483  * @param dqrdb - pointer to the database connection
484  * @param dqrs - pointer to the array of DQR structures
485  */
486 void dqrdb_print_dqrs(FILE *fp, DQRDB *dqrdb, DQR **dqrs)
487 {
488  int id_width = 0;
489  int ds_width = 0;
490  int var_width = 0;
491  int color_width = 0;
492  int desc_width = 0;
493  int length;
494  char format1[64];
495  char format2[64];
496  char ts1[64];
497  char ts2[64];
498  int i;
499 
500  for (i = 0; dqrs[i]; i++) {
501 
502  if (dqrs[i]->id) {
503  length = strlen(dqrs[i]->id);
504  if (id_width < length) { id_width = length; }
505  }
506 
507  if (dqrs[i]->ds_name) {
508  length = strlen(dqrs[i]->ds_name);
509  if (ds_width < length) { ds_width = length; }
510  }
511 
512  if (dqrs[i]->var_name) {
513  length = strlen(dqrs[i]->var_name);
514  if (var_width < length) { var_width = length; }
515  }
516 
517  if (dqrs[i]->color) {
518  length = strlen(dqrs[i]->color);
519  if (color_width < length) { color_width = length; }
520  }
521 
522  if (dqrs[i]->code_desc) {
523  length = strlen(dqrs[i]->code_desc);
524  if (desc_width < length) { desc_width = length; }
525  }
526  }
527 
528  sprintf(format1, "%%-%ds | %%-%ds | %%-%ds ", id_width, ds_width, var_width);
529  sprintf(format2, "| %%-%ds | %%-%ds ", color_width, desc_width);
530 
531  fprintf(fp, format1, "id", "datastream", "variable");
532  fprintf(fp, "| code ");
533  fprintf(fp, format2, "color", "code_desc");
534  fprintf(fp, "| start time | end time\n");
535 
536  for (i = 0; dqrs[i]; i++) {
537  fprintf(fp, format1, dqrs[i]->id, dqrs[i]->ds_name, dqrs[i]->var_name);
538 
539  if (dqrs[i]->code < 0) {
540  fprintf(fp, "| %d ", dqrs[i]->code);
541  }
542  else {
543  fprintf(fp, "| %d ", dqrs[i]->code);
544  }
545 
546  fprintf(fp, format2, dqrs[i]->color, dqrs[i]->code_desc);
547 
548  fprintf(fp, "| %s | %s\n",
549  dbconn_time_to_text(dqrdb->dbconn, dqrs[i]->start, ts1),
550  dbconn_time_to_text(dqrdb->dbconn, dqrs[i]->end, ts2));
551  }
552 }
553 
554 /*@}*/