/* ** gsql -- A simplified, unified interface to various SQL packages. ** Copyright (C) 1999 John Schulien ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. ** ** informix_backend.c -- the Informix database backend ** ** BIG FAT WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ** ** This module is not built by default and can only be distributed in ** source because of 2 reasons: ** ** 1. the GPL license of both this module and gxsnmp itself does not permit ** a link against the proprietary Informix libraries. ** ** 2. a binary module would contain parts of Informix code (at least one ** object file is always linked in - even if dynamic linkage is used), ** so there would most likely a runtime license from Informix be needed ** to distribute the binary code. ** ** This module is just a demonstration of how esql could be used with gxsnmp. ** */ #define DEBUG_QUERIES EXEC SQL include sqltypes.h; /* Grrr! Informix defines v_int, v_long etc., so undo this to make glib happy */ #undef v_int #undef v_long #undef v_float #include #include "plugins.h" static int cursor_cnt = 0; /******************************************************************************* ** ** Private structure to represent an open database ** ** The first two elements of this structure are mandatory. ** ** The Informix backend currently only supports one connection at a time. ** The syntax would be even more ugly (and a second *_cnt hack would be ** needed to keep track of the distinct connections). ** ** Informix keeps track of the connections internally, so we can just ** limit the structure to the mandatory parts. *******************************************************************************/ typedef struct _G_sql_connection { struct _G_sql_backend * backend; /* Pointer to the backend block */ gint errno; /* Last error reported on connection */ } G_sql_connection; /******************************************************************************* ** ** Private structure to represent an open query ** ** The first two elements of this structure are mandatory ** *******************************************************************************/ typedef struct _G_sql_query { struct _G_sql_connection * connection; /* Pointer to the connection block */ gint errno; /* Last error reported on query */ char curname[20]; /* Name of cursor */ char desname[20]; /* Name of descriptor */ char qidname[20]; /* Name of query id */ } G_sql_query; /* ** Next comes the public portion of the database interface definition. ** G_sql_backend and G_sql_query must be defined before including g_sql.h. ** with G_SQL_BACKEND defined. */ #define G_SQL_BACKEND #include "g_sql.h" /* ** Forward references */ static gboolean sql_initialize (G_sql_backend * dbb); static gboolean sql_terminate (G_sql_backend * dbb); static G_sql_connection * sql_connect (G_sql * db); static gboolean sql_disconnect (G_sql_connection * dbc); static GList * sql_enum_dbs (G_sql_connection * dbc); static gboolean sql_select (G_sql_connection * dbc, gchar * database); static G_sql_query * sql_query (G_sql_connection * dbc, gchar * query, gint querylen); static gboolean sql_free_query (G_sql_query * dbq); static gboolean sql_next_row (G_sql_query * dbq); static gchar * sql_field (G_sql_query * dbq, void ** accel, gchar * field, gint * length); /* ** The database backend control block for the Informix engine */ static struct _G_sql_backend backend = { "Informix", /* Name of the database engine */ NULL, /* Private data pointer */ sql_initialize, /* "Initialize" handler */ sql_terminate, /* "Terminate" handler */ sql_connect, /* "Connect" handler */ sql_disconnect, /* "Disconnect" handler */ sql_enum_dbs, /* "Enumerate databases" handler */ sql_select, /* "Select" handler */ sql_query, /* "Query" handler */ sql_free_query, /* "Free-Query" handler */ sql_next_row, /* "Next Row" handler */ sql_field /* "Get Field" handler */ }; void print_error() { g_warning("%d - %s", SQLCODE, SQLSTATE); } /******************************************************************************* ** ** sql_initialize () -- Perform database engine-specific initialization ** ** Informix has no initialization function, so just return TRUE. ** *******************************************************************************/ gboolean sql_initialize (G_sql_backend * dbb) { g_print ("Informix database plugin installed.\n"); return TRUE; } /******************************************************************************* ** ** sql_terminate () -- Perform database engine-specific termination ** ** Informix has no termination function, so just return TRUE. ** *******************************************************************************/ gboolean sql_terminate (G_sql_backend *dbb) { g_print ("Informix database plugin terminated.\n"); return TRUE; } /******************************************************************************* ** ** sql_connect () -- Connect to a Informix database server ** *******************************************************************************/ static G_sql_connection * sql_connect (G_sql * db) { G_sql_connection *dbc; int len; EXEC SQL BEGIN DECLARE SECTION; char *user, *pwd; char host[256]; EXEC SQL END DECLARE SECTION; dbc = (G_sql_connection *) g_new0 (G_sql_connection, 1); if (!dbc) return NULL; dbc->backend = &backend; g_snprintf(host, sizeof(host), "@%s", db->host); if (db->user && db->password && strcmp(db->user, "")) { user = db->user; pwd = db->password; EXEC SQL CONNECT TO :host USER :user USING :pwd; } else { EXEC SQL CONNECT TO :host; } if(!SQLCODE) { dbc->errno = 0; return dbc; } g_warning("CONNECT to database host '%s' failed.", db->host); print_error(); g_free(dbc); return NULL; } /******************************************************************************* ** ** sql_disconnect () -- Close a connection to a MySQL database server ** ** Close the open connection, then free the control block. ** ** Returns: TRUE -- if the operation was successful ** FALSE -- if the operation failed ** *******************************************************************************/ static gboolean sql_disconnect (G_sql_connection * dbc) { EXEC SQL DISCONNECT ALL; g_free (dbc); return TRUE; } /******************************************************************************* ** ** sql_enum_dbs () -- Enumerate the available databases ** ** Use the mysql_list_dbs() function to list the available databases on the ** connection. ** ** Returns: GList of databases if the operation succeeded ** NULL if the operation failed. ** *******************************************************************************/ static GList * sql_enum_dbs (G_sql_connection * dbc) { EXEC SQL BEGIN DECLARE SECTION; char name[256]; EXEC SQL END DECLARE SECTION; char *nname; GList *result = NULL; EXEC SQL DATABASE sysmaster; if (SQLCODE) { g_warning("Selecting sysmaster database failed."); print_error(); return NULL; } EXEC SQL DECLARE data_cur CURSOR FOR SELECT name into :name FROM informix.sysdatabases; if (SQLCODE) { g_warning("Query failed."); print_error(); return NULL; } EXEC SQL OPEN data_cur; for(;;) { EXEC SQL FETCH data_cur; if (SQLCODE) break; nname = g_strdup(name); result = g_list_append(result, nname); } EXEC SQL CLOSE data_cur; EXEC SQL FREE data_cur; return result; } /******************************************************************************* ** ** db_select () -- Select a database on an open Informix server connection ** ** Returns: TRUE -- if the select operation was successful ** FALSE -- if the select operation failed ** *******************************************************************************/ static gboolean sql_select (G_sql_connection * dbc, gchar *database) { EXEC SQL BEGIN DECLARE SECTION; char *execstr; EXEC SQL END DECLARE SECTION; int len; len = strlen(database) + 15; execstr = g_malloc(len); g_snprintf(execstr, len, "DATABASE '%s';", database); EXEC SQL PREPARE data_id FROM :execstr; EXEC SQL EXECUTE data_id; EXEC SQL FREE data_id; g_free(execstr); if(!SQLCODE) { return TRUE; } g_warning("DATABASE %s failed.", database); print_error(); return FALSE; } /******************************************************************************* ** ** sql_query () -- Issue a database query to an open Informix server connection ** ** Returns Values: ** ** The return value is an open database query structure, or NULL if the ** operation failed. ** *******************************************************************************/ static G_sql_query * sql_query (G_sql_connection * dbc, gchar * query, gint querylen) { EXEC SQL BEGIN DECLARE SECTION; char *execstr; char *cname; char *qname; char *dname; EXEC SQL END DECLARE SECTION; G_sql_query *dbq; if (!(dbq = (G_sql_query *) g_new0 (G_sql_query, 1))) { g_print ("Informix backend: out of memory\n"); return NULL; } dbq->connection = dbc; g_snprintf(dbq->curname, sizeof(dbq->curname), "c_%d", cursor_cnt); g_snprintf(dbq->desname, sizeof(dbq->desname), "d_%d", cursor_cnt); g_snprintf(dbq->qidname, sizeof(dbq->qidname), "q_%d", cursor_cnt++); cname = dbq->curname; dname = dbq->desname; qname = dbq->qidname; g_print ("cname: %s, dname: %s, qname: %s\n", cname, dname, qname); execstr=query; EXEC SQL PREPARE :qname FROM :execstr; EXEC SQL ALLOCATE DESCRIPTOR :dname; EXEC SQL DESCRIBE :qname USING SQL DESCRIPTOR :dname; if (SQLCODE) /* no SELECT statement */ { g_print("No select statement in %s, no cursor needed.\n", query); strcpy(dbq->curname, ""); EXEC SQL EXECUTE :qname; return dbq; } EXEC SQL DECLARE :cname CURSOR FOR :qname; if(SQLCODE) { g_warning("Error in SELECT statement."); print_error(); EXEC SQL FREE :cname; EXEC SQL FREE :qname; EXEC SQL DEALLOCATE DESCRIPTOR :dname; return NULL; } g_print("Opening CURSOR for %s.\n",query); EXEC SQL OPEN :cname; return dbq; } /******************************************************************************* ** ** sql_free_query () -- Free up a query structure ** *******************************************************************************/ static gboolean sql_free_query (G_sql_query *dbq) { EXEC SQL BEGIN DECLARE SECTION; char *cname; char *qname; char *dname; EXEC SQL END DECLARE SECTION; cname = dbq->curname; dname = dbq->desname; qname = dbq->qidname; if (strcmp(cname,"")) { EXEC SQL CLOSE :cname; EXEC SQL FREE :cname; } EXEC SQL FREE :qname; EXEC SQL DEALLOCATE DESCRIPTOR :dname; g_free (dbq); return TRUE; } /******************************************************************************* ** ** sql_next_row () -- Select the next row in a query result ** ** This subroutine returns the private handle of the next available row ** in a query result. ** ** Return values: ** ** TRUE -- If the operation succeeded ** FALSE -- If the operation failed ** *******************************************************************************/ static gboolean sql_next_row (G_sql_query * dbq) { EXEC SQL BEGIN DECLARE SECTION; char *cname; char *qname; char *dname; EXEC SQL END DECLARE SECTION; cname = dbq->curname; dname = dbq->desname; qname = dbq->qidname; EXEC SQL fetch :cname USING SQL DESCRIPTOR :dname; if(!SQLCODE) { return TRUE; } return FALSE; } /******************************************************************************* ** ** sql_field () -- Read a named field from a database row, ** and return a pointer to the data, and the length ** of the returned data. ** ** This subroutine returns the named field from a selected database row. ** This subroutine may read binary data that might contain zeroes. ** ** Return values: ** ** The return value is a pointer to the raw database field data, or NULL if ** the operation failed. The length of the result is stored in the "length" ** parameter. ** ** The caller is responsible for g_free()'ing the allocated storage. ** ** Since searching through the field table each time we are called is ** expensive, accelerators are used to speed up this process for subsequent ** row lookups on the same query. The accelerator field, provided by the ** caller, will be unique for each field name. We simply store the found ** row index plus one in the caller's accelerator field. Then, on ** subsequent calls, we can skip the search by using the accelerator ** value minus one for the field index. ** *******************************************************************************/ static gchar * sql_field (G_sql_query * dbq, G_sql_accelerator *accel, gchar * field, gint * length) { EXEC SQL BEGIN DECLARE SECTION; int i, type, dec, ind, count, len; char name[40]; static char ch[1024]; long dat; float flt; char *cname; char *qname; char *dname; EXEC SQL END DECLARE SECTION; static char buf [128]; int j; cname = dbq->curname; dname = dbq->desname; qname = dbq->qidname; EXEC SQL GET DESCRIPTOR :dname :count = COUNT; if (!*accel) for (i = 1; i <= count; i++) { EXEC SQL GET DESCRIPTOR :dname VALUE :i :type = TYPE, :name = NAME; j = strlen(name); if (j) j--; while(j && name[j] == ' ') { name[j] = '\0'; j--; } if (!g_strcasecmp (field, name)) *accel = (G_sql_accelerator) i; } if (!*accel) { g_print ("Informix backend: Field %s not found in selected row\n", field); return NULL; } i = (int)(*accel); EXEC SQL GET DESCRIPTOR :dname VALUE :i :type = TYPE; switch (type) { case SQLSERIAL: case SQLINT: case SQLSMINT: case SQLDECIMAL: case SQLMONEY: EXEC SQL GET DESCRIPTOR :dname VALUE :i :ind = INDICATOR, :dec = DATA; if (ind == -1) { *length = 0; return NULL; } g_snprintf(buf, sizeof(buf), "%d", dec); *length = strlen(buf); return buf; break; case SQLFLOAT: EXEC SQL GET DESCRIPTOR :dname VALUE :i :ind = INDICATOR, :flt = DATA; if (ind == -1) { *length = 0; return NULL; } g_snprintf(buf, sizeof(buf), "%f", flt); *length = strlen(buf); return buf; break; case SQLDATE: EXEC SQL GET DESCRIPTOR :dname VALUE :i :ind = INDICATOR, :dat = DATA; if (ind == -1) { *length = 0; return NULL; } rfmtdate(dat, "mmm. dd, yyyy", buf); *length = strlen(buf); return buf; break; case SQLVCHAR: case SQLCHAR: EXEC SQL GET DESCRIPTOR :dname VALUE :i :ind = INDICATOR, :ch = DATA, :len =LENGTH; if (ind == -1) { *length = 0; return NULL; } #if 0 /* Apparently the len parameter means the length of the field, not the lenther of the response in Informix */ *length = len; ch[len] = '\0'; #else j = strlen(ch); if (j) j--; while(j && ch[j] == ' ') { ch[j] = '\0'; j--; } *length = strlen(ch); #endif return ch; } } /****************************************************************************** ** ** Subroutine to load the plugin. Set the plugin type to PLUGIN_DATABASE. ** ******************************************************************************/ int load_plugin (PluginData * pd) { pd->type = PLUGIN_DATABASE; pd->name = g_strdup ("Informix DB backend"); return 0; } /****************************************************************************** ** ** Subroutine to unload the plugin ** ******************************************************************************/ void unload_plugin (PluginData * pd) { g_sql_unregister_backend (&backend); } /****************************************************************************** ** ** Subroutine to start the plugin ** ******************************************************************************/ void start_plugin (PluginData * pd) { g_print ("Starting Informix backend plugin ... "); g_sql_register_backend (&backend); } /* EOF */