/* * @(#)$Id: esqlc_v6.ec,v 2007.1 2007/06/09 18:15:08 jleffler Exp $ * * IBM Informix Database Driver for Perl (DBD::Informix) * Connection Management for ESQL/C Version 6.0x and later * * Copyright 1996-98 Jonathan Leffler * Copyright 2000 Informix Software Inc * Copyright 2002 IBM * Copyright 2007 Jonathan Leffler * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the Perl README file. */ /*TABSTOP=4*/ #include #include #include "esqlperl.h" #ifndef lint static const char rcs[] = "@(#)$Id: esqlc_v6.ec,v 2007.1 2007/06/09 18:15:08 jleffler Exp $"; #endif /* ================================================================= */ /* =================== Database Level Operations =================== */ /* ================================================================= */ static char *get_server_name(void) { char *srvr = 0; char *ix_srvr = getenv("INFORMIXSERVER"); if (ix_srvr == 0 || *ix_srvr == '\0' || (srvr = (char *)malloc(strlen(ix_srvr) + 2)) == 0) { sqlca.sqlcode = -952; } { srvr[0] = '@'; strcpy(&srvr[1], ix_srvr); } return(srvr); } static void rel_server_name(char *srvr) { free(srvr); } /* Execute a full CONNECT statement - no error checking */ static void full_connect(char *connection, char *dbase, char *user, char *pass) { EXEC SQL BEGIN DECLARE SECTION; char *dbconn = connection; char *dbname = dbase; char *dbpass = pass; char *dbuser = user; EXEC SQL END DECLARE SECTION; EXEC SQL CONNECT TO :dbname AS :dbconn USER :dbuser USING :dbpass WITH CONCURRENT TRANSACTION; } /* ** Use CONNECT to initiate database connection ** ** If both user and password are provided, then the USER clause is used. ** If no database is specified, a default connection will be made. ** ** Note that CONNECT statements (and DISCONNECT and SET CONNECTION) ** cannot be prepared. */ Boolean dbd_ix_connect(char *connection, char *dbase, char *user, char *pass) { EXEC SQL BEGIN DECLARE SECTION; char *dbconn; char *dbname; EXEC SQL END DECLARE SECTION; Boolean conn_ok = False; if (user != (char *)0 && pass != (char *)0) { /* User name and password provided */ if (dbase == (char *)0 || *dbase == '\0') { /* No database name; connect to '@server' */ dbname = get_server_name(); if (dbname != 0) { dbd_ix_debug(1, "CONNECT TO '%s' {DEFAULT with user info}\n", dbname); full_connect(connection, dbname, user, pass); rel_server_name(dbname); } } else { dbd_ix_debug(1, "CONNECT TO '%s' with user info\n", dbase); full_connect(connection, dbase, user, pass); } } else if (dbase == (char *)0 || *dbase == '\0') { /* Not frequently used, but valid */ /* Reset connection name to empty string, and connect to default */ /* Typically used to create database on default server */ /* Only works when no username/password needed to connect */ /* Nasty interface, overwriting connection name! */ *connection = '\0'; dbd_ix_debug(1, "CONNECT TO DEFAULT %s\n", "- no user info"); EXEC SQL CONNECT TO DEFAULT WITH CONCURRENT TRANSACTION; } else { dbconn = connection; dbname = dbase; dbd_ix_debug(1, "CONNECT TO '%s' - no user info\n", dbname); EXEC SQL CONNECT TO :dbname AS :dbconn WITH CONCURRENT TRANSACTION; } if (sqlca.sqlcode == 0) conn_ok = True; return(conn_ok); } /* Basic interface to DISCONNECT */ static void do_disconnect(char *connection) { EXEC SQL BEGIN DECLARE SECTION; char *dbconn = connection; EXEC SQL END DECLARE SECTION; if (*connection != '\0') { dbd_ix_debug(1, "DISCONNECT (%s)\n", connection); EXEC SQL DISCONNECT :dbconn; } else { dbd_ix_debug(1, "DISCONNECT DEFAULT%s\n", connection); EXEC SQL DISCONNECT DEFAULT; } } /* External interface to disconnect which handles various oddities */ void dbd_ix_disconnect(char *connection) { do_disconnect(connection); if (sqlca.sqlcode == -1800) { /* ** -1800: Invalid transaction state ** One problem was discovered by Nathan Neulinger (nneul@umr.edu). ** This can occur if the application is talking to a 5.0x engine. ** The solution seems to be to do CLOSE DATABASE, which also closes ** the connection (you get error -1803 if you try to DISCONNECT ** after doing CLOSE DATABASE). Ensure that the correct connection ** is current before closing the database. Bugs B64926 and B42204 ** are the source of the trouble. ** ** A second version of the problem found with 7.x databases where a ** transaction has been started and not completed. Trying CLOSE ** DATABASE fails with -759 (Cannot use database commands in an ** explicit database connection). It requires a ROLLBACK WORK ** instead, followed by a DISCONNECT. If you are connected to a ** 5.0x engine, then the ROLLBACK WORK succeeds, but the DISCONNECT ** fails a second time, but the CLOSE DATABASE ruse then works. ** ** If both these attempts fail, then the disconnect operation gives ** up, letting the database engine clean up. The engine will do a ** rollback when it notes the absence of the application. */ dbd_ix_debug(1, "DISCONNECT **FAILED: -1800 ** <<%s>>\n", connection); dbd_ix_setconnection(connection); dbd_ix_debug(1, "Try ROLLBACK WORK <<%s>>\n", connection); EXEC SQL ROLLBACK WORK; if (sqlca.sqlcode == 0) { dbd_ix_debug(1, "ROLLBACK WORK worked <<%s>>\n", connection); do_disconnect(connection); } if (sqlca.sqlcode < 0) { dbd_ix_debug(1, "DISCONNECT ** FAILED AGAIN (%ld) **\n", sqlca.sqlcode); dbd_ix_debug(1, "Try CLOSE DATABASE <<%s>>\n", connection); EXEC SQL CLOSE DATABASE; if (*connection == '\0') { dbd_ix_debug(1, "Retry DISCONNECT DEFAULT%s\n", connection); EXEC SQL DISCONNECT DEFAULT; } } } dbd_ix_debug(1, "DISCONNECT -- STATUS %ld\n", sqlca.sqlcode); } /* Ensure that the correct connection is current -- a no-op in version 5.0x */ void dbd_ix_setconnection(char *conn) { EXEC SQL BEGIN DECLARE SECTION; char *nm_connection = conn; EXEC SQL END DECLARE SECTION; if (nm_connection) { dbd_ix_debug(1, "SET CONNECTION %s\n", nm_connection); EXEC SQL SET CONNECTION :nm_connection; } else { dbd_ix_debug(1, "SET CONNECTION DEFAULT%s\n", ""); EXEC SQL SET CONNECTION DEFAULT; } }