/*****************************************************************************
File: database.c 
Author: Salas Lopez, Jorge
Date:   31/03/2004
Description: Communication Database Library for postgres v6.4 | v7.2.
Invocation:  Nothing
*****************************************************************************/

/*****************************************************************************  
   Table of Contents:
   Reusable Functions:      
    void Connect(struct DBType *db);
    struct DBType *CreateDBType(char *name, char *options);
    void DestroyDB(struct DBType *db);
    void Disconnect(struct DBType *db);
    int ExecuteQuery(struct DBType *db, const char *query);
    int ExecuteTransaction(struct DBType *db, const char *update);
    int ExecuteUpdate(struct DBType *db, const char *update);
    char *GetCommandStatus(struct DBType *db);
    char *GetDatabaseName(struct DBType *db);
    char *GetErrorMessage(struct DBType *db);
    int GetFieldModifier(struct DBType *db, int index);
    char *GetFieldName(struct DBType *db, int index);
    int GetFieldSize(struct DBType *db, int index);
    int GetFieldType(struct DBType *db, int index);
    int GetNumberFields(struct DBType *db);
    int GetNumberTuples(struct DBType *db);
    char *GetResultValue(struct DBType *db, int tuple, int field);
    int GetValueLenght(struct DBType *db, int tuple, int field);
    int isFieldNull(struct DBType *db, int tuple, int field);
    int ProcessConectionPid(struct DBType *db);    
   Intern Functions:      
    ---
   Includes:	  
    stdio.h; printf, sprintf 
   Source code in this file:
    All
*********************************************************************************/

/********************************************************************************
Import Libraries
*********************************************************************************/
#include <stdio.h>
#include "database.h"
#include "tools.h"

/********************************************************************************
Prototype Functions
*********************************************************************************/
int Connect(struct DBType *db);
struct DBType *CreateDBType(char *name, char *options);
void DestroyDB(struct DBType **db);
void Disconnect(struct DBType *db);
int ExecuteQuery(struct DBType *db, const char *query);
int ExecuteTransaction(struct DBType *db, const char *update);
int ExecuteUpdate(struct DBType *db, const char *update);
int FreeResult(struct DBType *db);
char *GetCommandStatus(struct DBType *db);
char *GetDatabaseName(struct DBType *db);
char *GetErrorMessage(struct DBType *db);
int GetFieldModifier(struct DBType *db, int index);
char *GetFieldName(struct DBType *db, int index);
int GetFieldSize(struct DBType *db, int index);
int GetFieldType(struct DBType *db, int index);
int GetNumberFields(struct DBType *db);
int GetNumberTuples(struct DBType *db);
char *GetResultValue(struct DBType *db, int tuple, int field);
int GetValueLenght(struct DBType *db, int tuple, int field);
int isFieldNull(struct DBType *db, int tuple, int field);
int ProcessConectionPid(struct DBType *db);

/********************************************************************************
Defines
*********************************************************************************/
#define POSTGRESV72
//#define POSTGRESV64

/* Log Management */
#define FATAL_DATABASE
#define ERROR_DATABASE
//#define INFO_DATABASE
//#define WARN_DATABASE
//#define DEBUG_DATABASE

/********************
 *  IMPLEMENTATION  *
 *******************/
/********************************************************************************
Reusable Function.
   void Connect(struct DBType *db);
   Purpose: Connect to database   
   Limitations: Nothing
   Returns: if connection is established return -2
            if an error occurs return -1
            else return 0            
*********************************************************************************/
int Connect(struct DBType *db)
{
  int value = 0;
  
  if ((*db).pgdata.state.message != NULL){    
    Free((*db).pgdata.state.message);
  }
  if ((*db).pgdata.state.result != NULL){    
    FreeResult(db);
  }
  /* Connect to Database */
  //if ((*db).pgdata.connection == NULL){
    #ifdef DEBUG_DATABASE      
      printf("%s\n", (*db).pgdata.repldata.database);
      printf("%s\n", (*db).pgdata.pgoptions);
    #endif
    (*db).pgdata.connection=PQsetdb((*db).pgdata.pghost, (*db).pgdata.pgport, (*db).pgdata.pgoptions, (*db).pgdata.pgtty, (*db).pgdata.repldata.database);    
    /* Review state */
    if (PQstatus((*db).pgdata.connection) == CONNECTION_BAD) {      
      /* Connection fault */
      value = -1;
      Append(&(*db).pgdata.state.message, PQerrorMessage((*db).pgdata.connection));
    }else{
      Append(&(*db).pgdata.state.message, "");      
    }
  //}else{
    /* Connection is established, not necesary connect */
    //value = -2;
    //Append(&(*db).pgdata.state.message, "Connection is established, not necesary connect");
  //}
  #ifdef DEBUG_DATABASE
    printf("Connection database <%s> | <%d>\n", (*db).pgdata.repldata.database, value);
  #endif  
  return value;
}

/********************************************************************************
Reusable Function.
   struct DBType *CreateDBType(char *name, char *options);
   Purpose: Create DBType
   Limitations: Nothing
   Returns: struct DBType
*********************************************************************************/
struct DBType *CreateDBType(char *name, char *options)
{
  struct DBType *db = NULL;

  if (!(db = (struct DBType*) malloc (sizeof(struct DBType)))){
    printf ( "%s# %d # Out of memory.\n", __FILE__, __LINE__ );
  }
  
  /* Initialice Struct */
  //(*db).pgdata.connection=NULL;
  (*db).pgdata.pghost=NULL;
  (*db).pgdata.pgport=NULL;
  (*db).pgdata.pgtty=NULL;
  (*db).pgdata.pgoptions=NULL;
  (*db).pgdata.repldata.qid = 0;
  (*db).pgdata.repldata.database = NULL;
  (*db).pgdata.state.message = NULL;
  (*db).pgdata.state.result = NULL;
  
  /* Name Database */
  Append(&(*db).pgdata.repldata.database, name);  

  /* Options */
  if (options == NULL) {
    #ifdef POSTGRESV72
      Append(&(*db).pgdata.pgoptions, "-R -o ");
    #endif
    #ifdef POSTGRESV64
      Append(&(*db).pgdata.pgoptions, "-R -b -L -o ");
    #endif
    Append(&(*db).pgdata.pgoptions, (char *) getenv("LOCAL"));
    Append(&(*db).pgdata.pgoptions, "/localog");
  }
  #ifdef DEBUG_DATABASE
    printf("Create database <%s> \n", (*db).pgdata.repldata.database);
  #endif  
  return db;
}

/********************************************************************************
Reusable Function.
   void DestroyDB(struct DBType **db);
   Purpose: Destroy DBType
   Limitations: Nothing
   Returns: struct DBType
*********************************************************************************/
void DestroyDB(struct DBType **db)
{
  /* JSalas Falta Hacerlo */
  Free((**db).pgdata.pgoptions);
  Free((**db).pgdata.repldata.database);
  Free(*db);
  #ifdef DEBUG_DATABASE
    printf("Free Memory database\n");
  #endif
}

/********************************************************************************
Reusable Function.
   void Disconnect(struct DBType *db);
   Purpose: Disconnect to database   
   Limitations: Nothing
   Returns: Nothing            
*********************************************************************************/
void Disconnect(struct DBType *db)
{
  if ((*db).pgdata.state.message != NULL){
    Free((*db).pgdata.state.message);
  }
  //printf("DB 1\n");
  PQfinish((*db).pgdata.connection);
  //printf("DB 2\n");
  //(*db).pgdata.connection == NULL;
  #ifdef DEBUG_DATABASE
    printf("Disconnect database <%s>\n", (*db).pgdata.repldata.database);
  #endif
  //printf("DB 3\n");   
  //sleep(1);
  //printf("DB 4\n");
}

/********************************************************************************
Reusable Function.
   int ExecuteQuery(struct DBType *db, const char *query);
   Purpose: Execute query;   
   Limitations: Nothing
   Returns: if an error occurs return -1
            else return 0
*********************************************************************************/
int ExecuteQuery(struct DBType *db, const char *query)
{
  int value = 0;
  char *aux = NULL;
  
  if ((*db).pgdata.state.message != NULL){
      Free((*db).pgdata.state.message);
  }
  if ((*db).pgdata.state.result != NULL){
    FreeResult(db);
  }

  /* Execute Query */
  //aux = (char *) malloc ((strlen(query) + 1) * sizeof(char));
  //strcpy(aux, query);
  //strcat(aux, ";");
  (*db).pgdata.state.result=PQexec((*db).pgdata.connection, query);
  if ((PQresultStatus((*db).pgdata.state.result) != PGRES_COMMAND_OK)
     && (PQresultStatus((*db).pgdata.state.result) != PGRES_TUPLES_OK)) {
    /* ExecuteQuery fault */
    value = -1;    
    Append(&(*db).pgdata.state.message, (char *)PQresultErrorMessage((*db).pgdata.state.result));
  }else{
    Append(&(*db).pgdata.state.message, "");
  }
  #ifdef DEBUG_DATABASE
    printf("Query: <%s> <%s> | <%d>\n", (*db).pgdata.repldata.database, query, value);
  #endif  
  return value;
}

/********************************************************************************
Reusable Function.
   int ExecuteTransaction(struct DBType *db, const char *update);
   Purpose: Execute {BEGIN | COMMIT | ABORT | END}   
   Limitations: Nothing
   Returns: if an error occurs returns -1
            else return 0
*********************************************************************************/
int ExecuteTransaction(struct DBType *db, const char *update)
{
  if (strcmp(update, "BEGIN") == 0){    
    return ExecuteUpdate(db, "begin;");

  }else if (strcmp(update, "END") == 0){
    return ExecuteUpdate(db, "end;");

  }else if (strcmp(update, "ABORT") == 0){
    return ExecuteUpdate(db, "abort;");

  } else {
    return -1;
  }
}

/********************************************************************************
Reusable Function.
   int ExecuteUpdate(struct DBType *db, const char *update);
   Purpose: Execute sql statement   
   Limitations: Nothing
   Returns: if an error occurs returns -1
            else return 0
*********************************************************************************/
int ExecuteUpdate(struct DBType *db, const char *update)
{
  int value = 0;
  //char *aux = NULL;
  
  if ((*db).pgdata.state.message != NULL){    
    Free((*db).pgdata.state.message);    
  }  
  if ((*db).pgdata.state.result != NULL){        
    FreeResult(db);    
  }
  /* Execute Update */
  //aux = (char *) malloc ((strlen(update) + 1) * sizeof(char));
  //strcpy(aux, update);
  //strcat(aux, ";");
  (*db).pgdata.state.result=PQexec((*db).pgdata.connection, update);  
  if ((PQresultStatus((*db).pgdata.state.result) != PGRES_COMMAND_OK)){
    //&& (PQresultStatus((*db).pgdata.state.result) != PGRES_TUPLES_OK)){
    /* ExecuteUpdate fault */    
    value = -1;    
    Append(&(*db).pgdata.state.message, (char *)PQresultErrorMessage((*db).pgdata.state.result));
  }else{    
    Append(&(*db).pgdata.state.message, "");
  }
  #ifdef DEBUG_DATABASE
    printf("Update: <%s> <%s> | <%d>\n", (*db).pgdata.repldata.database, update, value);
  #endif
  //Free(aux);  
  return value;
}

/********************************************************************************
Reusable Function.
   int ExistBinaryTuples(struct DBType *db);
   Purpose: 
   Limitations: Nothing
   Returns: if an error exist return -1
            else return 1 if exist binary tuples
            0 if not exist binary tuples
*********************************************************************************/
int ExistBinaryTuples(struct DBType *db)
{ 
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQbinaryTuples((*db).pgdata.state.result);	
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int FreeResult(struct DBType *db);
   Purpose: Free memory of variable result
   Limitations: Nothing
   Returns: if an error exist return -1
            else return 0
*********************************************************************************/
int FreeResult(struct DBType *db)
{
  int value = 0;

  if ((*db).pgdata.state.result != NULL){
    PQclear((*db).pgdata.state.result);
    (*db).pgdata.state.result = NULL;
  }
  if ((*db).pgdata.state.message != NULL){
    Free((*db).pgdata.state.message);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetCommandStatus(struct DBType *db);
   Purpose: Get the command status string associated with the SQL
            command that generated the PGresult
   Limitations: Nothing
   Returns: if an error exist return NULL
            else return message with command status
*********************************************************************************/
char *GetCommandStatus(struct DBType *db)
{
  char *value = NULL;

  if ((*db).pgdata.state.result != NULL){
    value = PQcmdStatus((*db).pgdata.state.result);	
  }
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetDatabaseName(struct DBType *db);
   Purpose: Get the name of database
   Limitations: Nothing
   Returns: if an error exist return NULL
            else return database's name
*********************************************************************************/
char *GetDatabaseName(struct DBType *db)
{
  char *value = NULL;

  if ((*db).pgdata.repldata.database != NULL){
    Append(&value, (*db).pgdata.repldata.database);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetErrorMessage(struct DBType *db);
   Purpose: Get status message
   Limitations: Nothing
   Returns: Status error message
*********************************************************************************/
char *GetErrorMessage(struct DBType *db)
{
  return (*db).pgdata.state.message;
}

/********************************************************************************
Reusable Function.
   int GetFieldModifier(struct DBType *db, int index);
   Purpose: Get the type-specific modification data of the field
            associated with the given field index.
            Field indices start at 0.  
   Limitations: Nothing
   Returns: if an error occurs return -1
            else return modifier
*********************************************************************************/
int GetFieldModifier(struct DBType *db, int index)
{
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQfmod((*db).pgdata.state.result, index);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetFieldName(struct DBType *db, int index);
   Purpose: Get the field (attribute) name associated 
            with the given field index. Field indices start at 0.
   Limitations: Nothing
   Returns: if an error exist return NULL
            else return the name of field (attribute)           
*********************************************************************************/
char *GetFieldName(struct DBType *db, int index)

{
  char *value = NULL;

  if ((*db).pgdata.state.result != NULL){
    value = PQfname((*db).pgdata.state.result, index);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int GetFieldSize(struct DBType *db, int index);
   Purpose: Get the size in bytes of the field 
            associated with the given field index.
            Field indices start at 0. 
   Limitations: Nothing
   Returns: Return the space allocated for this field in a database tuple, 
             in other words the size of the server's binary representation
             of the data type. 
            Return -1 if the field is variable size 
             or if an error occurs.           
*********************************************************************************/
int GetFieldSize(struct DBType *db, int index)
{
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQfsize((*db).pgdata.state.result, index);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int GetFieldType(struct DBType *db, int index);
   Purpose: Get the field type associated with the given field index. 
            The integer returned is an internal coding of the type.
            Field indices start at 0. 
   Limitations: Nothing
   Returns: if an error exist return -1
            else return the type of field (attribute)           
*********************************************************************************/
int GetFieldType(struct DBType *db, int index)
{
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQftype((*db).pgdata.state.result, index);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int GetNumberFields(struct DBType *db);
   Purpose: Get the number of fields (attributes) 
           in each tuple of the query result
   Limitations: Nothing
   Returns: if an error exist return -1
            else return the number of fields (attributes)           
*********************************************************************************/
int GetNumberFields(struct DBType *db)
{
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQnfields((*db).pgdata.state.result);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int GetNumberTuples(struct DBType *db);
   Purpose: Get number of tuples
   Limitations: Nothing
   Returns: if an error exist return -1
            else return number of tuples
*********************************************************************************/
int GetNumberTuples(struct DBType *db)
{
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQntuples((*db).pgdata.state.result);
  }

  #ifdef DEBUG_DATABASE
    printf("Tuples: %d\n", value);
  #endif  
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetResultValue(struct DBType *db, int tuple, int field);
   Purpose: Get a single field (attribute) value of one tuple. 
            Tuple and field indices start at 0.
   Limitations: Nothing
   Returns: if an error occurs return NULL
            else return value of field
*********************************************************************************/
char *GetResultValue(struct DBType *db, int tuple, int field)
{
  char *value = NULL;

  if ((*db).pgdata.state.result != NULL){
    value = PQgetvalue((*db).pgdata.state.result, tuple, field);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetRowsAffected(struct DBType *db);
   Purpose: Get number of rows affected in the last update
   Limitations: Nothing
   Returns: if an error exist return NULL
            else return number of rows affected
*********************************************************************************/
char *GetRowsAffected(struct DBType *db)
{
  char *value = NULL;

  if ((*db).pgdata.state.result != NULL){
    value = (char *)PQcmdTuples((*db).pgdata.state.result);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int GetValueLenght(struct DBType *db, int tuple, int field);
   Purpose: Returns the length of a field (attribute) in bytes. 
            Tuple and field indices start at 0. 			
   Limitations: Nothing
   Returns: if an error occurs return -1
            else return lenght of data
*********************************************************************************/
int GetValueLenght(struct DBType *db, int tuple, int field)
{
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQgetlength((*db).pgdata.state.result, tuple, field);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int isFieldNull(struct DBType *db, int tuple, int field);
   Purpose: Get number of rows affected in the last update
   Limitations: Nothing
   Returns: if an error exist return -1
            else if data in field is null return 1
            else return 0
*********************************************************************************/
int isFieldNull(struct DBType *db, int tuple, int field)
{
  int value = -1;

  if ((*db).pgdata.state.result != NULL){
    value = PQgetisnull((*db).pgdata.state.result, tuple, field);
  }
  return value;
}

/********************************************************************************
Reusable Function.
   int ProcessConectionPid(struct DBType *db);
   Purpose: Get the process ID of the backend server handling this connection.
   Limitations: Nothing
   Returns: if an error exist return -1
            else return pid
*********************************************************************************/
int ProcessConectionPid(struct DBType *db)
{
  int value = -1;

  if ((*db).pgdata.connection != NULL){
    value = PQbackendPID((*db).pgdata.connection);	
  }
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetFieldMetadata(struct DBType *db);
   Purpose: Get field (attribute) metadata.             
   Limitations: Nothing
   Returns: if an error occurs return NULL
            else return value of field
*********************************************************************************/
char *GetFieldsMetadata(struct DBType *db)
{
  char *value = NULL;
  int nfields = 0;
  int i = 0;
  
  if ((*db).pgdata.state.result != NULL){    
    /* Get number of fields in each tuple */
    nfields = GetNumberFields(db);      
      
    for (i = 0; i < nfields; i++) {      
      Append(&value, GetFieldMetadata(db, i));
    }      
  }
  return value;
}

/********************************************************************************
Reusable Function.
   char *GetFieldMetadata(struct DBType *db);
   Purpose: Get field (attribute) metadata.             
   Limitations: Nothing
   Returns: if an error occurs return NULL
            else return value of field
*********************************************************************************/
char *GetFieldMetadata(struct DBType *db, int field){
  char *buffer = NULL;
  int tamBuffer = 0;
  char *name;
  int typeField, longField, modifierField;

  // JSalas FALTA buffer es estatico en cuanto a tamanno
  if (!(buffer = (char*) malloc (sizeof(char)*100/* TAMBUFFER */))){
    printf ( "%s# %d # Out of memory.\n", __FILE__, __LINE__ );
  }  
    
  /* Name of field */            
  name = GetFieldName(db, field);
  strcpy(buffer, name);
  tamBuffer = strlen(name) + 1;

  /* Type of field (4 bytes) */      
  typeField = GetFieldType(db, field);
  convertToChar (buffer, 4, tamBuffer, typeField);
  tamBuffer = tamBuffer + 4;
      
  /* Long of field (4 /*JSalas 2 bytes) */
  longField = GetFieldSize(db, field);
  convertToChar (buffer, 4, tamBuffer, longField);
  tamBuffer = tamBuffer + 4;
     
  /* Modifier of field (4 bytes) */      
  modifierField = GetFieldModifier(db, field);
  convertToChar (buffer, 4, tamBuffer, modifierField);
  tamBuffer = tamBuffer + 4;
  
  return buffer;
}

