/*********************************************************************
 *
 * This is based on code created by Peter Harvey,
 * (pharvey@codebydesign.com).
 *
 * Modified and extended by Nick Gorham
 * (nick@easysoft.com).
 *
 * Any bugs or problems should be considered the fault of Nick and not
 * Peter.
 *
 * copyright (c) 1999 Nick Gorham
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **********************************************************************
 *
 * $Id: SQLConnect.c,v 1.2 1999/12/10 01:50:32 harvey Exp $
 *
 * $Log: SQLConnect.c,v $
 * Revision 1.2  1999/12/10 01:50:32  harvey
 * Updated with current sources from unixODBC cvs.
 *
 * Revision 1.18  1999/11/13 23:40:58  ngorham
 *
 * Alter the way DM logging works
 * Upgrade the Postgres driver to 6.4.6
 *
 * Revision 1.17  1999/11/10 03:51:33  ngorham
 *
 * Update the error reporting in the DM to enable ODBC 3 and 2 calls to
 * work at the same time
 *
 * Revision 1.16  1999/10/24 23:54:17  ngorham
 *
 * First part of the changes to the error reporting
 *
 * Revision 1.15  1999/10/14 06:49:24  ngorham
 *
 * Remove @all_includes@ from Drivers/MiniSQL/Makefile.am
 *
 * Revision 1.14  1999/10/09 00:15:58  ngorham
 *
 * Add mapping from SQL_TYPE_X to SQL_X and SQL_C_TYPE_X to SQL_C_X
 * when the driver is a ODBC 2 one
 *
 * Revision 1.13  1999/10/07 20:39:25  ngorham
 *
 * Added .cvsignore files and fixed a couple of bugs in the DM
 *
 * Revision 1.12  1999/10/06 07:10:46  ngorham
 *
 * As the book says check dlerror after a dl func
 *
 * Revision 1.11  1999/10/06 07:01:25  ngorham
 *
 * Added more support for non linux platforms
 *
 * Revision 1.10  1999/09/26 18:55:03  ngorham
 *
 * Fixed a problem where the cursor lib was being used by default
 *
 * Revision 1.9  1999/09/24 22:54:52  ngorham
 *
 * Fixed some unchanged dlopen,dlsym,dlclose functions
 *
 * Revision 1.8  1999/09/21 22:34:24  ngorham
 *
 * Improve performance by removing unneeded logging calls when logging is
 * disabled
 *
 * Revision 1.7  1999/09/20 21:46:49  ngorham
 *
 * Added support for libtld dlopen replace
 *
 * Revision 1.6  1999/09/19 22:24:33  ngorham
 *
 * Added support for the cursor library
 *
 * Revision 1.5  1999/08/03 21:47:39  shandyb
 * Moving to automake: changed files in DriverManager
 *
 * Revision 1.4  1999/07/10 21:10:15  ngorham
 *
 * Adjust error sqlstate from driver manager, depending on requested
 * version (ODBC2/3)
 *
 * Revision 1.3  1999/07/04 21:05:07  ngorham
 *
 * Add LGPL Headers to code
 *
 * Revision 1.2  1999/06/30 23:56:54  ngorham
 *
 * Add initial thread safety code
 *
 * Revision 1.1.1.1  1999/05/29 13:41:05  sShandyb
 * first go at it
 *
 * Revision 1.4  1999/06/07 01:29:30  pharvey
 * *** empty log message ***
 *
 * Revision 1.3  1999/06/02 20:12:10  ngorham
 *
 * Fixed botched log entry, and removed the dos \r from the sql header files.
 *
 * Revision 1.2  1999/06/02 19:57:20  ngorham
 *
 * Added code to check if a attempt is being made to compile with a C++
 * Compiler, and issue a message.
 * Start work on the ODBC2-3 conversions.
 *
 * Revision 1.1.1.1  1999/05/27 18:23:17  pharvey
 * Imported sources
 *
 * Revision 1.7  1999/05/09 23:27:11  nick
 * All the API done now
 *
 * Revision 1.6  1999/05/04 22:41:12  nick
 * and another night ends
 *
 * Revision 1.5  1999/05/03 19:50:43  nick
 * Another check point
 *
 * Revision 1.4  1999/04/30 16:22:47  nick
 * Another checkpoint
 *
 * Revision 1.3  1999/04/29 21:40:58  nick
 * End of another night :-)
 *
 * Revision 1.2  1999/04/29 20:47:37  nick
 * Another checkpoint
 *
 * Revision 1.1  1999/04/25 23:06:11  nick
 * Initial revision
 *
 *
 **********************************************************************/

#include "drivermanager.h"

static char const rcsid[]= "$RCSfile: SQLConnect.c,v $ $Revision: 1.2 $";

#define CURSOR_LIB      "libodbccr.so"

/*
 * structure to contain the loaded lib entry points
 */

static struct driver_func  template_func[] =
{
    /* 00 */ { SQL_API_SQLALLOCCONNECT,      "SQLAllocConnect", SQLAllocConnect },
    /* 01 */ { SQL_API_SQLALLOCENV,          "SQLAllocEnv", SQLAllocEnv  },
    /* 02 */ { SQL_API_SQLALLOCHANDLE,       "SQLAllocHandle", SQLAllocHandle },
    /* 03 */ { SQL_API_SQLALLOCSTMT,         "SQLAllocStmt", SQLAllocStmt },
    /* 04 */ { SQL_API_SQLALLOCHANDLESTD,    "SQLAllocHandleStd", SQLAllocHandleStd },
    /* 05 */ { SQL_API_SQLBINDCOL,           "SQLBindCol", SQLBindCol },
    /* 06 */ { SQL_API_SQLBINDPARAM,         "SQLBindParam", SQLBindParam },
    /* 07 */ { SQL_API_SQLBINDPARAMETER,     "SQLBindParameter", SQLBindParameter },
    /* 08 */ { SQL_API_SQLBROWSECONNECT,     "SQLBrowseConnect", SQLBrowseConnect },
    /* 09 */ { SQL_API_SQLBULKOPERATIONS,    "SQLBulkOperations", SQLBulkOperations },
    /* 10 */ { SQL_API_SQLCANCEL,            "SQLCancel", SQLCancel },
    /* 11 */ { SQL_API_SQLCLOSECURSOR,       "SQLCloseCursor", SQLCloseCursor },
    /* 12 */ { SQL_API_SQLCOLATTRIBUTE,      "SQLColAttribute", SQLColAttribute },
    /* 13 */ { SQL_API_SQLCOLATTRIBUTES,     "SQLColAttributes", SQLColAttributes },
    /* 14 */ { SQL_API_SQLCOLUMNPRIVILEGES,  "SQLColumnPrivileges", SQLColumnPrivileges },
    /* 15 */ { SQL_API_SQLCOLUMNS,           "SQLColumns", SQLColumns },
    /* 16 */ { SQL_API_SQLCONNECT,           "SQLConnect", SQLConnect },
    /* 17 */ { SQL_API_SQLCOPYDESC,          "SQLCopyDesc", SQLCopyDesc },
    /* 18 */ { SQL_API_SQLDATASOURCES,       "SQLDataSources", SQLDataSources },
    /* 19 */ { SQL_API_SQLDESCRIBECOL,       "SQLDescribeCol", SQLDescribeCol },
    /* 20 */ { SQL_API_SQLDESCRIBEPARAM,     "SQLDescribeParam", SQLDescribeParam },
    /* 21 */ { SQL_API_SQLDISCONNECT,        "SQLDisconnect", SQLDisconnect },
    /* 22 */ { SQL_API_SQLDRIVERCONNECT,     "SQLDriverConnect", SQLDriverConnect },
    /* 23 */ { SQL_API_SQLDRIVERS,           "SQLDrivers", SQLDrivers },
    /* 24 */ { SQL_API_SQLENDTRAN,           "SQLEndTran", SQLEndTran },
    /* 25 */ { SQL_API_SQLERROR,             "SQLError", SQLError },
    /* 26 */ { SQL_API_SQLEXECDIRECT,        "SQLExecDirect", SQLExecDirect },
    /* 27 */ { SQL_API_SQLEXECUTE,           "SQLExecute", SQLExecute },
    /* 28 */ { SQL_API_SQLEXTENDEDFETCH,     "SQLExtendedFetch", SQLExtendedFetch },
    /* 29 */ { SQL_API_SQLFETCH,             "SQLFetch", SQLFetch },
    /* 30 */ { SQL_API_SQLFETCHSCROLL,       "SQLFetchScroll", SQLFetchScroll },
    /* 31 */ { SQL_API_SQLFOREIGNKEYS,       "SQLForeignKeys", SQLForeignKeys },
    /* 32 */ { SQL_API_SQLFREEENV,           "SQLFreeEnv", SQLFreeEnv },
    /* 33 */ { SQL_API_SQLFREEHANDLE,        "SQLFreeHandle", SQLFreeHandle },
    /* 34 */ { SQL_API_SQLFREESTMT,          "SQLFreeStmt", SQLFreeStmt },
    /* 35 */ { SQL_API_SQLFREECONNECT,       "SQLFreeConnect", SQLFreeConnect },
    /* 36 */ { SQL_API_SQLGETCONNECTATTR,    "SQLGetConnectAttr", SQLGetConnectAttr },
    /* 37 */ { SQL_API_SQLGETCONNECTOPTION,  "SQLGetConnectOption", SQLGetConnectOption },
    /* 38 */ { SQL_API_SQLGETCURSORNAME,     "SQLGetCursorName", SQLGetCursorName },
    /* 39 */ { SQL_API_SQLGETDATA,           "SQLGetData", SQLGetData },
    /* 40 */ { SQL_API_SQLGETDESCFIELD,      "SQLGetDescField", SQLGetDescField },
    /* 41 */ { SQL_API_SQLGETDESCREC,        "SQLGetDescRec", SQLGetDescRec },
    /* 42 */ { SQL_API_SQLGETDIAGFIELD,      "SQLGetDiagField", SQLGetDiagField },
    /* 43 */ { SQL_API_SQLGETENVATTR,        "SQLGetEnvAttr", SQLGetEnvAttr },
    /* 44 */ { SQL_API_SQLGETFUNCTIONS,      "SQLGetFunctions", SQLGetFunctions },
    /* 45 */ { SQL_API_SQLGETINFO,           "SQLGetInfo", SQLGetInfo },
    /* 46 */ { SQL_API_SQLGETSTMTATTR,       "SQLGetStmtAttr", SQLGetStmtAttr },
    /* 47 */ { SQL_API_SQLGETSTMTOPTION,     "SQLGetStmtOption", SQLGetStmtOption },
    /* 48 */ { SQL_API_SQLGETTYPEINFO,       "SQLGetTypeInfo", SQLGetTypeInfo },
    /* 49 */ { SQL_API_SQLMORERESULTS,       "SQLMoreResults", SQLMoreResults },
    /* 50 */ { SQL_API_SQLNATIVESQL,         "SQLNativeSql", SQLNativeSql },
    /* 51 */ { SQL_API_SQLNUMPARAMS,         "SQLNumParams", SQLNumParams },
    /* 52 */ { SQL_API_SQLNUMRESULTCOLS,     "SQLNumResultCols", SQLNumResultCols },
    /* 53 */ { SQL_API_SQLPARAMDATA,         "SQLParamData", SQLParamData },
    /* 54 */ { SQL_API_SQLPARAMOPTIONS,      "SQLParamOptions", SQLParamOptions },
    /* 55 */ { SQL_API_SQLPREPARE,           "SQLPrepare", SQLPrepare },
    /* 56 */ { SQL_API_SQLPRIMARYKEYS,       "SQLPrimaryKeys", SQLPrimaryKeys },
    /* 57 */ { SQL_API_SQLPROCEDURECOLUMNS,  "SQLProcedureColumns", SQLProcedureColumns },
    /* 58 */ { SQL_API_SQLPROCEDURES,        "SQLProcedures", SQLProcedures },
    /* 59 */ { SQL_API_SQLPUTDATA,           "SQLPutData", SQLPutData },
    /* 60 */ { SQL_API_SQLROWCOUNT,          "SQLRowCount", SQLRowCount },
    /* 61 */ { SQL_API_SQLSETCONNECTATTR,    "SQLSetConnectAttr", SQLSetConnectAttr },
    /* 62 */ { SQL_API_SQLSETCONNECTOPTION,  "SQLSetConnectOption", SQLSetConnectOption },
    /* 63 */ { SQL_API_SQLSETCURSORNAME,     "SQLSetCursorName", SQLSetCursorName },
    /* 64 */ { SQL_API_SQLSETDESCFIELD,      "SQLSetDescField", SQLSetDescField },
    /* 65 */ { SQL_API_SQLSETDESCREC,        "SQLSetDescRec", SQLSetDescRec },
    /* 66 */ { SQL_API_SQLSETENVATTR,        "SQLSetEnvAttr", SQLSetEnvAttr },
    /* 67 */ { SQL_API_SQLSETPARAM,          "SQLSetParam", SQLSetParam },
    /* 68 */ { SQL_API_SQLSETPOS,            "SQLSetPos", SQLSetPos },
    /* 69 */ { SQL_API_SQLSETSCROLLOPTIONS,  "SQLSetScrollOptions", SQLSetScrollOptions },
    /* 70 */ { SQL_API_SQLSETSTMTATTR,       "SQLSetStmtAttr", SQLSetStmtAttr },
    /* 71 */ { SQL_API_SQLSETSTMTOPTION,     "SQLSetStmtOption", SQLSetStmtOption },
    /* 72 */ { SQL_API_SQLSPECIALCOLUMNS,    "SQLSpecialColumns", SQLSpecialColumns },
    /* 73 */ { SQL_API_SQLSTATISTICS,        "SQLStatistics", SQLStatistics },
    /* 74 */ { SQL_API_SQLTABLEPRIVILEGES,   "SQLTablePrivileges", SQLTablePrivileges },
    /* 75 */ { SQL_API_SQLTABLES,            "SQLTables", SQLTables },
    /* 76 */ { SQL_API_SQLTRANSACT,          "SQLTransact", SQLTransact },
    /* 77 */ { SQL_API_SQLGETDIAGREC,        "SQLGetDiagRec", SQLGetDiagRec },
};
/*
 * open the library, extract the names, and do setup
 * before the actual connect.
 */

int __connect_part_one( DMHDBC connection, char *driver_lib, char *driver_name )
{
    int i;
    int ret;
    SQLCHAR s0[ 20 ];
    int threading_level;
    char threading_string[ 50 ];

    /*
     * check to see if we want to alter the default threading level
     * before opening the lib
     */

    SQLGetPrivateProfileString( driver_name, "Threading", "3",
				threading_string, sizeof( threading_string ), 
                "ODBCINST.INI" );

    threading_level = atoi( threading_string );

    if ( threading_level >= 0 && threading_level <= 3 )
    {
        dbc_change_thread_support( connection, threading_level );
    }

    /*
     * open the lib
     */

    connection -> driver_env = (SQLHANDLE)NULL;
    connection -> driver_dbc = (SQLHANDLE)NULL;
    connection -> functions = NULL;
    connection -> dl_handle = NULL;

    if ( !(connection -> dl_handle = lt_dlopen( driver_lib )))
    {
        char txt[ 256 ];

        sprintf( txt, "Can't open lib '%s' : %s", 
                driver_lib, lt_dlerror());

        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                txt );

        __post_internal_error( &connection -> error,
                ERROR_IM003, txt,
                connection -> environment -> requested_version );
        return 0;
    }

    /*
     * extract all the function entry points
     */
    if ( !(connection -> functions = malloc( sizeof( template_func ))))
    {
        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                "Error: IM001" );

        __post_internal_error( &connection -> error,
                ERROR_HY001, NULL,
                connection -> environment -> requested_version );
        return 0;
    }

    memcpy( connection -> functions, template_func,
            sizeof( template_func ));

    for ( i = 0;
            i < sizeof( template_func ) / sizeof( template_func[ 0 ] );
            i ++ )
    {
        connection -> functions[ i ].func =
            lt_dlsym( connection -> dl_handle,
                    connection -> functions[ i ].name );

        if ( lt_dlerror())
        {
            connection -> functions[ i ].func = NULL;
        }

        /*
         * blank out ones that are in the DM to fix a big 
         * with glib 2.0.6
         */

		if ( connection -> functions[ i ].func &&
			connection -> functions[ i ].func == 
            connection -> functions[ i ].dm_func )
		{	
			connection -> functions[ i ].func = NULL;
		}

        connection -> functions[ i ].can_supply =
            ( connection -> functions[ i ].func != NULL );
    }

    /*
     * allocate a env handle
     */

    if ( CHECK_SQLALLOCHANDLE( connection ))
    {
        ret = SQLALLOCHANDLE( connection,
                SQL_HANDLE_ENV,
                SQL_NULL_HENV,
                &connection -> driver_env,
                connection );
    }
    else if ( CHECK_SQLALLOCENV( connection ))
    {
        ret = SQLALLOCENV( connection,
                &connection -> driver_env );
    }
    else
    {
        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                "Error: IM004" );

        __post_internal_error( &connection -> error,
                ERROR_IM004, NULL,
                connection -> environment -> requested_version );
        return 0;
    }

    if ( ret )
    {
        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                "Error: HY004" );

        __post_internal_error( &connection -> error,
                ERROR_HY004, NULL,
                connection -> environment -> requested_version );
        return 0;
    }

    /*
     * if it looks like a 3.x driver, try setting the interface type
     * to 3.x
     */
    if ( CHECK_SQLSETENVATTR( connection ))
    {
        ret = SQLSETENVATTR( connection,
                connection -> driver_env,
                SQL_ATTR_ODBC_VERSION,
                connection -> environment -> requested_version,
                0 );

        /*
         * if it don't set then assume a 2.x driver
         */

        if ( ret )
        {
            connection -> driver_version = SQL_OV_ODBC2;
        }
        else
        {
            if ( CHECK_SQLGETENVATTR( connection ))
            {
                SQLINTEGER actual_version;

                ret = SQLGETENVATTR( connection,
                    connection -> driver_env,
                    SQL_ATTR_ODBC_VERSION,
                    &actual_version,
                    0,
                    NULL );

                if ( !ret )
                {
                    connection -> driver_version = actual_version;
                }
                else
                {
                    connection -> driver_version =
                        connection -> environment -> requested_version;
                }
            }
            else
            {
                connection -> driver_version =
                    connection -> environment -> requested_version;
            }
        }
    }
    else
    {
        connection -> driver_version = SQL_OV_ODBC2;
    }

    /*
     * allocate a connection handle
     */
    if ( connection -> driver_version == SQL_OV_ODBC3 )
    {
        if ( CHECK_SQLALLOCHANDLE( connection ))
        {
            ret = SQLALLOCHANDLE( connection,
                    SQL_HANDLE_DBC,
                    connection -> driver_env,
                    &connection -> driver_dbc,
                    connection );

            if ( ret )
            {
                dm_log_write( connection -> log_handle,
                        __get_pid( s0 ),
                        __FILE__,
                        __LINE__,
                        LOG_INFO,
                        LOG_INFO,
                        "Error: IM005" );

                __post_internal_error( &connection -> error,
                        ERROR_IM005, NULL,
                        connection -> environment -> requested_version );
                return 0;
            }
        }
        else if ( CHECK_SQLALLOCCONNECT( connection ))
        {
            ret = SQLALLOCCONNECT( connection,
                    connection -> driver_env,
                    &connection -> driver_dbc );

            if ( ret )
            {
                dm_log_write( connection -> log_handle,
                        __get_pid( s0 ),
                        __FILE__,
                        __LINE__,
                        LOG_INFO,
                        LOG_INFO,
                        "Error: IM005" );

                __post_internal_error( &connection -> error,
                        ERROR_IM005, NULL,
                        connection -> environment -> requested_version );
                return 0;
            }
        }
        else
        {
            dm_log_write( connection -> log_handle,
                    __get_pid( s0 ),
                    __FILE__,
                    __LINE__,
                    LOG_INFO,
                    LOG_INFO,
                    "Error: IM005" );

            __post_internal_error( &connection -> error,
                    ERROR_IM005, NULL,
                    connection -> environment -> requested_version );
            return 0;
        }
    }
    else
    {
        if ( CHECK_SQLALLOCCONNECT( connection ))
        {
            ret = SQLALLOCCONNECT( connection,
                    connection -> driver_env,
                    &connection -> driver_dbc );

            if ( ret )
            {
                dm_log_write( connection -> log_handle,
                        __get_pid( s0 ),
                        __FILE__,
                        __LINE__,
                        LOG_INFO,
                        LOG_INFO,
                        "Error: IM005" );

                __post_internal_error( &connection -> error,
                        ERROR_IM005, NULL,
                        connection -> environment -> requested_version );
                return 0;
            }
        }
        else if ( CHECK_SQLALLOCHANDLE( connection ))
        {
            ret = SQLALLOCHANDLE( connection,
                    SQL_HANDLE_DBC,
                    connection -> driver_env,
                    &connection -> driver_dbc,
                    connection );

            if ( ret )
            {
                dm_log_write( connection -> log_handle,
                        __get_pid( s0 ),
                        __FILE__,
                        __LINE__,
                        LOG_INFO,
                        LOG_INFO,
                        "Error: IM005" );

                __post_internal_error( &connection -> error,
                        ERROR_IM005, NULL,
                        connection -> environment -> requested_version );
                return 0;
            }
        }
        else
        {
            dm_log_write( connection -> log_handle,
                    __get_pid( s0 ),
                    __FILE__,
                    __LINE__,
                    LOG_INFO,
                    LOG_INFO,
                    "Error: IM005" );

            __post_internal_error( &connection -> error,
                    ERROR_IM005, NULL,
                    connection -> environment -> requested_version );
            return 0;
        }
    }

    return 1;
}

/*
 * extract the available functions and call SQLSetConnectAttr
 */

int __connect_part_two( DMHDBC connection )
{
    int i, use_cursor;
    SQLCHAR s0[ 20 ];

    /*
     * Call SQLFunctions to get the supported list and
     * mask out those that are exported but not supported
     */

    if ( CHECK_SQLGETFUNCTIONS( connection ))
    {
        for ( i = 0;
            i < sizeof( template_func ) / sizeof( template_func[ 0 ] );
            i ++ )
        {
            if ( connection -> functions[ i ].func )
            {
                SQLRETURN ret;
                SQLUSMALLINT supported;

                if ( i > 100 )
                {
                    supported = FALSE;
                }
                else
                {
                    ret = SQLGETFUNCTIONS( connection,
                            connection -> driver_dbc,
                            connection -> functions[ i ].ordinal,
                            &supported );
                }

                if ( supported == SQL_FALSE )
                {
                    connection -> functions[ i ].func = NULL;
                    connection -> functions[ i ].can_supply = 0;
                }
            }
        }
    }

    /*
     * CoLAttributes is the same as ColAttribute
     */

    if ( connection -> functions[ DM_SQLCOLATTRIBUTE ].func &&
        !connection -> functions[ DM_SQLCOLATTRIBUTES ].func )
    {
        connection -> functions[ DM_SQLCOLATTRIBUTES ].func =
            connection -> functions[ DM_SQLCOLATTRIBUTE ].func;
        connection -> functions[ DM_SQLCOLATTRIBUTES ].can_supply = 1;
    }
    if ( connection -> functions[ DM_SQLCOLATTRIBUTES ].func &&
        !connection -> functions[ DM_SQLCOLATTRIBUTE ].func )
    {
        connection -> functions[ DM_SQLCOLATTRIBUTE ].func =
            connection -> functions[ DM_SQLCOLATTRIBUTES ].func;
        connection -> functions[ DM_SQLCOLATTRIBUTE ].can_supply = 1;
    }

    /*
     * mark the functions that the driver manager does
     */

    /*
     * SQLDatasources
     */
    connection -> functions[ DM_SQLDATASOURCES ].can_supply = 1;

    /*
     * SQLDrivers
     */
    connection -> functions[ DM_SQLDRIVERS ].can_supply = 1;

    /*
     * SQLAllocHandleStd
     */
    connection -> functions[ DM_SQLALLOCHANDLESTD ].can_supply = 1;

    /*
     * add all the functions that are supported via ODBC 2<->3
     * issues
     */
    if ( !connection -> functions[ DM_SQLALLOCENV ].func &&
            connection -> functions[ DM_SQLALLOCHANDLE ].func )
    {
        connection -> functions[ DM_SQLALLOCENV ].can_supply = 1;
    }
    if ( !connection -> functions[ DM_SQLALLOCCONNECT ].func &&
            connection -> functions[ DM_SQLALLOCHANDLE ].func )
    {
        connection -> functions[ DM_SQLALLOCCONNECT ].can_supply = 1;
    }
    if ( !connection -> functions[ DM_SQLALLOCSTMT ].func &&
            connection -> functions[ DM_SQLALLOCHANDLE ].func )
    {
        connection -> functions[ DM_SQLALLOCSTMT ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLFREEENV ].func &&
            connection -> functions[ DM_SQLFREEHANDLE ].func )
    {
        connection -> functions[ DM_SQLFREEENV ].can_supply = 1;
    }
    if ( !connection -> functions[ DM_SQLFREECONNECT ].func &&
            connection -> functions[ DM_SQLFREEHANDLE ].func )
    {
        connection -> functions[ DM_SQLALLOCCONNECT ].can_supply = 1;
    }

    /*
     * ODBC 3 still needs SQLFreeStmt
     */

    /*
     * this is only partial, as we can't support a descriptor alloc
     */
    if ( !connection -> functions[ DM_SQLALLOCHANDLE ].func &&
            connection -> functions[ DM_SQLALLOCENV ].func &&
            connection -> functions[ DM_SQLALLOCCONNECT ].func &&
            connection -> functions[ DM_SQLALLOCHANDLE ].func )
    {
        connection -> functions[ DM_SQLALLOCHANDLE ].can_supply = 1;
    }
    if ( !connection -> functions[ DM_SQLFREEHANDLE ].func &&
            connection -> functions[ DM_SQLFREEENV ].func &&
            connection -> functions[ DM_SQLFREECONNECT ].func &&
            connection -> functions[ DM_SQLFREEHANDLE ].func )
    {
        connection -> functions[ DM_SQLFREEHANDLE ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLBINDPARAM ].func &&
                        connection -> functions[ DM_SQLBINDPARAMETER ].func )
    {
        connection -> functions[ DM_SQLBINDPARAM ].can_supply = 1;
    }
    else if ( !connection -> functions[ DM_SQLBINDPARAMETER ].func &&
                        connection -> functions[ DM_SQLBINDPARAM ].func )
    {
        connection -> functions[ DM_SQLBINDPARAMETER ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLGETCONNECTOPTION ].func &&
                        connection -> functions[ DM_SQLGETCONNECTATTR ].func )
    {
        connection -> functions[ DM_SQLGETCONNECTOPTION ].can_supply = 1;
    }
    else if ( !connection -> functions[ DM_SQLGETCONNECTATTR ].func &&
                        connection -> functions[ DM_SQLGETCONNECTOPTION ].func )
    {
        connection -> functions[ DM_SQLGETCONNECTATTR ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLGETSTMTOPTION ].func &&
                        connection -> functions[ DM_SQLGETSTMTATTR ].func )
    {
        connection -> functions[ DM_SQLGETSTMTOPTION ].can_supply = 1;
    }
    else if ( !connection -> functions[ DM_SQLGETSTMTATTR ].func &&
                        connection -> functions[ DM_SQLGETSTMTOPTION ].func )
    {
        connection -> functions[ DM_SQLGETSTMTATTR ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLPARAMOPTIONS ].func &&
                        connection -> functions[ DM_SQLSETSTMTATTR ].func )
    {
        connection -> functions[ DM_SQLPARAMOPTIONS ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLSETCONNECTOPTION ].func &&
                        connection -> functions[ DM_SQLSETCONNECTATTR ].func )
    {
        connection -> functions[ DM_SQLSETCONNECTOPTION ].can_supply = 1;
    }
    else if ( !connection -> functions[ DM_SQLSETCONNECTATTR ].func &&
                        connection -> functions[ DM_SQLSETCONNECTOPTION ].func )
    {
        connection -> functions[ DM_SQLSETCONNECTATTR ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLSETPARAM ].func &&
                        connection -> functions[ DM_SQLBINDPARAMETER ].func )
    {
        connection -> functions[ DM_SQLSETPARAM ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLSETSCROLLOPTIONS ].func &&
                        connection -> functions[ DM_SQLSETSTMTATTR ].func )
    {
        connection -> functions[ DM_SQLSETSCROLLOPTIONS ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLSETSTMTOPTION ].func &&
                        connection -> functions[ DM_SQLSETSTMTATTR ].func )
    {
        connection -> functions[ DM_SQLSETSTMTOPTION ].can_supply = 1;
    }
    else if ( !connection -> functions[ DM_SQLSETSTMTATTR ].func &&
                        connection -> functions[ DM_SQLSETSTMTOPTION ].func )
    {
        connection -> functions[ DM_SQLSETSTMTATTR ].can_supply = 1;
    }

    if ( !connection -> functions[ DM_SQLTRANSACT ].func &&
                        connection -> functions[ DM_SQLENDTRAN ].func )
    {
        connection -> functions[ DM_SQLTRANSACT ].can_supply = 1;
    }
    else if ( !connection -> functions[ DM_SQLENDTRAN ].func &&
                        connection -> functions[ DM_SQLTRANSACT ].func )
    {
        connection -> functions[ DM_SQLENDTRAN ].can_supply = 1;
    }

    /*
     * TO_DO get some driver settings, such as the GETDATA_EXTENSTION
     * it supports
     */

    /*
     * see if it's a version 3 driver so we can do descriptor
     * stuff.
     */

    connection -> driver_act_ver = 0;
    if ( CHECK_SQLGETINFO( connection ))
    {
        char txt[ 20 ];
        SQLRETURN ret;

        ret = SQLGETINFO( connection,
                    connection -> driver_dbc,
                    SQL_DRIVER_ODBC_VER,
                    txt,
                    sizeof( txt ),
                    NULL );

        if ( SQL_SUCCEEDED( ret ))
        {
            connection -> driver_act_ver = atoi( txt );
        }
    }

    /*
     * TO_DO now we should pass any SQLSetEnvAttr settings
     */

    /*
     * now we have a connection handle, and we can check to see if
     * we need to use the cursor library
     */

    if ( connection -> cursors == SQL_CUR_USE_ODBC )
    {
        use_cursor = 1;
    }
    else if ( connection -> cursors == SQL_CUR_USE_IF_NEEDED )
    {
        /*
         * get scrollable info
         */

        if ( !CHECK_SQLGETINFO( connection ))
        {
            /*
             * bit of a retarded driver, better give up
             */
            use_cursor = 0;
        }
        else
        {
            SQLRETURN ret;
            SQLUINTEGER val;

            /*
             * check if static cursors support scrolling
             */

            if ( connection -> driver_act_ver ==
                    SQL_OV_ODBC3 )
            {
                ret = SQLGETINFO( connection,
                        connection -> driver_dbc,
                        SQL_STATIC_CURSOR_ATTRIBUTES1,
                        &val,
                        NULL,
                        NULL );

                if ( ret != SQL_SUCCESS )
                {
                    use_cursor = 1;
                }
                else
                {
                    /*
                     * do we need it ?
                     */
                    if ( !( val & SQL_FD_FETCH_PRIOR )) 
                    {
                        use_cursor = 1;
                    }
                    else
                    {
                        use_cursor = 0;
                    }
                }
            }
            else
            {
                ret = SQLGETINFO( connection,
                        connection -> driver_dbc,
                        SQL_FETCH_DIRECTION,
                        &val,
                        NULL,
                        NULL );

                if ( ret != SQL_SUCCESS )
                {
                    use_cursor = 1;
                }
                else
                {
                    /*
                     * are we needed
                     */

                    if ( !( val & SQL_FD_FETCH_PRIOR )) 
                    {
                        use_cursor = 1;
                    }
                    else 
                    {
                        use_cursor = 0;
                    }
                }
            }
        }
    }
    else
    {
        use_cursor = 0;
    }

    /*
     * if required connect to the cursor lib
     */

    if ( use_cursor )
    {
        int (*cl_connect)(void*);

        if ( !(connection -> cl_handle = lt_dlopen( CURSOR_LIB )))
        {
            char txt[ 256 ];

            sprintf( txt, "Can't open cursor lib '%s' : %s", 
                CURSOR_LIB, lt_dlerror());

            dm_log_write( connection -> log_handle,
                    __get_pid( s0 ),
                    __FILE__,
                    __LINE__,
                    LOG_INFO,
                    LOG_INFO,
                    txt  );

            __post_internal_error( &connection -> error,
                    ERROR_01000, txt,
                    connection -> environment -> requested_version );

            return 0;
        }

        if ( !( cl_connect = (int(*)(void*))lt_dlsym( connection -> cl_handle,
                        "CLConnect" )))
        {
            dm_log_write( connection -> log_handle,
                    __get_pid( s0 ),
                    __FILE__,
                    __LINE__,
                    LOG_INFO,
                    LOG_INFO,
                    "Error: 01000 Unable to load Cursor Lib" );

            __post_internal_error( &connection -> error,
                    ERROR_01000, "Unable to load cursor library",
                    connection -> environment -> requested_version );

            lt_dlclose( connection -> cl_handle );
            connection -> cl_handle = NULL;

            return 0;
        }
        if ( cl_connect( connection ) != SQL_SUCCESS )
        {
            lt_dlclose( connection -> cl_handle );
            connection -> cl_handle = NULL;
            return 0;
        }
    }
    else
    {
        connection -> cl_handle = NULL;
    }

    return 1;
}

/*
 * clean up after the first part of the connect
 */

void __disconnect_part_one( DMHDBC connection )
{
    int ret;

    /*
     * try a version 3 disconnect first on the connection
     */
    if ( connection -> driver_dbc )
    {
        if ( connection -> driver_version == SQL_OV_ODBC3 )
        {
            if ( CHECK_SQLFREEHANDLE( connection ))
            {
                ret = SQLFREEHANDLE( connection,
                        SQL_HANDLE_DBC,
                        connection -> driver_dbc );

                if ( !ret )
                    connection -> driver_dbc = (SQLHANDLE)NULL;
            }
        }

        /*
         * is it still open ?
         */

        if (  connection -> driver_dbc &&
                CHECK_SQLFREECONNECT( connection ))
        {
            SQLFREECONNECT( connection,
                    connection -> driver_dbc );
        }
        else
        {
            /*
             * not a lot we can do here
             */
        }
    }
    connection -> driver_dbc = (SQLHANDLE)NULL;

    /*
     * now disconnect the environment
     */

    if ( connection -> driver_env )
    {
        if ( connection -> driver_version == SQL_OV_ODBC3 )
        {
            if ( CHECK_SQLFREEHANDLE( connection ))
            {
                ret = SQLFREEHANDLE( connection,
                        SQL_HANDLE_ENV,
                        connection -> driver_env );

                if ( !ret )
                    connection -> driver_env = (SQLHANDLE)NULL;
            }
        }

        /*
         * is it still open ?
         */

        if ( CHECK_SQLFREEENV( connection ))
        {
            SQLFREEENV( connection,
                    connection -> driver_env );
        }
        else
        {
            /*
             * not a lot we can do here
             */
        }
    }
    connection -> driver_env = (SQLHANDLE)NULL;

    /*
     * unload the lib
     */

    if ( connection -> cl_handle )
    {
        lt_dlclose( connection -> cl_handle );
        connection -> cl_handle = NULL;
    }

    if ( connection -> dl_handle )
    {
        lt_dlclose( connection -> dl_handle );
        connection -> dl_handle = NULL;
    }

    /*
     * free some memory
     */

    if ( connection -> functions )
    {
        free( connection -> functions );
        connection -> functions = NULL;
    }
}

void __disconnect_part_two( DMHDBC connection )
{
    if ( CHECK_SQLDISCONNECT( connection ))
    {
        SQLDISCONNECT( connection,
                connection -> driver_dbc );
    }
}

/*
 * interface for SQLGetFunctions
 */

void  __check_for_function( DMHDBC connection,
        SQLUSMALLINT function_id,
        SQLUSMALLINT *supported )
{
    int i;

    if ( function_id == SQL_API_ODBC3_ALL_FUNCTIONS )
    {
        for ( i = 0; i < SQL_API_ODBC3_ALL_FUNCTIONS_SIZE; i ++ )
        {
            supported[ i ] = 0x0000;
        }
        for ( i = 0; i < sizeof( template_func ) / sizeof( template_func[ 0 ] ); i ++ )
        {
        int id = connection -> functions[ i ].ordinal;

            if ( connection -> functions[ i ].can_supply )
                supported[ id >> 4 ] |= ( 1 << ( id & 0x000F ));
        }
    }
    else if ( function_id == SQL_API_ALL_FUNCTIONS )
    {
        for ( i = 0; i < 100; i ++ )
        {
            supported[ i ] = SQL_FALSE;
        }
        for ( i = 0; i < sizeof( template_func ) / sizeof( template_func[ 0 ] ); i ++ )
        {
            if ( connection -> functions[ i ].ordinal < 100 )
            {
                if ( connection -> functions[ i ].can_supply )
                    supported[ connection -> functions[ i ].ordinal ] =
                        SQL_TRUE;
            }
        }
    }
    else
    {
        *supported = SQL_FALSE;
        for ( i = 0; i < sizeof( template_func ) / sizeof( template_func[ 0 ] ); i ++ )
        {
            if ( connection->functions[ i ].ordinal == function_id )
            {
                if ( connection -> functions[ i ].can_supply )
                    *supported = SQL_TRUE;
                break;
            }
        }
    }
}

SQLRETURN SQLConnect( SQLHDBC connection_handle,
           SQLCHAR *server_name,
           SQLSMALLINT name_length1,
           SQLCHAR *user_name,
           SQLSMALLINT name_length2,
           SQLCHAR *authentication,
           SQLSMALLINT name_length3 )
{
    DMHDBC connection = (DMHDBC)connection_handle;
    int len, ret_from_connect;
    char dsn[ SQL_MAX_DSN_LENGTH + 1 ];
    char lib_name[ INI_MAX_PROPERTY_VALUE + 1 ];
    char driver_name[ INI_MAX_PROPERTY_VALUE + 1 ];
    SQLCHAR s0[ 20 ], s1[ 100 ], s2[ 100 ], s3[ 100 ];

    /*
     * check connection
     */
    if ( !__validate_dbc( connection ))
    {
        return SQL_INVALID_HANDLE;
    }

    function_entry( connection );

    if ( connection -> log_handle )
    {
        sprintf( connection -> msg, "\n\t\tEntry:\
            \n\t\t\tConnection = %p\
            \n\t\t\tServer Name = %s\
            \n\t\t\tUser Name = %s\
            \n\t\t\tAuthentication = %s",
                connection,
                __string_with_length( s1, server_name, name_length1 ),
                __string_with_length( s2, user_name, name_length1 ),
                __string_with_length( s3, authentication, name_length1 ));

        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                connection -> msg );
    }

    thread_protect( SQL_HANDLE_DBC, connection );

    if (( name_length1 < 0 && name_length1 != SQL_NTS ) ||
        ( name_length2 < 0 && name_length2 != SQL_NTS ) ||
        ( name_length3 < 0 && name_length3 != SQL_NTS ))

    {
        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                "Error: HY090" );

        __post_internal_error( &connection -> error,
                ERROR_HY090, NULL,
                connection -> environment -> requested_version );

        thread_release( SQL_HANDLE_DBC, connection );

        return function_return( connection, SQL_ERROR );
    }

    /*
     * check the state of the connection
     */
    if ( connection -> state != STATE_C2 )
    {
        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                "Error: 08002" );

        __post_internal_error( &connection -> error,
                ERROR_08002, NULL,
                connection -> environment -> requested_version );

        thread_release( SQL_HANDLE_DBC, connection );

        return function_return( connection, SQL_ERROR );
    }

    if ( name_length1 )
    {
        if ( name_length1 == SQL_NTS )
            len = strlen( server_name );
        else
            len = name_length1;

        if ( len > SQL_MAX_DSN_LENGTH )
        {
            dm_log_write( connection -> log_handle,
                    __get_pid( s0 ),
                    __FILE__,
                    __LINE__,
                    LOG_INFO,
                    LOG_INFO,
                    "Error: IM010" );

            __post_internal_error( &connection -> error,
                    ERROR_IM010, NULL,
                    connection -> environment -> requested_version );

            thread_release( SQL_HANDLE_DBC, connection );

            return function_return( connection, SQL_ERROR );
        }

        memcpy( dsn, server_name, len );
        dsn[ len ] ='\0';
    }
    else
    {
        strcpy( dsn, "DEFAULT" );
    }

    if ( !__find_lib_name( dsn, lib_name, driver_name ))
    {
        /*
         * if not found look for a default
         */

        if ( !__find_lib_name( "DEFAULT", lib_name, driver_name ))
        {
            dm_log_write( connection -> log_handle,
                    __get_pid( s0 ),
                    __FILE__,
                    __LINE__,
                    LOG_INFO,
                    LOG_INFO,
                    "Error: IM002" );

            __post_internal_error( &connection -> error,
                    ERROR_IM002, NULL,
                    connection -> environment -> requested_version );

            thread_release( SQL_HANDLE_DBC, connection );

            return function_return( connection, SQL_ERROR );
        }
    }

    /*
     * if necessary change the threading level
     */

    if ( !__connect_part_one( connection, lib_name, driver_name ))
    {
        thread_release( SQL_HANDLE_DBC, connection );
        return function_return( connection, SQL_ERROR );
    }

    if ( !CHECK_SQLCONNECT( connection ))
    {
        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                "Error: IM001" );

        __disconnect_part_one( connection );
        __post_internal_error( &connection -> error,
                ERROR_IM001, NULL,
                connection -> environment -> requested_version );

        thread_release( SQL_HANDLE_DBC, connection );

        return function_return( connection, SQL_ERROR );
    }

    ret_from_connect = SQLCONNECT( connection,
            connection -> driver_dbc,
            dsn, SQL_NTS,
            user_name, name_length2,
            authentication, name_length3 );

    if ( !SQL_SUCCEEDED( ret_from_connect ))
    {
        SQLCHAR sqlstate[ 6 ];
        SQLINTEGER native_error, ind;
        SQLCHAR message_text[ SQL_MAX_MESSAGE_LENGTH + 1 ];
        SQLRETURN ret;

        /*
         * get the errors from the driver before
         * loseing the connection
         */

        do
        {
            ret = SQLERROR( connection,
                    SQL_NULL_HENV,
                    connection -> driver_dbc,
                    SQL_NULL_HSTMT,
                    sqlstate,
                    &native_error,
                    message_text,
                    sizeof( message_text ),
                    &ind );

            if ( SQL_SUCCEEDED( ret ))
            {
                __post_internal_error_ex( &connection -> error,
                        sqlstate,
                        native_error,
                        message_text );
            }

            sprintf( connection -> msg,
                    "\n\t\tExit:[%s]",
                        __get_return_status( ret_from_connect ));

            dm_log_write( connection -> log_handle,
                    __get_pid( s0 ),
                    __FILE__,
                    __LINE__,
                    LOG_INFO,
                    LOG_INFO,
                    connection -> msg );
        }
        while( SQL_SUCCEEDED( ret ));

        __disconnect_part_one( connection );

        thread_release( SQL_HANDLE_DBC, connection );

        return function_return( connection, ret_from_connect );
    }

    /*
     * we should be connected now
     */
    connection -> state = STATE_C4;
    strcpy( connection -> dsn, dsn );

    /*
     * did we get the type we wanted
     */
    if ( connection -> driver_version !=
            connection -> environment -> requested_version )
    {
        connection -> driver_version =
            connection -> environment -> requested_version;

        __post_internal_error( &connection -> error,
                ERROR_01000, "Driver does not support the requested version",
                connection -> environment -> requested_version );
        ret_from_connect = SQL_SUCCESS_WITH_INFO;
    }

    if ( !__connect_part_two( connection ))
    {
        __disconnect_part_two( connection );
        __disconnect_part_one( connection );

        thread_release( SQL_HANDLE_DBC, connection );

        return function_return( connection, SQL_ERROR );
    }

    if ( connection -> log_handle )
    {
        sprintf( connection -> msg,
                "\n\t\tExit:[%s]",
                    __get_return_status( ret_from_connect ));

        dm_log_write( connection -> log_handle,
                __get_pid( s0 ),
                __FILE__,
                __LINE__,
                LOG_INFO,
                LOG_INFO,
                connection -> msg );
    }

    thread_release( SQL_HANDLE_DBC, connection );

    return function_return( connection, ret_from_connect );
}
