/*
* @(#)$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 <string.h>
#include <stdlib.h>
#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;
}
}