Ingres and OpenAPI

sabato 31 Maggio 2008
I’d the (nsg, IMHO) opportunity to work with the Ingres Database from a C++ program. Ingres LOGO Since I don’t like SQL as C embedded, I choose to use the OpenAPI Libraries that comes from the Ingres Distribution on my Etch. They sell the OpenAPI as an “alternative to ODBC”. They are database aware. I’ve to test this. I’ve only used OpenAPI to access a remote Ingres DB. In order to have things working, first of all you’ve to load the environment, located in
# to have some logs of the OpenAPI
export IIAPI_TRACE=1
export IIAPI_LOG=/tmp/massi.log
#env
. /opt/Ingres/IngresII/.ingIIsh
Then you’ve to go to use the netutil program to add a so called Vnode (a name like cochise::massi, in Ingres terminology) that is an alias for the database/host. On the other part, each OpenAPI function are equals: they need a structure before the function call (called parameter block) that you’ve to fill with all the parameters. It makes an huge use of functions pointers as Callbacks. My working pre is:

/* doInsertIngres()
 * 
 * Vera e propria implementazione della insert in Ingres()
 */


void
LulliServer::doInsertIngres(ServerSocket& s, 
     string data, string host, string dbname)
{
 lm.append_msg("In LulliServer::doInsertIngres()",INFO,__FILE__,__LINE__);
	
	char *dbname_char;
	char *vnode_char;
	char *ctx_string;
	char *query_char;
	const string ack="ACK\n";
	
	dbname_char = (char *)malloc(dbname.length());
	sprintf(dbname_char, "%s",dbname.c_str());
	vnode_char = (char *)malloc(host.length());
	sprintf(vnode_char, "%s",host.c_str());
	ctx_string=(char *)malloc(dbname.length()+host.length()+4);
	sprintf(ctx_string, "%s::%s", vnode_char, dbname_char);
	
	string query;
	s << ack;
	s >> query;
	II_PTR		connHandle = (II_PTR)NULL;
    II_PTR		tranHandle = (II_PTR)NULL;
    IIAPI_COMMITPARM    commitParm;
    
//    II_PTR		stmtHandle;
//    IIAPI_QUERYPARM	queryParm;
//    IIAPI_SETDESCRPARM	setDescrParm;
//    IIAPI_PUTPARMPARM	putParmParm;
//    IIAPI_GETQINFOPARM	getQInfoParm; 
//    IIAPI_CLOSEPARM	closeParm;
    IIAPI_WAITPARM	waitParm = { -1 };

    
    IIdemo_init(); 
    IIdemo_conn(ctx_string,&connHandle);   

    /* 
    **  Valid query: no row count 
    */
	query_char = (char *)malloc(query.length());
	sprintf(query_char, "%s",query.c_str());
	fprintf(stderr, "Connecting to %s with sql: %s\n",ctx_string,query_char);
    IIdemo_query(&connHandle, &tranHandle,"doing insert",query_char); // leggi 1-7
	 


    commitParm.cm_genParm.gp_callback = NULL;
    commitParm.cm_genParm.gp_closure = NULL;
    commitParm.cm_tranHandle = tranHandle;

    IIapi_commit( &commitParm );

    while( commitParm.cm_genParm.gp_completed == FALSE )
            IIapi_wait( &waitParm );


    if (dbError == true)
    	IIdemo_rollback(&tranHandle);
    	
    IIdemo_disconn(&connHandle);
    IIdemo_term();

    	
}



/*
** Name: IIdemo_query
**
** Description:
**	Execute SQL statement taking no parameters and returning no rows.
**
** Input:
**	connHandle	Connection handle.
**	tranHandle	Transaction handle.
**	desc		Query description.
**	queryText	SQL query text.
**
** Output:
**	connHandle	Connection handle.
**	tranHandle	Transaction handle.
**
** Return value:
**	None.
*/

void
LulliServer::IIdemo_query( II_PTR *connHandle, II_PTR *tranHandle, 
	      char *desc, char *queryText )
{
	lm.append_msg("In LulliServer::IIdemo_query()",INFO,__FILE__,__LINE__);
    IIAPI_QUERYPARM	queryParm;
    IIAPI_GETQINFOPARM	getQInfoParm;
    IIAPI_CLOSEPARM	closeParm;
    IIAPI_WAITPARM	waitParm = { -1 };

   // printf( "IIdemo_query: %s\n", desc );

    /*
    ** Call IIapi_query to execute statement.
    */
    queryParm.qy_genParm.gp_callback = NULL;
    queryParm.qy_genParm.gp_closure = NULL;
    queryParm.qy_connHandle = *connHandle;
    queryParm.qy_queryType = IIAPI_QT_QUERY;
    queryParm.qy_queryText = queryText;
    queryParm.qy_parameters = FALSE;
    queryParm.qy_tranHandle = *tranHandle;
    queryParm.qy_stmtHandle = NULL;

    IIapi_query( &queryParm );
  
    while( queryParm.qy_genParm.gp_completed == FALSE )
	IIapi_wait( &waitParm );

    if ( queryParm.qy_genParm.gp_status != IIAPI_ST_SUCCESS)
	IIdemo_checkError(& queryParm.qy_genParm);

    /*
    ** Return transaction handle.
    */
    *tranHandle = queryParm.qy_tranHandle;

    /*
    ** Call IIapi_getQueryInfo() to get results.
    */
    getQInfoParm.gq_genParm.gp_callback = NULL;
    getQInfoParm.gq_genParm.gp_closure = NULL;
    getQInfoParm.gq_stmtHandle = queryParm.qy_stmtHandle;

    IIapi_getQueryInfo( &getQInfoParm );

    while( getQInfoParm.gq_genParm.gp_completed == FALSE )
	IIapi_wait( &waitParm );

    if ( getQInfoParm.gq_genParm.gp_status != IIAPI_ST_SUCCESS )
	IIdemo_checkError( &getQInfoParm.gq_genParm );
    else 
	IIdemo_checkQInfo( &getQInfoParm ); 

    /*
    ** Call IIapi_close() to release resources.
    */
    closeParm.cl_genParm.gp_callback = NULL;
    closeParm.cl_genParm.gp_closure = NULL;
    closeParm.cl_stmtHandle = queryParm.qy_stmtHandle;

    IIapi_close( &closeParm );

    while( closeParm.cl_genParm.gp_completed == FALSE )
	IIapi_wait( &waitParm );

    if ( closeParm.cl_genParm.gp_status != IIAPI_ST_SUCCESS )
	IIdemo_checkError(&closeParm.cl_genParm);

    return;
}


/*
** Name: IIdemo_checkError
**
** Description:
**	Check the status of an API function call 
**	and process error information.
**
** Input:
**	genParm		API generic parameters.
**
** Output:
**	None.
**
** Return value:
**	None.
*/

void
LulliServer::IIdemo_checkError( IIAPI_GENPARM        *genParm )
{
	lm.append_msg("In LulliServer::IIdemo_checkError()",INFO,__FILE__,__LINE__);
    IIAPI_GETEINFOPARM	getErrParm; 
    char		type[33];
    char *errorstr;
    char *dberrorstr;
    /*
    ** Check API call status.
    */
    dberrorstr = (char *)calloc(512,1);
    sprintf( dberrorstr, "\tgp_status = %s\n",
               (genParm->gp_status == IIAPI_ST_SUCCESS) ?  
			"IIAPI_ST_SUCCESS" :
               (genParm->gp_status == IIAPI_ST_MESSAGE) ?  
			"IIAPI_ST_MESSAGE" :
               (genParm->gp_status == IIAPI_ST_WARNING) ?  
			"IIAPI_ST_WARNING" :
               (genParm->gp_status == IIAPI_ST_NO_DATA) ?  
			"IIAPI_ST_NO_DATA" :
               (genParm->gp_status == IIAPI_ST_ERROR)   ?  
			"IIAPI_ST_ERROR"   :
               (genParm->gp_status == IIAPI_ST_FAILURE) ? 
			"IIAPI_ST_FAILURE" :
               (genParm->gp_status == IIAPI_ST_NOT_INITIALIZED) ?
			"IIAPI_ST_NOT_INITIALIZED" :
               (genParm->gp_status == IIAPI_ST_INVALID_HANDLE) ?
			"IIAPI_ST_INVALID_HANDLE"  :
               (genParm->gp_status == IIAPI_ST_OUT_OF_MEMORY) ?
			"IIAPI_ST_OUT_OF_MEMORY"   :
              "(unknown status)" );
	lm.append_msg(dberrorstr, ERRO, __FILE__, __LINE__);
	dbStatus = dberrorstr;
    /*
    ** Check for error information.
    */
    if ( ! genParm->gp_errorHandle )  return;
    getErrParm.ge_errorHandle = genParm->gp_errorHandle;

    do
    { 
	/*
	** Invoke API function call.
 	*/
    	IIapi_getErrorInfo( &getErrParm );

 	/*
	** Break out of the loop if no data or failed.
	*/
    	if ( getErrParm.ge_status != IIAPI_ST_SUCCESS )
	    break;

	/*
	** Process result.
	*/
	switch( getErrParm.ge_type )
	{
	    case IIAPI_GE_ERROR	 : 
		strcpy( type, "ERROR" ); 	break;

	    case IIAPI_GE_WARNING :
		strcpy( type, "WARNING" ); 	break;

	    case IIAPI_GE_MESSAGE :
		strcpy(type, "USER MESSAGE");	break;

	    default:
		sprintf( type, "unknown error type: %d", getErrParm.ge_type);
		break;
	}

	errorstr = (char *)calloc(512,1);
	sprintf( errorstr, "\tError Info: %s '%s' 0x%x: %s\n",
		   type, getErrParm.ge_SQLSTATE, getErrParm.ge_errorPre,
		   getErrParm.ge_message ? getErrParm.ge_message : "NULL" );
	lm.append_msg(errorstr,ERRO,__FILE__,__LINE__);
	
	dbError=true;
	
    } while( 1 );

    return;
}


/*
** Name: IIdemo_checkQInfo
**
** Description:
**	Processes the information returned by IIapi_getQInfo().
**
** Input:
**	getQInfoParm	Parameter block from IIapi_getQInfo().
**
** Output:
**	None.
**
** Return value:
**	None.
*/

void
LulliServer::IIdemo_checkQInfo( IIAPI_GETQINFOPARM *getQInfoParm )
{
	lm.append_msg("In LulliServer::IIdemo_checkQInfo()",INFO,__FILE__,__LINE__);
    /*
    ** Check query result flags.
    */
    if ( getQInfoParm->gq_flags & IIAPI_GQF_FAIL )
        printf( "\tflag = IIAPI_GQF_FAIL\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_ALL_UPDATED )
        printf( "\tflag = IIAPI_GQF_ALL_UPDATED\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_NULLS_REMOVED )
        printf( "\tflag = IIAPI_GQF_NULLS_REMOVED\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_UNKNOWN_REPEAT_QUERY )
        printf( "\tflag = IIAPI_GQF_UNKNOWN_REPEAT_QUERY\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_END_OF_DATA )
        printf( "\tflag = IIAPI_GQF_END_OF_DATA\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_CONTINUE )
        printf( "\tflag = IIAPI_GQF_CONTINUE\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_INVALID_STATEMENT )
        printf( "\tflag = IIAPI_GQF_INVALID_STATEMENT\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_TRANSACTION_INACTIVE )
        printf( "\tflag = IIAPI_GQF_TRANSACTION_INACTIVE\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_OBJECT_KEY )
        printf( "\tflag = IIAPI_GQF_OBJECT_KEY\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_TABLE_KEY )
        printf( "\tflag = IIAPI_GQF_TABLE_KEY\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_NEW_EFFECTIVE_USER )
        printf( "\tflag = IIAPI_GQF_NEW_EFFECTIVE_USER\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_FLUSH_QUERY_ID )
        printf( "\tflag = IIAPI_GQF_FLUSH_QUERY_ID\n" );

    if ( getQInfoParm->gq_flags & IIAPI_GQF_ILLEGAL_XACT_STMT )
        printf( "\tflag = IIAPI_GQF_ILLEGAL_XACT_STMT\n" );

    /*
    ** Check query result values.
    */
    if ( getQInfoParm->gq_mask & IIAPI_GQ_ROW_COUNT )
        printf( "\trow count = %d\n", getQInfoParm->gq_rowCount );

    if ( getQInfoParm->gq_mask & IIAPI_GQ_CURSOR )
        printf( "\treadonly = TRUE\n" );

    if ( getQInfoParm->gq_mask & IIAPI_GQ_PROCEDURE_RET )
        printf( "\tprocedure return = %d\n", getQInfoParm->gq_procedureReturn );

    if ( getQInfoParm->gq_mask & IIAPI_GQ_PROCEDURE_ID )
        printf("\tprocedure handle = 0x%x\n",(int)getQInfoParm->gq_procedureHandle);

    if ( getQInfoParm->gq_mask & IIAPI_GQ_REPEAT_QUERY_ID )
        printf("\trepeat query ID = 0x%x\n",(int)getQInfoParm->gq_repeatQueryHandle);

    if ( getQInfoParm->gq_mask & IIAPI_GQ_TABLE_KEY )
        printf( "\tReceived table key\n" );

    if ( getQInfoParm->gq_mask & IIAPI_GQ_OBJECT_KEY )
        printf( "\treceived object key\n" );

    return;
}


/*
** Name: IIdemo_init
**
** Description:
**	Initialize API access.
**
** Input:
**	None.
**
** Output:
**	None.
**
** Return value:
**	None.
*/

void
LulliServer::IIdemo_init()
{
	lm.append_msg("In LulliServer::IIdemo_init()",INFO,__FILE__,__LINE__);
    IIAPI_INITPARM  initParm;

    //printf( "IIdemo_init: initializing API\n" );
    initParm.in_version = IIAPI_VERSION_1; 
    initParm.in_timeout = -1;
    IIapi_initialize( &initParm );

    return;
}


/*
** Name: IIdemo_term
**
** Description:
**	Terminate API access.
**
** Input:
**	None.
**
** Output:
**	None.
**
** Return value:
**	None.
*/

void
LulliServer::IIdemo_term()
{
	lm.append_msg("In LulliServer::IIdemo_term()",INFO,__FILE__,__LINE__);
    IIAPI_TERMPARM  termParm;

    //printf( "IIdemo_term: shutting down API\n" );
    IIapi_terminate( &termParm );

    return;
}


/*
** Name: IIdemo_conn
**
** Description:
**	Open connection with target Database.
**
** Input:
**	dbname		Database name.
**
** Output:
**	connHandle	Connection handle.
**
** Return value:
**	None.
*/
    
void
LulliServer::IIdemo_conn( char *dbname, II_PTR *connHandle )
{
	lm.append_msg("In LulliServer::IIdemo_conn()",INFO,__FILE__,__LINE__);
    IIAPI_CONNPARM	connParm;
    IIAPI_WAITPARM	waitParm = { -1 };
    
    //printf( "IIdemo_conn: establishing connection\n" );
    
    connParm.co_genParm.gp_callback = NULL;
    connParm.co_genParm.gp_closure = NULL;
    connParm.co_target =  dbname;
    connParm.co_connHandle = NULL;
    connParm.co_tranHandle = NULL;
    connParm.co_username = NULL;
    connParm.co_password = NULL;
    connParm.co_timeout = -1;

    IIapi_connect( &connParm );
    
    while( connParm.co_genParm.gp_completed == FALSE )
	IIapi_wait( &waitParm );

    *connHandle = connParm.co_connHandle;
    return;
}


/*
** Name: IIdemo_disconn
**
** Description:
**	Release DBMS connection.
**
** Input:
**	connHandle	Connection handle.
**
** Output:
**	connHandle	Connection handle.
**
** Return value:
**	None.
*/
    
void
LulliServer::IIdemo_disconn( II_PTR *connHandle )
{
	lm.append_msg("In LulliServer::IIdemo_disconn()",INFO,__FILE__,__LINE__);
    IIAPI_DISCONNPARM	disconnParm;
    IIAPI_WAITPARM	waitParm = { -1 };
    
    //printf( "IIdemo_disconn: releasing connection\n" );
    
    disconnParm.dc_genParm.gp_callback = NULL;
    disconnParm.dc_genParm.gp_closure = NULL;
    disconnParm.dc_connHandle = *connHandle;
    
    IIapi_disconnect( &disconnParm );
    
    while( disconnParm.dc_genParm.gp_completed == FALSE )
	IIapi_wait( &waitParm );
    
    *connHandle = NULL;
    return;
}


/*
** Name: IIdemo_rollback
**
** Description:
**	Invokes IIapi_rollback() to rollback current transaction
**	and resets the transaction handle.
**
** Input:
**	tranHandle	Handle of transaction.
**
** Output:
**	tranHandle	Updated handle.
**
** Return value:
**      None.
*/
	
void
LulliServer::IIdemo_rollback( II_PTR *tranHandle )
{
	lm.append_msg("In LulliServer::IIdemo_rollback()",INFO,__FILE__,__LINE__);
    IIAPI_ROLLBACKPARM	rollbackParm;
    IIAPI_WAITPARM	waitParm = { -1 };

    //printf( "IIdemo_rollback: rolling back transaction\n" );

    rollbackParm.rb_genParm.gp_callback = NULL;
    rollbackParm.rb_genParm.gp_closure = NULL;
    rollbackParm.rb_tranHandle = *tranHandle;
    rollbackParm.rb_savePointHandle = NULL; 

    IIapi_rollback( &rollbackParm );

    while( rollbackParm.rb_genParm.gp_completed == FALSE )
	IIapi_wait( &waitParm );

    *tranHandle = NULL;
    return;
}
It is taken from the demo API in the Ingres directory.

How to use the dlopen() with AIX

sabato 31 Maggio 2008
The dlopen(3) is a very famous and interesting function. Its behavior is well-known. Now I would like to have a C program able to load shared objects. I have:
[max@rdev04:/home/max]# uname -a
AIX rdev04 3 4 0041666C4C00
This is the main source file:
// prova.c
#include
#include
#include

int main(int argc, char *argv[])
{
  void *handle;
  char path[1024];
  void (*stampa)(void);
  
  if (argc<2)
    {
      fprintf(stderr, "Usage: %s \n",argv[0]);
      return -1;
    }
  
  sprintf(path, "%s/%s",getcwd(path, sizeof(path)),argv[1]);

  
  fprintf(stderr, "Loading %s\n",path);

  handle = dlopen(path, RTLD_LAZY);

  if (!handle)
    {
      fprintf(stderr, "%s",dlerror());
      return -1;
    }

  stampa=dlsym(handle,"stampa");

  if (!stampa)
    {
      fprintf(stderr, "%s\n",dlerror());
      return -1;
    }

  stampa();
  return 0;
  
}
That loads the first shared object
#include
// shared1.c
void
stampa(void)
{
  fprintf(stderr, "First of all\n");
}
The second object file is made of two file: a file that prints something and the secondo that is compiled as object. The first:
#include
//shared2.c
extern void ciaiccio(void);

void
stampa(void)
{
  fprintf(stderr,"I'm in stampa\n");
  ciaiccio();
}
and the second:
#include
//static1.c
void
ciaiccio(void)
{
    fprintf(stderr, "I'm in ciaiaccio\n");
}

The Make operation is interesting: first you’ve to compile the main program, telling that libraries are loaded run time:
cc -brtl prova.c -o prova
Then we compile the first shared1.c directly to the shared object:
cc -G shared1.c -o shared1.so
The we compile the static1 and shared2 as follows:
cc -c static1.c -o static1.o
cc -c shared2.c -o shared2.o
The we compile the $(OBJS)=static1, shared2 as
cc -G $(OBJ) -o libshared2.so
That’s it! This is the output:
[max@rdev04:/dvl1/gen/source/BDEX/MassiTEST]# ./prova shared1.so
Loading /dvl1/gen/source/BDEX/MassiTEST/shared1.so
First of all
[max@rdev04:/dvl1/gen/source/BDEX/MassiTEST]# ./prova libshared2.so
Loading /dvl1/gen/source/BDEX/MassiTEST/libshared2.so
I'm in stampa
m in ciaiaccio

Who Am I?

Image of massi with Chicago Bulls

Something about me?Data follows ...

Arguments