head	1.1;
access;
symbols;
locks; strict;
comment	@ * @;


1.1
date	94.10.17.20.52.35;	author jolly;	state Exp;
branches;
next	;


desc
@@


1.1
log
@Initial revision
@
text
@/* ************************************************************************
 *
 * tdb.h -- providing a common interface for Tioga to access different kind
 *          of database systems.
 *
 *
 * ************************************************************************/

#include "tdb.h"
#include "stringutils.h"

#define TG_INITIAL_ROWS 32
char tgDbErrMsg[TG_DB_ERR_MSG_LEN];

#ifdef TDB_MI
char beginXact[] = "begin transaction;";
char endXact[] = "end transaction;";
char abortXact[] = "abort transaction;";

/**  XXX : hardwired to connect w/ user 'tioga' and password 'demo' ***/
TgDb *TgNewDB(char *name, char *host, char *port, char *login, char *password)
/*
 * TgNewDB -- Initialize a TgDb structure to contain information about the
 *            database that you want to connect to.
 */
{
  TgDb *result;

  result = (TgDb *) malloc(sizeof(TgDb));

  result->cinfo.server_name = NULL;
  if (host == NULL || *host == '\0') {
    result->cinfo.server_host = "localhost";
  } else {
    result->cinfo.server_host = dupstr(host);
  }

  if (port == NULL || *port == '\0') {
    result->cinfo.server_port = 7531;
  } else {
    result->cinfo.server_port = atoi(port);
  }

  result->dinfo.database_name = dupstr(name);
  if (login == NULL || *login == '\0') {
    result->dinfo.user_name = "tioga";  
    result->dinfo.password = "demo";
  } else {
    result->dinfo.user_name = dupstr(login);
    result->dinfo.password = dupstr(password);
  }
#if 0
  if (login == NULL || *login == '\0') {
    result->dinfo.user_name = dupstr(getenv("USER"));
  } else {
    result->dinfo.user_name = dupstr(login);
  }
  
  result->dinfo.password = dupstr(password);
#endif /* if 0 */
  return result;
}

void TgFreeDb(TgDb *db)
/*
 * TgFreeDb -- free the db structure.  If a connection is still present when
 *             this routine is called, the connection will be closed.
 *             It would be better for the user to call the macro TG_FREEDB
 *             instead of calling this function directly.
 */
{
  if (db == NULL)
    return;

  if (db->conn) {
    TgCloseDB(db);
  }
  TG_FREE(free, db->cinfo.server_name);
  TG_FREE(free, db->cinfo.server_host);
  TG_FREE(free, db->dinfo.database_name);
  TG_FREE(free, db->dinfo.user_name);
  TG_FREE(free, db->dinfo.password);
}

int TgConnectDB(TgDb *db)
/*
 * TgConnectDB -- establish a connection with the database backend.
 *
 *                Returns:
 *                          0  -- connection to server failed
 *                          1  -- connection to server succeeded, no errors
 */
{
  db->conn = mi_server_connect(&(db->cinfo));
  if (db->conn == NULL) {
    sprintf(tgDbErrMsg, "Connection with \"%s\" on port %d failed",
	    db->cinfo.server_host, db->cinfo.server_port);
    return 0;
  }
  if (mi_login(db->conn, &(db->dinfo)) == MI_ERROR) {
    sprintf(tgDbErrMsg, "Unable to login to database \"%s\" with user \
name \"%s\"", db->dinfo.database_name, db->dinfo.user_name);
    return 0;
  }
  return 1;
}

void TgCloseDB(TgDb *db)
/*
 * TgCloseDB -- close the connection with the database server.
 */
{
  if (db->conn) {
    mi_close(db->conn);
    db->conn = NULL;
  }
}

TgTupleGroup *TgRetrieve(TgDb *db, char *query, int binary)
/*
 * TgRetrieve -- Do a retrieve query to the database and returns the result
 *               in a TgTupleGroup.  The result can be in either binary or
 *               ASCII form.
 *
 *               Returns NULL if an error has occured with the error message
 *               stored in 'tgDbErrMsg'
 */
{
  TgTupleGroup *result;
  if (TgDoQuery(db, query, binary) == 2) {
    result = TgGetData(db, binary);
  } else {
    result = NULL;
  }
  while (TgDoQuery(db, NULL, 0) != 1) ;
  return result;
}

TgTupleGroup *TgGetData(TgDb *db, int binary)
/*
 * TgGetData -- get data from a retrieve query.  Call this function only
 *              immediately after TgDoQuery() returns 2.
 */
{
  MI_CONNECTION *conn = db->conn;
  MI_ROW        *row;
  MI_ROW_DESC   *rowDesc;
  int           error, numCols, i, colLen, numRows, err;
  char          *colName, *colVal;
  TgTupleGroup  *result;
  TgTypeBlock   *type;
  TgAttValue    value;
  TgTuple       tuple;
  
  if (conn == NULL)
    return NULL;

  rowDesc = mi_get_row_desc_without_row(conn);
  if (rowDesc == NULL)
    return NULL;
  numCols = mi_column_count(rowDesc);
  if (numCols == 0)
    return NULL;
  result = (TgTupleGroup *) malloc(sizeof(TgTupleGroup));
  result->numAttributes = numCols;
  result->type = binary;
  result->attribute = (TgTypeBlock *) malloc(sizeof(TgTypeBlock) * numCols);
  for (i = 0 ; i < numCols ; i++) {
    type = &(result->attribute[i]);
    type->name = dupstr(mi_column_name(rowDesc, i));
    type->adtid = atoi(mi_column_type_id(rowDesc, i));
    type->adtsize = mi_column_type_length(rowDesc, i);
  }

  numRows = TG_INITIAL_ROWS;

  if (numRows == 0) {
    result->numTuples = 0;    
    result->tuple = NULL;
    return result;
  }
  result->tuple = (TgTuple *) malloc(sizeof(TgTuple) * numRows);
  result->numTuples = 0;
  err = 0;
  while (!err && ((row = mi_next_row(conn, &error)) != NULL)) {
    if (result->numTuples >= numRows) {
      numRows += numRows/2;
      result->tuple = (TgTuple *) realloc(result->tuple,
					  sizeof(TgTuple) * numRows);
    }
    tuple = (TgTuple) malloc(sizeof(TgAttValue) * numCols);
    result->tuple[result->numTuples] = tuple;
    for (i = 0 ; i < numCols ; i++) {
      switch (mi_value(row, i, &colVal, &colLen)) {
      case MI_ERROR:
	sprintf(tgDbErrMsg, "can not get value");
	err = 1;
	return NULL;
      case MI_NULL_VALUE:
	value = NULL;
	break;
      case MI_NORMAL_VALUE:
	if (binary) {
	  value = (TgAttValue) malloc(colLen);
	  memcpy(value, colVal, colLen);
	} else {
	  value = (TgAttValue) malloc(colLen+1);
	  memcpy(value, colVal, colLen);
	  ((char *) value)[colLen] = '\0';
	}
	break;
      case MI_ROW_VALUE:
	value = (TgAttValue) malloc(12);
	strcpy(value, "<composite>");
	break;
      default:
	sprintf(tgDbErrMsg, "Unknown value type returned");
	err = 1;
	break;
      }
      tuple[i] = value;
    }
    result->numTuples++;
  }
  if (err) {
    TgFreeTupleGroup(result);
  }
  while (TgDoQuery(db, NULL, binary) != 1);
  return result;
}

int TgDoQuery(TgDb *db, char *query, int binary)
/*
 * TgDoQuery -- issue an arbituary query to the database back end.
 *              If a NULL is passed in as the query, this function checks
 *              to make sure that all processing that needs to be done are
 *              done.
 *
 *              Returns:
 *                      0 -- error occurred, error mesg is stored in tgDbErrMsg
 *                      1 -- query successful, no more processing needed
 *                      2 -- query returns tuples, more processing is needed
 */
{
  int result, count;
  MI_CONNECTION *conn = db->conn;
  char *cmd;
  char *msgPtr = tgDbErrMsg;

  if (conn == NULL) {
    sprintf(tgDbErrMsg, "No connection with the database");
    return 0;
  }
  if ((query != NULL) &&
/*      (mi_exec(conn, query, binary ? MI_QUERY_BINARY : 0))) {*/
      (mi_exec(conn, query, 0))) { /* no binary select for now */
    sprintf(tgDbErrMsg, "Can not execute query: \"%s\"", query);
    return 0;
  }
      
  while ((result = mi_get_result(conn)) != MI_NO_MORE_RESULTS) {
    switch (result) {
    case MI_ERROR:
      sprintf(msgPtr, "Error occured when executing query");
      return 0;
      break;
    case MI_DDL:
      break;
    case MI_DML:
      cmd = mi_result_command_name(conn);
      if ((count = mi_result_row_count(conn)) == MI_ERROR) {
	sprintf(msgPtr, "Can not get row count on a DML command");
	return 0;
      } else {
	sprintf(msgPtr, "%d rows affected by %s command\n", count, cmd);
	msgPtr = (char *) msgPtr + strlen(msgPtr);
      }
      break;
    case MI_ROWS:
      return 2;
      break;
    default:
      sprintf(msgPtr, "Unknown results\n");
      return 0;
      break;
    }
    
  }
  return 1;
}

#endif /* TDB_MI */

#ifdef TDB_PQ
/* -- put function interface to POSTGRES database here -- */

#include "libpq-fe.h"
#include "pqutils.h"
extern char* PQhost;
extern char* PQport;
extern char* PQtty;
extern char* PQoption;
extern char* PQdatabase;
extern char PQerrormsg[];

char beginXact[] = "begin";
char endXact[] = "end";
char abortXact[] = "abort";

TgDb* 
TgNewDB(char *name, char *host, char *port, char *pgtty, char *pgoption)
/*
 * TgNewDB -- Initialize a TgDb structure to contain information about the
 *            database that you want to connect to.
 */
{
  TgDb *result;

  result = (TgDb *) malloc(sizeof(TgDb));

  result->name = (name) ? dupstr(name) : NULL;
  result->host = (host) ? dupstr(host) : "localhost";
  result->port = (port) ? dupstr(port) : "4321";
  result->pgtty = (pgtty) ? dupstr(pgtty) : NULL;
  result->pgoption = (pgoption) ? dupstr(pgoption) : NULL; 
  result->status = 0;
  result->portal = NULL;
  return result;
}

void 
TgFreeDb(TgDb *db)
/*
 * TgFreeDb -- free the db structure.  If a connection is still present when
 *             this routine is called, the connection will be closed.
 *             It would be better for the user to call the macro TG_FREEDB
 *             instead of calling this function directly.
 */
{
  if (db == NULL)
    return;

  if (db->status) {
    TgCloseDB(db);
  }
  TG_FREE(free, db->name);
  TG_FREE(free, db->host);
  TG_FREE(free, db->port);
  TG_FREE(free, db->pgtty);
  TG_FREE(free, db->pgoption);
  TG_FREE(free, db);
}

int 
TgConnectDB(TgDb *db)
/*
 * TgConnectDB -- establish a connection with the database backend.
 *
 *                Returns:
 *                          0  -- connection to server failed
 *                          1  -- connection to server succeed, no error.
 */
{
  char *statusString;

  if (db->status)
    fprintf(stderr,"TgConnectDB: warning, connecting to an already open db\n");

  if ( db->host && (db->host[0] != '\0'))
    PQhost = dupstr(db->host);
  if ( db->port && (db->port[0] != '\0'))
    PQport = dupstr(db->port);
  if ( db->pgtty && (db->pgtty[0] != '\0'))
    PQtty = dupstr(db->pgtty);
  if ( db->pgoption && (db->pgoption[0] != '\0'))
    PQoption = dupstr(db->pgoption);

  if ( db->name && (db->name[0] != '\0'))
    PQsetdb(db->name);
  else
    {
      sprintf(tgDbErrMsg, "TgConnectDB: data base name cannot be null");
      return 0;
    }

  /* check db connection to make sure the database exists and all that */
  statusString = pq_exec(" ", "TgConnectDB");
  if (statusString[0] == 'R' ||
      statusString[0] == 'E') 
    {
	strcpy(tgDbErrMsg,PQerrormsg);
	return 0;
      }
  db->status = 1;

  /* clear the error message string */
  tgDbErrMsg[0] = '\0';
  return 1;
}
  
void 
TgCloseDB(TgDb *db)
/*
 * TgCloseDB -- close the connection with the database server.
 */
{
  if (db->status) {
    PQfinish();
    db->status = 0;
  }
}

TgTupleGroup*
TgRetrieve(TgDb *db, char *query, int binary)
/*
 * TgRetrieve -- Do a retrieve query to the database and returns the result
 *               in a TgTupleGroup.  The result can be in either binary or
 *               ASCII form.
 *
 *               Returns NULL if an error has occurred with the error message
 *               stored in 'tgDbErrMsg'
 */
{
  TgTupleGroup *result;
  if (TgDoQuery(db, query, binary) == 2) {
    result = TgGetData(db, binary);
  } else {
    result = NULL;
  }
  while (TgDoQuery(db, NULL, 0) != 1) ;
  return result;
}

TgTupleGroup*
TgGetData(TgDb *db, int binary)
/*
 * TgGetData -- get data from a retrieve query.  Call this function only
 *              immediately after TgDoQuery() returns 2.
 */
{
  PortalBuffer* pbuf;
  int           numCols;
  int           numRows;
  TgTupleGroup* result;
  TgTypeBlock*  type;
  TgAttValue    value;
  TgTuple       tuple;
  char*         attval;
  int           i,j;
  int           colLen;

  if (db->status == 0 || db->portal == NULL)
    return NULL;

  result = (TgTupleGroup *) malloc(sizeof(TgTupleGroup));

  pbuf = db->portal;

  /* always assume only 1 group returned */
  numCols = PQnfieldsGroup(pbuf,0);
  if (numCols == 0)
    return NULL;

  result->numAttributes = numCols;
  result->type = binary;
  result->attribute = (TgTypeBlock *) malloc(sizeof(TgTypeBlock) * numCols);

  numRows = PQntuplesGroup(pbuf,0);
  result->tuple = (TgTuple *) malloc(sizeof(TgTuple) * numRows);
  result->numTuples = numRows;

  for (i = 0 ; i < numCols ; i++) {
    /* for determining type name, type id, and type size
       use the first group of the result, and assume that
       the result tuples are not ragged*/
    type = &(result->attribute[i]);
    type->name = dupstr(PQfnameGroup(pbuf,0,i));
    type->adtid = PQftypeGroup(pbuf,0,i);
    type->adtsize = PQfsizeGroup(pbuf,0,i);
  }

 /* fill the result->tuple structure
    with ascii representations of the data */
  for (i=0;i<numRows;i++)
  {
    tuple = (TgTuple) malloc(sizeof(TgAttValue)*numCols);
    result->tuple[i] = tuple;
    for (j=0;j<numCols;j++)
      {
	attval = PQgetvalue(pbuf,i,j);
	if (attval)
	  {
	  value = (TgAttValue) malloc(strlen(attval)+1);
	  strcpy(value,attval);
	}
	else
	  value = NULL;
	tuple[j] = value;
      }
  }    
  return result;
}

int 
TgDoQuery(TgDb *db, char *query, int binary)
/*
 * TgDoQuery -- issue an arbituary query to the database back end.
 *              If a NULL is passed in as the query, this function checks
 *              to make sure that all processing that needs to be done are
 *              done.
 *
 *              Returns:
 *                      0 -- error occured, error mesg is stored in tgDbErrMsg
 *                      1 -- query successful, no more processing needed
 *                      2 -- query returns tuples, more processing is needed
 */
{
  char* pqres;
  int ngroups;

  if (db->status == 0) {
    sprintf(tgDbErrMsg, "No connection with the database");
    return 0;
  }
  if (query != NULL && query[0] != '\0')
    {
      pqres = pq_exec(query,"TgDoQuery");
      if (*pqres == 'E' || *pqres == 'R') {
	sprintf(tgDbErrMsg, "TgDoQuery: error in query: \n\t%s\n%s\n",
	       query,PQerrormsg);
	return 0;
      }
      if (*pqres == 'P')
	{
	  db->portal = PQparray(++pqres);
	  ngroups = PQngroups(db->portal);
	  if (ngroups > 1)
	    {
	      fprintf(stderr,"TgDoQuery: query %s returned %d groups.  expected 0\n", query, ngroups);
            }
	  return 2;
	}
      else
	{
	db->portal = NULL;
      }
    }

  /* if we're here, we were either sent a blank query 
     or the pqres was not a 'E', 'R', 'P'

     in either case, we want to clear out the libpq buffer
     in case we have multiple status words returned from the backend
     for a single front-end command
  */

  
  pqres = pq_exec(" ","TgDoQuery");
  if (*pqres == 'I')
    return 1;
  else
    {
      /* this cruftiness handles cases where the backend returns multiple
	 commands for a single front-end command, 
	 e.g.  create view results in CAPPEND and CDEFINE
	 */
      int extras = 0;
      while (*pqres != 'I')
	{
	  pqres = pq_exec(" ", "TgDoQuery");
	  extras++;
	}
      PQFlushI(extras);
      return 1;
    }
}

#endif /* TDB_PQ */

int TgAttNameToNum(TgTupleGroup *tg, char *attName)
/*
 * TgAttNameToNum -- given a tuple group and an attName, returns the attribute
 *                   index.  Returns -1 if attName doesn't exist, or other
 *                   error has occured that prevents the function from
 *                   determine the attribute number.
 */
{
  int i;
  
  if (tg == NULL) {
    return -1;
  }
  for (i = 0 ; i < tg->numAttributes ; i++) {
    if (strcmp(attName, tg->attribute[i].name) == 0) {
      return i;
    }
  }
  return -1;
}

void TgFreeTuplegroup(TgTupleGroup *tg)
/*
 * TgFreeTuplegroup -- free memory associated with a tuple group.  It is
 *                     recommended that you use TgFreeTupleGroup macro
 *                     instead of calling this function directly.
 */
{
  int i , j;
  if (tg == NULL) return;

  for (i = 0 ; i < tg->numTuples ; i++) {
    for (j = 0 ; j < tg->numAttributes ; j++) {
      TG_FREE(free, tg->tuple[i][j]);
    }
    TG_FREE(free, tg->tuple[i]);
  }
  TG_FREE(free, tg->tuple);
  TG_FREE(free, tg->attribute);
  TG_FREE(free, tg);  
}

TgAttValue 
dup_attr(TgTupleGroup* tg, int ti, char* attname)
{
  TgAttValue result;

  result = GET_ATTR(tg, ti, attname);
  if (result)
    return (TgAttValue)(dupstr(result));
  else
    return (TgAttValue)"";
}

/* -- utility functions -- */
/*
 * DuplicateString -- make a copy of the string str.  Returns NULL if the str
 *                    is NULL.
 */
/*
char *DuplicateString (char *str)
{
  char *res;
  int size;

  if (!str) return NULL;
  size = strlen (str) + 1;
  res = (char *) malloc(size);
  strncpy(res, str, size);
  return res;
}
*/
@
