1 /**
2 * collectd - src/oracle.c
3 * Copyright (C) 2008 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Linking src/oracle.c ("the oracle plugin") statically or dynamically with
19 * other modules is making a combined work based on the oracle plugin. Thus,
20 * the terms and conditions of the GNU General Public License cover the whole
21 * combination.
22 *
23 * In addition, as a special exception, the copyright holders of the oracle
24 * plugin give you permission to combine the oracle plugin with free software
25 * programs or libraries that are released under the GNU LGPL and with code
26 * included in the standard release of the Oracle® Call Interface (OCI) under
27 * the Oracle® Technology Network (OTN) License (or modified versions of such
28 * code, with unchanged license). You may copy and distribute such a system
29 * following the terms of the GNU GPL for the oracle plugin and the licenses of
30 * the other code concerned.
31 *
32 * Note that people who make modified versions of the oracle plugin are not
33 * obligated to grant this special exception for their modified versions; it is
34 * their choice whether to do so. The GNU General Public License gives
35 * permission to release a modified version without this exception; this
36 * exception also makes it possible to release a modified version which carries
37 * forward this exception. However, without this exception the OTN License does
38 * not allow linking with code licensed under the GNU General Public License.
39 *
40 * Oracle® is a registered trademark of Oracle Corporation and/or its
41 * affiliates. Other names may be trademarks of their respective owners.
42 *
43 * Authors:
44 * Florian octo Forster <octo at noris.net>
45 **/
47 #include "collectd.h"
48 #include "common.h"
49 #include "plugin.h"
51 #include <oci.h>
53 /*
54 * Data types
55 */
56 struct o_query_s
57 {
58 char *name;
59 char *statement;
60 char *type;
61 char **instances;
62 size_t instances_num;
63 char **values;
64 size_t values_num;
66 OCIStmt *oci_statement;
67 };
68 typedef struct o_query_s o_query_t;
70 struct o_database_s
71 {
72 char *name;
73 char *connect_id;
74 char *username;
75 char *password;
77 o_query_t **queries;
78 size_t queries_num;
80 OCISvcCtx *oci_service_context;
81 };
82 typedef struct o_database_s o_database_t;
84 /*
85 * Global variables
86 */
87 static o_query_t **queries = NULL;
88 static size_t queries_num = 0;
89 static o_database_t **databases = NULL;
90 static size_t databases_num = 0;
92 OCIEnv *oci_env = NULL;
93 OCIError *oci_error = NULL;
95 /*
96 * Functions
97 */
98 static void o_report_error (const char *where, /* {{{ */
99 const char *what, OCIError *eh)
100 {
101 char buffer[2048];
102 sb4 error_code;
103 int status;
105 status = OCIErrorGet (eh, /* record number = */ 1,
106 /* sqlstate = */ NULL,
107 &error_code,
108 (text *) &buffer[0],
109 (ub4) sizeof (buffer),
110 OCI_HTYPE_ERROR);
111 buffer[sizeof (buffer) - 1] = 0;
113 if (status == OCI_SUCCESS)
114 {
115 size_t buffer_length;
117 buffer_length = strlen (buffer);
118 while ((buffer_length > 0) && (buffer[buffer_length - 1] < 32))
119 {
120 buffer_length--;
121 buffer[buffer_length] = 0;
122 }
124 ERROR ("oracle plugin: %s: %s failed: %s",
125 where, what, buffer);
126 }
127 else
128 {
129 ERROR ("oracle plugin: %s: %s failed. Additionally, OCIErrorGet failed with status %i.",
130 where, what, status);
131 }
132 } /* }}} void o_report_error */
134 static void o_query_free (o_query_t *q) /* {{{ */
135 {
136 size_t i;
138 if (q == NULL)
139 return;
141 sfree (q->name);
142 sfree (q->statement);
143 sfree (q->type);
145 for (i = 0; i < q->instances_num; i++)
146 sfree (q->instances[i]);
147 sfree (q->instances);
149 for (i = 0; i < q->values_num; i++)
150 sfree (q->values[i]);
151 sfree (q->values);
153 sfree (q);
154 } /* }}} void o_query_free */
156 static void o_database_free (o_database_t *db) /* {{{ */
157 {
158 if (db == NULL)
159 return;
161 sfree (db->name);
162 sfree (db->connect_id);
163 sfree (db->username);
164 sfree (db->password);
165 sfree (db->queries);
167 sfree (db);
168 } /* }}} void o_database_free */
170 /* Configuration handling functions {{{
171 *
172 * <Plugin oracle>
173 * <Query "plugin_instance0">
174 * Statement "SELECT name, value FROM table"
175 * Type "gauge"
176 * InstancesFrom "name"
177 * ValuesFrom "value"
178 * </Query>
179 *
180 * <Database "plugin_instance1">
181 * Hostname "localhost"
182 * Username "oracle"
183 * Password "secret"
184 * Query "plugin_instance0"
185 * </Database>
186 * </Plugin>
187 */
189 static int o_config_set_string (char **ret_string, /* {{{ */
190 oconfig_item_t *ci)
191 {
192 char *string;
194 if ((ci->values_num != 1)
195 || (ci->values[0].type != OCONFIG_TYPE_STRING))
196 {
197 WARNING ("oracle plugin: The `%s' config option "
198 "needs exactly one string argument.", ci->key);
199 return (-1);
200 }
202 string = strdup (ci->values[0].value.string);
203 if (string == NULL)
204 {
205 ERROR ("oracle plugin: strdup failed.");
206 return (-1);
207 }
209 if (*ret_string != NULL)
210 free (*ret_string);
211 *ret_string = string;
213 return (0);
214 } /* }}} int o_config_set_string */
216 static int o_config_add_string (char ***ret_array, /* {{{ */
217 size_t *ret_array_len, oconfig_item_t *ci)
218 {
219 char **array;
220 size_t array_len;
221 int i;
223 if (ci->values_num < 1)
224 {
225 WARNING ("oracle plugin: The `%s' config option "
226 "needs at least one argument.", ci->key);
227 return (-1);
228 }
230 for (i = 0; i < ci->values_num; i++)
231 {
232 if (ci->values[i].type != OCONFIG_TYPE_STRING)
233 {
234 WARNING ("oracle plugin: Argument %i to the `%s' option "
235 "is not a string.", i + 1, ci->key);
236 return (-1);
237 }
238 }
240 array_len = *ret_array_len;
241 array = (char **) realloc (*ret_array,
242 sizeof (char *) * (array_len + ci->values_num));
243 if (array == NULL)
244 {
245 ERROR ("oracle plugin: realloc failed.");
246 return (-1);
247 }
248 *ret_array = array;
250 for (i = 0; i < ci->values_num; i++)
251 {
252 array[array_len] = strdup (ci->values[i].value.string);
253 if (array[array_len] == NULL)
254 {
255 ERROR ("oracle plugin: strdup failed.");
256 *ret_array_len = array_len;
257 return (-1);
258 }
259 array_len++;
260 }
262 *ret_array_len = array_len;
263 return (0);
264 } /* }}} int o_config_add_string */
266 static int o_config_add_query (oconfig_item_t *ci) /* {{{ */
267 {
268 o_query_t *q;
269 int status;
270 int i;
272 if ((ci->values_num != 1)
273 || (ci->values[0].type != OCONFIG_TYPE_STRING))
274 {
275 WARNING ("oracle plugin: The `Query' block "
276 "needs exactly one string argument.");
277 return (-1);
278 }
280 q = (o_query_t *) malloc (sizeof (*q));
281 if (q == NULL)
282 {
283 ERROR ("oracle plugin: malloc failed.");
284 return (-1);
285 }
286 memset (q, 0, sizeof (*q));
288 status = o_config_set_string (&q->name, ci);
289 if (status != 0)
290 {
291 sfree (q);
292 return (status);
293 }
295 /* Fill the `o_query_t' structure.. */
296 for (i = 0; i < ci->children_num; i++)
297 {
298 oconfig_item_t *child = ci->children + i;
300 if (strcasecmp ("Statement", child->key) == 0)
301 status = o_config_set_string (&q->statement, child);
302 else if (strcasecmp ("Type", child->key) == 0)
303 status = o_config_set_string (&q->type, child);
304 else if (strcasecmp ("InstancesFrom", child->key) == 0)
305 status = o_config_add_string (&q->instances, &q->instances_num, child);
306 else if (strcasecmp ("ValuesFrom", child->key) == 0)
307 status = o_config_add_string (&q->values, &q->values_num, child);
308 else
309 {
310 WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
311 status = -1;
312 }
314 if (status != 0)
315 break;
316 }
318 /* Check that all necessary options have been given. */
319 while (status == 0)
320 {
321 if (q->statement == NULL)
322 {
323 WARNING ("oracle plugin: `Statement' not given for query `%s'", q->name);
324 status = -1;
325 }
326 if (q->type == NULL)
327 {
328 WARNING ("oracle plugin: `Type' not given for query `%s'", q->name);
329 status = -1;
330 }
331 if (q->instances == NULL)
332 {
333 WARNING ("oracle plugin: `InstancesFrom' not given for query `%s'", q->name);
334 status = -1;
335 }
336 if (q->values == NULL)
337 {
338 WARNING ("oracle plugin: `ValuesFrom' not given for query `%s'", q->name);
339 status = -1;
340 }
342 break;
343 } /* while (status == 0) */
345 /* If all went well, add this query to the list of queries within the
346 * database structure. */
347 if (status == 0)
348 {
349 o_query_t **temp;
351 temp = (o_query_t **) realloc (queries,
352 sizeof (*queries) * (queries_num + 1));
353 if (temp == NULL)
354 {
355 ERROR ("oracle plugin: realloc failed");
356 status = -1;
357 }
358 else
359 {
360 queries = temp;
361 queries[queries_num] = q;
362 queries_num++;
363 }
364 }
366 if (status != 0)
367 {
368 o_query_free (q);
369 return (-1);
370 }
372 return (0);
373 } /* }}} int o_config_add_query */
375 static int o_config_add_database_query (o_database_t *db, /* {{{ */
376 oconfig_item_t *ci)
377 {
378 o_query_t *q;
379 o_query_t **temp;
380 size_t i;
382 if ((ci->values_num != 1)
383 || (ci->values[0].type != OCONFIG_TYPE_STRING))
384 {
385 WARNING ("oracle plugin: The `Query' config option "
386 "needs exactly one string argument.");
387 return (-1);
388 }
390 q = NULL;
391 for (i = 0; i < queries_num; i++)
392 {
393 if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0)
394 {
395 q = queries[i];
396 break;
397 }
398 }
400 if (q == NULL)
401 {
402 WARNING ("oracle plugin: Database `%s': Unknown query `%s'. "
403 "Please make sure that the <Query \"%s\"> block comes before "
404 "the <Database \"%s\"> block.",
405 db->name, ci->values[0].value.string,
406 ci->values[0].value.string, db->name);
407 return (-1);
408 }
410 temp = (o_query_t **) realloc (db->queries,
411 sizeof (*db->queries) * (db->queries_num + 1));
412 if (temp == NULL)
413 {
414 ERROR ("oracle plugin: realloc failed");
415 return (-1);
416 }
417 else
418 {
419 db->queries = temp;
420 db->queries[db->queries_num] = q;
421 db->queries_num++;
422 }
424 return (0);
425 } /* }}} int o_config_add_database_query */
427 static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
428 {
429 o_database_t *db;
430 int status;
431 int i;
433 if ((ci->values_num != 1)
434 || (ci->values[0].type != OCONFIG_TYPE_STRING))
435 {
436 WARNING ("oracle plugin: The `Database' block "
437 "needs exactly one string argument.");
438 return (-1);
439 }
441 db = (o_database_t *) malloc (sizeof (*db));
442 if (db == NULL)
443 {
444 ERROR ("oracle plugin: malloc failed.");
445 return (-1);
446 }
447 memset (db, 0, sizeof (*db));
449 status = o_config_set_string (&db->name, ci);
450 if (status != 0)
451 {
452 sfree (db);
453 return (status);
454 }
456 /* Fill the `o_database_t' structure.. */
457 for (i = 0; i < ci->children_num; i++)
458 {
459 oconfig_item_t *child = ci->children + i;
461 if (strcasecmp ("ConnectID", child->key) == 0)
462 status = o_config_set_string (&db->connect_id, child);
463 else if (strcasecmp ("Username", child->key) == 0)
464 status = o_config_set_string (&db->username, child);
465 else if (strcasecmp ("Password", child->key) == 0)
466 status = o_config_set_string (&db->password, child);
467 else if (strcasecmp ("Query", child->key) == 0)
468 status = o_config_add_database_query (db, child);
469 else
470 {
471 WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
472 status = -1;
473 }
475 if (status != 0)
476 break;
477 }
479 /* Check that all necessary options have been given. */
480 while (status == 0)
481 {
482 if (db->connect_id == NULL)
483 {
484 WARNING ("oracle plugin: `ConnectID' not given for query `%s'", db->name);
485 status = -1;
486 }
487 if (db->username == NULL)
488 {
489 WARNING ("oracle plugin: `Username' not given for query `%s'", db->name);
490 status = -1;
491 }
492 if (db->password == NULL)
493 {
494 WARNING ("oracle plugin: `Password' not given for query `%s'", db->name);
495 status = -1;
496 }
498 break;
499 } /* while (status == 0) */
501 /* If all went well, add this query to the list of queries within the
502 * database structure. */
503 if (status == 0)
504 {
505 o_database_t **temp;
507 temp = (o_database_t **) realloc (databases,
508 sizeof (*databases) * (databases_num + 1));
509 if (temp == NULL)
510 {
511 ERROR ("oracle plugin: realloc failed");
512 status = -1;
513 }
514 else
515 {
516 databases = temp;
517 databases[databases_num] = db;
518 databases_num++;
519 }
520 }
522 if (status != 0)
523 {
524 o_database_free (db);
525 return (-1);
526 }
528 return (0);
529 } /* }}} int o_config_add_database */
531 static int o_config (oconfig_item_t *ci) /* {{{ */
532 {
533 int i;
535 for (i = 0; i < ci->children_num; i++)
536 {
537 oconfig_item_t *child = ci->children + i;
538 if (strcasecmp ("Query", child->key) == 0)
539 o_config_add_query (child);
540 else if (strcasecmp ("Database", child->key) == 0)
541 o_config_add_database (child);
542 else
543 {
544 WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
545 }
546 } /* for (ci->children) */
548 return (0);
549 } /* }}} int o_config */
551 /* }}} End of configuration handling functions */
553 static int o_init (void) /* {{{ */
554 {
555 int status;
557 if (oci_env != NULL)
558 return (0);
560 status = OCIEnvCreate (&oci_env,
561 /* mode = */ OCI_THREADED,
562 /* context = */ NULL,
563 /* malloc = */ NULL,
564 /* realloc = */ NULL,
565 /* free = */ NULL,
566 /* user_data_size = */ 0,
567 /* user_data_ptr = */ NULL);
568 if (status != 0)
569 {
570 ERROR ("oracle plugin: OCIEnvCreate failed with status %i.", status);
571 return (-1);
572 }
574 status = OCIHandleAlloc (oci_env, (void *) &oci_error, OCI_HTYPE_ERROR,
575 /* user_data_size = */ 0, /* user_data = */ NULL);
576 if (status != OCI_SUCCESS)
577 {
578 ERROR ("oracle plugin: OCIHandleAlloc (OCI_HTYPE_ERROR) failed "
579 "with status %i.", status);
580 return (-1);
581 }
583 return (0);
584 } /* }}} int o_init */
586 static void o_submit (o_database_t *db, o_query_t *q, /* {{{ */
587 const data_set_t *ds, char **buffer_instances, char **buffer_values)
588 {
589 value_list_t vl = VALUE_LIST_INIT;
590 size_t i;
592 assert (((size_t) ds->ds_num) == q->values_num);
594 vl.values = (value_t *) malloc (sizeof (value_t) * q->values_num);
595 if (vl.values == NULL)
596 {
597 ERROR ("oracle plugin: malloc failed.");
598 return;
599 }
600 vl.values_len = ds->ds_num;
602 for (i = 0; i < q->values_num; i++)
603 {
604 char *endptr;
606 endptr = NULL;
607 errno = 0;
608 if (ds->ds[i].type == DS_TYPE_COUNTER)
609 vl.values[i].counter = (counter_t) strtoll (buffer_values[i],
610 &endptr, /* base = */ 0);
611 else if (ds->ds[i].type == DS_TYPE_GAUGE)
612 vl.values[i].gauge = (gauge_t) strtod (buffer_values[i], &endptr);
613 else
614 errno = EINVAL;
616 if ((endptr == buffer_values[i]) || (errno != 0))
617 {
618 WARNING ("oracle plugin: o_submit: Parsing `%s' as %s failed.",
619 buffer_values[i],
620 (ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
621 vl.values[i].gauge = NAN;
622 }
623 }
625 vl.time = time (NULL);
626 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
627 sstrncpy (vl.plugin, "oracle", sizeof (vl.plugin));
628 sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance));
629 sstrncpy (vl.type, q->type, sizeof (vl.type));
630 strjoin (vl.type_instance, sizeof (vl.type_instance),
631 buffer_instances, q->instances_num, "-");
632 vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
634 plugin_dispatch_values (&vl);
635 } /* }}} void o_submit */
637 static int o_read_database_query (o_database_t *db, /* {{{ */
638 o_query_t *q)
639 {
640 const data_set_t *ds;
641 ub4 param_counter; /* == number of columns */
642 int status;
643 size_t i;
644 ub4 j;
646 /* Scratch area for OCI to write values to */
647 char **buffer_instances;
648 char **buffer_values;
650 /* List of indizes of the instance and value columns. Only used for error
651 * checking. */
652 size_t *index_instances;
653 size_t *index_values;
655 /* List of `OCIDefine' pointers. These defines map columns to the buffer
656 * space declared above. */
657 OCIDefine **oci_defines;
659 ds = plugin_get_ds (q->type); /* {{{ */
660 if (ds == NULL)
661 {
662 WARNING ("oracle plugin: o_read_database_query (%s, %s): "
663 "plugin_get_ds (%s) failed. Please check if the type exists, "
664 "see types.db(5).",
665 db->name, q->name, q->type);
666 return (-1);
667 } /* }}} */
669 if (((size_t) ds->ds_num) != q->values_num)
670 {
671 ERROR ("oracle plugin: o_read_database_query (%s, %s): "
672 "The query `%s' uses the type `%s' which requires exactly "
673 "%i value%s, but you specified %zu value-column%s. "
674 "See types.db(5) for details.",
675 db->name, q->name,
676 q->name, q->type,
677 ds->ds_num, (ds->ds_num == 1) ? "" : "s",
678 q->values_num, (q->values_num == 1) ? "" : "s");
679 return (-1);
680 }
682 /* Prepare the statement */
683 if (q->oci_statement == NULL) /* {{{ */
684 {
685 status = OCIHandleAlloc (oci_env, (void *) &q->oci_statement,
686 OCI_HTYPE_STMT, /* user_data_size = */ 0, /* user_data = */ NULL);
687 if (status != OCI_SUCCESS)
688 {
689 o_report_error ("o_read_database_query", "OCIHandleAlloc", oci_error);
690 q->oci_statement = NULL;
691 return (-1);
692 }
694 status = OCIStmtPrepare (q->oci_statement, oci_error,
695 (text *) q->statement, (ub4) strlen (q->statement),
696 /* language = */ OCI_NTV_SYNTAX,
697 /* mode = */ OCI_DEFAULT);
698 if (status != OCI_SUCCESS)
699 {
700 o_report_error ("o_read_database_query", "OCIStmtPrepare", oci_error);
701 OCIHandleFree (q->oci_statement, OCI_HTYPE_STMT);
702 q->oci_statement = NULL;
703 return (-1);
704 }
705 assert (q->oci_statement != NULL);
706 } /* }}} */
708 /* Execute the statement */
709 status = OCIStmtExecute (db->oci_service_context, /* {{{ */
710 q->oci_statement,
711 oci_error,
712 /* iters = */ 0,
713 /* rowoff = */ 0,
714 /* snap_in = */ NULL, /* snap_out = */ NULL,
715 /* mode = */ OCI_DEFAULT);
716 if (status != OCI_SUCCESS)
717 {
718 o_report_error ("o_read_database_query", "OCIStmtExecute", oci_error);
719 ERROR ("oracle plugin: o_read_database_query: "
720 "Failing statement was: %s", q->statement);
721 return (-1);
722 } /* }}} */
724 /* Acquire the number of columns returned. */
725 param_counter = 0;
726 status = OCIAttrGet (q->oci_statement, OCI_HTYPE_STMT, /* {{{ */
727 ¶m_counter, /* size pointer = */ NULL,
728 OCI_ATTR_PARAM_COUNT, oci_error);
729 if (status != OCI_SUCCESS)
730 {
731 o_report_error ("o_read_database_query", "OCIAttrGet", oci_error);
732 return (-1);
733 } /* }}} */
735 /* Allocate the following buffers:
736 *
737 * - buffer_instances q->instances_num x DATA_MAX_NAME_LEN
738 * - buffer_values q->values_num x NUMBER_BUFFER_SIZE
739 * - index_instances q->instances_num
740 * - index_values q->values_num
741 * - oci_defines q->instances_num+q->values_num
742 * {{{ */
743 #define NUMBER_BUFFER_SIZE 64
745 #define FREE_ALL \
746 if (buffer_instances != NULL) { \
747 sfree (buffer_instances[0]); \
748 sfree (buffer_instances); \
749 } \
750 if (buffer_values != NULL) { \
751 sfree (buffer_values[0]); \
752 sfree (buffer_values); \
753 } \
754 sfree (index_instances); \
755 sfree (index_values); \
756 sfree (oci_defines)
758 #define ALLOC_OR_FAIL(ptr, ptr_size) \
759 do { \
760 size_t alloc_size = (size_t) ((ptr_size)); \
761 (ptr) = malloc (alloc_size); \
762 if ((ptr) == NULL) { \
763 FREE_ALL; \
764 ERROR ("oracle plugin: o_read_database_query: malloc failed."); \
765 return (-1); \
766 } \
767 memset ((ptr), 0, alloc_size); \
768 } while (0)
770 /* Initialize everything to NULL so the above works. */
771 buffer_instances = NULL;
772 buffer_values = NULL;
773 index_instances = NULL;
774 index_values = NULL;
775 oci_defines = NULL;
777 ALLOC_OR_FAIL (buffer_instances, q->instances_num * sizeof (char *));
778 ALLOC_OR_FAIL (buffer_instances[0], q->instances_num * DATA_MAX_NAME_LEN
779 * sizeof (char));
780 for (i = 1; i < q->instances_num; i++)
781 buffer_instances[i] = buffer_instances[i - 1] + DATA_MAX_NAME_LEN;
783 ALLOC_OR_FAIL (buffer_values, q->values_num * sizeof (char *));
784 ALLOC_OR_FAIL (buffer_values[0], q->values_num * NUMBER_BUFFER_SIZE
785 * sizeof (char));
786 for (i = 1; i < q->values_num; i++)
787 buffer_values[i] = buffer_values[i - 1] + NUMBER_BUFFER_SIZE;
789 ALLOC_OR_FAIL (index_instances, q->instances_num * sizeof (size_t));
790 ALLOC_OR_FAIL (index_values, q->values_num * sizeof (size_t));
792 ALLOC_OR_FAIL (oci_defines, (q->instances_num + q->values_num)
793 * sizeof (OCIDefine *));
794 /* }}} End of buffer allocations. */
796 /* ``Define'' the returned data, i. e. bind the columns to the buffers
797 * returned above. */
798 for (j = 1; j <= param_counter; j++) /* {{{ */
799 {
800 char *column_name;
801 size_t column_name_length;
802 char column_name_copy[DATA_MAX_NAME_LEN];
803 size_t i;
804 OCIParam *oci_param;
806 oci_param = NULL;
808 status = OCIParamGet (q->oci_statement, OCI_HTYPE_STMT, oci_error,
809 (void *) &oci_param, j);
810 if (status != OCI_SUCCESS)
811 {
812 /* This is probably alright */
813 DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);", status, status);
814 o_report_error ("o_read_database_query", "OCIParamGet", oci_error);
815 status = OCI_SUCCESS;
816 break;
817 }
819 column_name = NULL;
820 column_name_length = 0;
821 status = OCIAttrGet (oci_param, OCI_DTYPE_PARAM,
822 &column_name, &column_name_length, OCI_ATTR_NAME, oci_error);
823 if (status != OCI_SUCCESS)
824 {
825 o_report_error ("o_read_database_query", "OCIAttrGet (OCI_ATTR_NAME)",
826 oci_error);
827 continue;
828 }
830 /* Ensure null-termination. */
831 memset (column_name_copy, 0, sizeof (column_name_copy));
832 if (column_name_length >= sizeof (column_name_copy))
833 column_name_length = sizeof (column_name_copy) - 1;
834 memcpy (column_name_copy, column_name, column_name_length);
835 column_name_copy[column_name_length] = 0;
837 DEBUG ("oracle plugin: o_read_database_query: column_name[%u] = %s; column_name_length = %zu;",
838 (unsigned int) j, column_name_copy, column_name_length);
840 for (i = 0; i < q->instances_num; i++)
841 {
842 if (strcasecmp (q->instances[i], column_name_copy) != 0)
843 continue;
845 status = OCIDefineByPos (q->oci_statement,
846 &oci_defines[i], oci_error, j,
847 buffer_instances[i], DATA_MAX_NAME_LEN, SQLT_STR,
848 NULL, NULL, NULL, OCI_DEFAULT);
849 index_instances[i] = j;
851 DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> instances[%zu]",
852 (unsigned int) j, column_name_copy, i);
853 break;
854 }
856 for (i = 0; i < q->values_num; i++)
857 {
858 if (strcasecmp (q->values[i], column_name_copy) != 0)
859 continue;
861 status = OCIDefineByPos (q->oci_statement,
862 &oci_defines[q->instances_num + i], oci_error, j,
863 buffer_values[i], NUMBER_BUFFER_SIZE, SQLT_STR,
864 NULL, NULL, NULL, OCI_DEFAULT);
865 index_values[i] = j;
867 DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> values[%zu]",
868 (unsigned int) j, column_name_copy, i);
869 break;
870 }
871 } /* for (j = 1; j <= param_counter; j++) */
872 /* }}} End of the ``define'' stuff. */
874 /* Iterate over all indizes and check that all columns from which we're
875 * supposed to read instances or values have been found. */
876 /* {{{ */
877 status = 0;
878 for (i = 0; i < q->instances_num; i++)
879 {
880 if (index_instances[i] > 0)
881 continue;
883 ERROR ("oracle plugin: o_read_database_query (%s, %s): "
884 "Instance %zu of the `%s' query should be read from the column `%s', "
885 "but that column wasn't returned by the SQL statement. Please check "
886 "your configuration.",
887 db->name, q->name, (i + 1), q->name, q->instances[i]);
888 status++;
889 }
891 for (i = 0; i < q->values_num; i++)
892 {
893 if (index_values[i] > 0)
894 continue;
896 ERROR ("oracle plugin: o_read_database_query (%s, %s): "
897 "Value %zu of the `%s' query should be read from the column `%s', "
898 "but that column wasn't returned by the SQL statement. Please check "
899 "your configuration.",
900 db->name, q->name, (i + 1), q->name, q->values[i]);
901 status++;
902 }
904 if (status != 0)
905 {
906 FREE_ALL;
907 return (-1);
908 }
909 /* }}} */
911 /* Fetch and handle all the rows that matched the query. */
912 while (42) /* {{{ */
913 {
914 status = OCIStmtFetch2 (q->oci_statement, oci_error,
915 /* nrows = */ 1, /* orientation = */ OCI_FETCH_NEXT,
916 /* fetch offset = */ 0, /* mode = */ OCI_DEFAULT);
917 if (status == OCI_NO_DATA)
918 {
919 status = OCI_SUCCESS;
920 break;
921 }
922 else if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
923 {
924 o_report_error ("o_read_database_query", "OCIStmtFetch2", oci_error);
925 break;
926 }
928 for (i = 0; i < q->instances_num; i++)
929 {
930 DEBUG ("oracle plugin: o_read_database_query: "
931 "buffer_instances[%zu] = %s;",
932 i, buffer_instances[i]);
933 }
935 for (i = 0; i < q->values_num; i++)
936 {
937 DEBUG ("oracle plugin: o_read_database_query: "
938 "buffer_values[%zu] = %s;",
939 i, buffer_values[i]);
940 }
942 o_submit (db, q, ds, buffer_instances, buffer_values);
943 } /* }}} while (42) */
945 /* DEBUG ("oracle plugin: o_read_database_query: This statement succeeded: %s", q->statement); */
946 FREE_ALL;
948 return (0);
949 #undef FREE_ALL
950 #undef ALLOC_OR_FAIL
951 } /* }}} int o_read_database_query */
953 static int o_read_database (o_database_t *db) /* {{{ */
954 {
955 size_t i;
956 int status;
958 if (db->oci_service_context == NULL)
959 {
960 status = OCILogon (oci_env, oci_error,
961 &db->oci_service_context,
962 (OraText *) db->username, (ub4) strlen (db->username),
963 (OraText *) db->password, (ub4) strlen (db->password),
964 (OraText *) db->connect_id, (ub4) strlen (db->connect_id));
965 if (status != OCI_SUCCESS)
966 {
967 o_report_error ("o_read_database", "OCILogon", oci_error);
968 DEBUG ("oracle plugin: OCILogon (%s): db->oci_service_context = %p;",
969 db->connect_id, db->oci_service_context);
970 db->oci_service_context = NULL;
971 return (-1);
972 }
973 assert (db->oci_service_context != NULL);
974 }
976 DEBUG ("oracle plugin: o_read_database: db->connect_id = %s; db->oci_service_context = %p;",
977 db->connect_id, db->oci_service_context);
979 for (i = 0; i < db->queries_num; i++)
980 o_read_database_query (db, db->queries[i]);
982 return (0);
983 } /* }}} int o_read_database */
985 static int o_read (void) /* {{{ */
986 {
987 size_t i;
989 for (i = 0; i < databases_num; i++)
990 o_read_database (databases[i]);
992 return (0);
993 } /* }}} int o_read */
995 static int o_shutdown (void) /* {{{ */
996 {
997 size_t i;
999 for (i = 0; i < databases_num; i++)
1000 if (databases[i]->oci_service_context != NULL)
1001 {
1002 OCIHandleFree (databases[i]->oci_service_context, OCI_HTYPE_SVCCTX);
1003 databases[i]->oci_service_context = NULL;
1004 }
1006 for (i = 0; i < queries_num; i++)
1007 if (queries[i]->oci_statement != NULL)
1008 {
1009 OCIHandleFree (queries[i]->oci_statement, OCI_HTYPE_STMT);
1010 queries[i]->oci_statement = NULL;
1011 }
1013 OCIHandleFree (oci_env, OCI_HTYPE_ENV);
1014 return (0);
1015 } /* }}} int o_shutdown */
1017 void module_register (void) /* {{{ */
1018 {
1019 plugin_register_complex_config ("oracle", o_config);
1020 plugin_register_init ("oracle", o_init);
1021 plugin_register_read ("oracle", o_read);
1022 plugin_register_shutdown ("oracle", o_shutdown);
1023 } /* }}} void module_register */
1025 /*
1026 * vim: shiftwidth=2 softtabstop=2 et fdm=marker
1027 */