2 * collectd - src/oracle.c
3 * Copyright (C) 2008 Florian octo Forster
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.
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.
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
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
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.
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.
40 * Oracle® is a registered trademark of Oracle Corporation and/or its
41 * affiliates. Other names may be trademarks of their respective owners.
44 * Florian octo Forster <octo at noris.net>
66 OCIStmt *oci_statement;
68 typedef struct o_query_s o_query_t;
80 OCISvcCtx *oci_service_context;
82 typedef struct o_database_s o_database_t;
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;
98 static void o_report_error (const char *where, /* {{{ */
99 const char *what, OCIError *eh)
105 status = OCIErrorGet (eh, /* record number = */ 1,
106 /* sqlstate = */ NULL,
109 (ub4) sizeof (buffer),
111 buffer[sizeof (buffer) - 1] = 0;
113 if (status == OCI_SUCCESS)
115 size_t buffer_length;
117 buffer_length = strlen (buffer);
118 while ((buffer_length > 0) && (buffer[buffer_length - 1] < 32))
121 buffer[buffer_length] = 0;
124 ERROR ("oracle plugin: %s: %s failed: %s",
125 where, what, buffer);
129 ERROR ("oracle plugin: %s: %s failed. Additionally, OCIErrorGet failed with status %i.",
130 where, what, status);
132 } /* }}} void o_report_error */
134 static void o_query_free (o_query_t *q) /* {{{ */
142 sfree (q->statement);
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]);
154 } /* }}} void o_query_free */
156 static void o_database_free (o_database_t *db) /* {{{ */
162 sfree (db->connect_id);
163 sfree (db->username);
164 sfree (db->password);
168 } /* }}} void o_database_free */
170 /* Configuration handling functions {{{
173 * <Query "plugin_instance0">
174 * Statement "SELECT name, value FROM table"
176 * InstancesFrom "name"
180 * <Database "plugin_instance1">
184 * Query "plugin_instance0"
189 static int o_config_set_string (char **ret_string, /* {{{ */
194 if ((ci->values_num != 1)
195 || (ci->values[0].type != OCONFIG_TYPE_STRING))
197 WARNING ("oracle plugin: The `%s' config option "
198 "needs exactly one string argument.", ci->key);
202 string = strdup (ci->values[0].value.string);
205 ERROR ("oracle plugin: strdup failed.");
209 if (*ret_string != NULL)
211 *ret_string = string;
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)
223 if (ci->values_num < 1)
225 WARNING ("oracle plugin: The `%s' config option "
226 "needs at least one argument.", ci->key);
230 for (i = 0; i < ci->values_num; i++)
232 if (ci->values[i].type != OCONFIG_TYPE_STRING)
234 WARNING ("oracle plugin: Argument %i to the `%s' option "
235 "is not a string.", i + 1, ci->key);
240 array_len = *ret_array_len;
241 array = (char **) realloc (*ret_array,
242 sizeof (char *) * (array_len + ci->values_num));
245 ERROR ("oracle plugin: realloc failed.");
250 for (i = 0; i < ci->values_num; i++)
252 array[array_len] = strdup (ci->values[i].value.string);
253 if (array[array_len] == NULL)
255 ERROR ("oracle plugin: strdup failed.");
256 *ret_array_len = array_len;
262 *ret_array_len = array_len;
264 } /* }}} int o_config_add_string */
266 static int o_config_add_query (oconfig_item_t *ci) /* {{{ */
272 if ((ci->values_num != 1)
273 || (ci->values[0].type != OCONFIG_TYPE_STRING))
275 WARNING ("oracle plugin: The `Query' block "
276 "needs exactly one string argument.");
280 q = (o_query_t *) malloc (sizeof (*q));
283 ERROR ("oracle plugin: malloc failed.");
286 memset (q, 0, sizeof (*q));
288 status = o_config_set_string (&q->name, ci);
295 /* Fill the `o_query_t' structure.. */
296 for (i = 0; i < ci->children_num; i++)
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);
310 WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
318 /* Check that all necessary options have been given. */
321 if (q->statement == NULL)
323 WARNING ("oracle plugin: `Statement' not given for query `%s'", q->name);
328 WARNING ("oracle plugin: `Type' not given for query `%s'", q->name);
331 if (q->instances == NULL)
333 WARNING ("oracle plugin: `InstancesFrom' not given for query `%s'", q->name);
336 if (q->values == NULL)
338 WARNING ("oracle plugin: `ValuesFrom' not given for query `%s'", q->name);
343 } /* while (status == 0) */
345 /* If all went well, add this query to the list of queries within the
346 * database structure. */
351 temp = (o_query_t **) realloc (queries,
352 sizeof (*queries) * (queries_num + 1));
355 ERROR ("oracle plugin: realloc failed");
361 queries[queries_num] = q;
373 } /* }}} int o_config_add_query */
375 static int o_config_add_database_query (o_database_t *db, /* {{{ */
382 if ((ci->values_num != 1)
383 || (ci->values[0].type != OCONFIG_TYPE_STRING))
385 WARNING ("oracle plugin: The `Query' config option "
386 "needs exactly one string argument.");
391 for (i = 0; i < queries_num; i++)
393 if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0)
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);
410 temp = (o_query_t **) realloc (db->queries,
411 sizeof (*db->queries) * (db->queries_num + 1));
414 ERROR ("oracle plugin: realloc failed");
420 db->queries[db->queries_num] = q;
425 } /* }}} int o_config_add_database_query */
427 static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
433 if ((ci->values_num != 1)
434 || (ci->values[0].type != OCONFIG_TYPE_STRING))
436 WARNING ("oracle plugin: The `Database' block "
437 "needs exactly one string argument.");
441 db = (o_database_t *) malloc (sizeof (*db));
444 ERROR ("oracle plugin: malloc failed.");
447 memset (db, 0, sizeof (*db));
449 status = o_config_set_string (&db->name, ci);
456 /* Fill the `o_database_t' structure.. */
457 for (i = 0; i < ci->children_num; i++)
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);
471 WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
479 /* Check that all necessary options have been given. */
482 if (db->connect_id == NULL)
484 WARNING ("oracle plugin: `ConnectID' not given for query `%s'", db->name);
487 if (db->username == NULL)
489 WARNING ("oracle plugin: `Username' not given for query `%s'", db->name);
492 if (db->password == NULL)
494 WARNING ("oracle plugin: `Password' not given for query `%s'", db->name);
499 } /* while (status == 0) */
501 /* If all went well, add this query to the list of queries within the
502 * database structure. */
507 temp = (o_database_t **) realloc (databases,
508 sizeof (*databases) * (databases_num + 1));
511 ERROR ("oracle plugin: realloc failed");
517 databases[databases_num] = db;
524 o_database_free (db);
529 } /* }}} int o_config_add_database */
531 static int o_config (oconfig_item_t *ci) /* {{{ */
535 for (i = 0; i < ci->children_num; i++)
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);
544 WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
546 } /* for (ci->children) */
549 } /* }}} int o_config */
551 /* }}} End of configuration handling functions */
553 static int o_init (void) /* {{{ */
560 status = OCIEnvCreate (&oci_env,
561 /* mode = */ OCI_THREADED,
562 /* context = */ NULL,
564 /* realloc = */ NULL,
566 /* user_data_size = */ 0,
567 /* user_data_ptr = */ NULL);
570 ERROR ("oracle plugin: OCIEnvCreate failed with status %i.", status);
574 status = OCIHandleAlloc (oci_env, (void *) &oci_error, OCI_HTYPE_ERROR,
575 /* user_data_size = */ 0, /* user_data = */ NULL);
576 if (status != OCI_SUCCESS)
578 ERROR ("oracle plugin: OCIHandleAlloc (OCI_HTYPE_ERROR) failed "
579 "with status %i.", status);
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)
589 value_list_t vl = VALUE_LIST_INIT;
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)
597 ERROR ("oracle plugin: malloc failed.");
600 vl.values_len = ds->ds_num;
602 for (i = 0; i < q->values_num; i++)
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);
616 if ((endptr == buffer_values[i]) || (errno != 0))
618 WARNING ("oracle plugin: o_submit: Parsing `%s' as %s failed.",
620 (ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
621 vl.values[i].gauge = NAN;
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, /* {{{ */
640 const data_set_t *ds;
641 ub4 param_counter; /* == number of columns */
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
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); /* {{{ */
662 WARNING ("oracle plugin: o_read_database_query (%s, %s): "
663 "plugin_get_ds (%s) failed. Please check if the type exists, "
665 db->name, q->name, q->type);
669 if (((size_t) ds->ds_num) != q->values_num)
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.",
677 ds->ds_num, (ds->ds_num == 1) ? "" : "s",
678 q->values_num, (q->values_num == 1) ? "" : "s");
682 /* Prepare the statement */
683 if (q->oci_statement == NULL) /* {{{ */
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)
689 o_report_error ("o_read_database_query", "OCIHandleAlloc", oci_error);
690 q->oci_statement = NULL;
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)
700 o_report_error ("o_read_database_query", "OCIStmtPrepare", oci_error);
701 OCIHandleFree (q->oci_statement, OCI_HTYPE_STMT);
702 q->oci_statement = NULL;
705 assert (q->oci_statement != NULL);
708 /* Execute the statement */
709 status = OCIStmtExecute (db->oci_service_context, /* {{{ */
714 /* snap_in = */ NULL, /* snap_out = */ NULL,
715 /* mode = */ OCI_DEFAULT);
716 if (status != OCI_SUCCESS)
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);
724 /* Acquire the number of columns returned. */
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)
731 o_report_error ("o_read_database_query", "OCIAttrGet", oci_error);
735 /* Allocate the following buffers:
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
743 #define NUMBER_BUFFER_SIZE 64
746 if (buffer_instances != NULL) { \
747 sfree (buffer_instances[0]); \
748 sfree (buffer_instances); \
750 if (buffer_values != NULL) { \
751 sfree (buffer_values[0]); \
752 sfree (buffer_values); \
754 sfree (index_instances); \
755 sfree (index_values); \
758 #define ALLOC_OR_FAIL(ptr, ptr_size) \
760 size_t alloc_size = (size_t) ((ptr_size)); \
761 (ptr) = malloc (alloc_size); \
762 if ((ptr) == NULL) { \
764 ERROR ("oracle plugin: o_read_database_query: malloc failed."); \
767 memset ((ptr), 0, alloc_size); \
770 /* Initialize everything to NULL so the above works. */
771 buffer_instances = NULL;
772 buffer_values = NULL;
773 index_instances = 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
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
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
798 for (j = 1; j <= param_counter; j++) /* {{{ */
801 size_t column_name_length;
802 char column_name_copy[DATA_MAX_NAME_LEN];
808 status = OCIParamGet (q->oci_statement, OCI_HTYPE_STMT, oci_error,
809 (void *) &oci_param, j);
810 if (status != OCI_SUCCESS)
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;
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)
825 o_report_error ("o_read_database_query", "OCIAttrGet (OCI_ATTR_NAME)",
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++)
842 if (strcasecmp (q->instances[i], column_name_copy) != 0)
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);
856 for (i = 0; i < q->values_num; i++)
858 if (strcasecmp (q->values[i], column_name_copy) != 0)
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);
867 DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> values[%zu]",
868 (unsigned int) j, column_name_copy, i);
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. */
878 for (i = 0; i < q->instances_num; i++)
880 if (index_instances[i] > 0)
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]);
891 for (i = 0; i < q->values_num; i++)
893 if (index_values[i] > 0)
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]);
911 /* Fetch and handle all the rows that matched the query. */
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)
919 status = OCI_SUCCESS;
922 else if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
924 o_report_error ("o_read_database_query", "OCIStmtFetch2", oci_error);
928 for (i = 0; i < q->instances_num; i++)
930 DEBUG ("oracle plugin: o_read_database_query: "
931 "buffer_instances[%zu] = %s;",
932 i, buffer_instances[i]);
935 for (i = 0; i < q->values_num; i++)
937 DEBUG ("oracle plugin: o_read_database_query: "
938 "buffer_values[%zu] = %s;",
939 i, buffer_values[i]);
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); */
951 } /* }}} int o_read_database_query */
953 static int o_read_database (o_database_t *db) /* {{{ */
958 if (db->oci_service_context == NULL)
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)
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;
973 assert (db->oci_service_context != NULL);
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]);
983 } /* }}} int o_read_database */
985 static int o_read (void) /* {{{ */
989 for (i = 0; i < databases_num; i++)
990 o_read_database (databases[i]);
993 } /* }}} int o_read */
995 static int o_shutdown (void) /* {{{ */
999 for (i = 0; i < databases_num; i++)
1000 if (databases[i]->oci_service_context != NULL)
1002 OCIHandleFree (databases[i]->oci_service_context, OCI_HTYPE_SVCCTX);
1003 databases[i]->oci_service_context = NULL;
1006 for (i = 0; i < queries_num; i++)
1007 if (queries[i]->oci_statement != NULL)
1009 OCIHandleFree (queries[i]->oci_statement, OCI_HTYPE_STMT);
1010 queries[i]->oci_statement = NULL;
1013 OCIHandleFree (oci_env, OCI_HTYPE_ENV);
1015 } /* }}} int o_shutdown */
1017 void module_register (void) /* {{{ */
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 */
1026 * vim: shiftwidth=2 softtabstop=2 et fdm=marker