/*****************************************************************************
File: sqlmiddlertools.c
Author: Salas Lopez, Jorge
Date:   27/05/2004
Description: Database Library Tools for postgres v7.2.
Invocation:  Nothing
*****************************************************************************/

/*****************************************************************************
   Table of Contents:
   Reusable Functions:
    int GetWriteSet(struct DBType *db, char *writeset);
    int SetWriteSet(struct DBType *db, char *writeset, int length);
    char *SQLDeleteTableDB(char *db, char *table);
    char *SQLExistDBInDBSchema(char *db);
    char *SQLInsertTableDB(char *maxid, char *db, char *table);
    char *SQLLastIDCC();
    char *SQLRecordsDBSchema();
    char *SQLTablesDB();
   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 GetWriteSet(struct DBType *db, char *writeset);
int SetWriteSet(struct DBType *db, char *writeset, int length);
char *SQLDeleteTableDB(char *db, char *table);
char *SQLExistDBInDBSchema(char *db);
char *SQLInsertTableDB(char *maxid, char *db, char *table);
char *SQLLastIDCC();
char *SQLRecordsDBSchema();
char *SQLTablesDB();

/********************************************************************************
Defines
*********************************************************************************/
//#define DEBUG_SQLMIDDLERTOOLS
#define GET_WRITESET 2074
#define PUT_WRITESET 2075
#define MAX_RETRY_WRITESET 2

/********************
 *  IMPLEMENTATION  *
 *******************/
/********************************************************************************
Reusable Function.
   int GetWriteSet(struct DBType *db, char *writeset);
   Purpose: Get writeset of the last update
   Limitations: Nothing
   Returns: if an error exist return -1
            else return size of writeset
*********************************************************************************/
int GetWriteSet(struct DBType *db, char *writeset)
{
  PQArgBlock ar[1];
  int bytes = -1;

  if ((*db).pgdata.connection != NULL){
    PQfn((*db).pgdata.connection, GET_WRITESET, (int*) writeset, &bytes, 0, ar, 0);
  }

  #ifdef DEBUG_SQLMIDDLERTOOLS
    printf("WriteSet: <%d> <%d>\n", (int*)writeset, bytes);
  #endif
  return bytes;
}

/********************************************************************************
Reusable Function.
   int SetWriteSet(struct DBType *db, char *writeset, int length);
   Purpose: Set writeset of the last update
   Limitations: Nothing
   Returns: if an error exist return -1
            else return size of writeset
*********************************************************************************/
int SetWriteSet(struct DBType *db, char *writeset, int writeset_length)
{
  PGconn * conn = NULL;
  //char * pghost = NULL, * pgport = NULL, * pgtty = NULL;
  PGresult * res;
  //char pgoptions[200];
  PQArgBlock ar[1];
  char dummy[1000];
  int dummy_length;

  ar[0].len = writeset_length;
  ar[0].isint = 0;
  ar[0].u.ptr = ( int * ) writeset;

  conn = (*db).pgdata.connection;

#ifdef D
  printf( "APPLYING a writeset of %d bytes\n", ar[0].len);
#endif

#ifdef DEBUG_PUT_GET
  printf( "put_writeset: " );
  for ( writeset_index = 0; writeset_index < msg.trans.writeset_length; writeset_index++ ) {
    printf( "%c", msg.trans.writeset[writeset_index] );
  }
#endif

  // D Serrano 10/3/04
  // Postgres 7 needs begin before PUT_WRITESET
  res = PQexec( conn, "begin" );
  if ( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
    printf( "Error PQexec postgres <%s>\n", PQerrorMessage( conn ) );
    PQfinish( conn );
    exit( 0 );
  }
  //

  res = PQfn( conn, PUT_WRITESET, ( int * ) dummy, & dummy_length, 0, ar, 1 );
  if ( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
          //printf( "Error executing <PUT_WRITESET> in postgres <%s>\n", PQerrorMessage( conn ) );
          //printf( "PGresult = %s\n", PQresStatus( PQresultStatus( res ) ) );
//          printf( "QM%d sending ws tid %d to postgres\n", qid, msg.trans.transaction.tid );
//          printf("\n");
    res = PQexec( conn, "begin" );
    if ( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
      printf( "Error PQexec postgres <%s>\n", PQerrorMessage( conn ) );
      PQfinish( conn );
      exit( 0 );
    }

    res = PQfn( conn, PUT_WRITESET, ( int * ) dummy, & dummy_length, 0, ar, 1 );
    if ( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
       printf( "Error executing <PUT_WRITESET> in postgres <%s>\n", PQerrorMessage( conn ) );
       printf( "PGresult = %s\n", PQresStatus( PQresultStatus( res ) ) );
       //printf( "QM%d sending ws tid %d to postgres\n", qid, msg.trans.transaction);
    }
//          PQfinish( conn );
//          exit( 0 );
  }

   PQclear(res);
  // D Serrano 10/3/04
  // Postgres 7 needs end after PUT_WRITESET to force commit changes in database
  res = PQexec( conn, "end;" );
  if ( PQresultStatus( res ) != PGRES_COMMAND_OK ) {
    printf( "Error executing <end> in postgres <%s>\n", PQerrorMessage( conn ) );
    PQfinish( conn );
    exit( 0 );
  }
  PQclear(res);

//

#ifdef DEBUG_PUT_GET
  printf( "PGresult = %s\n", PQresStatus( PQresultStatus( res ) ) );
#endif

  // send commit to postgres
#ifdef D
  printf( "QM%d sending commit tid %d to postgres\n", qid, msg.trans.transaction.tid );
#endif

  /*PQArgBlock ar[1];
  char dummy[1000];
  int dummy_length;
  int bytes = -1;
  int retry = 0;
  int ok = 0;

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

  /*
  ar[0].len = msg.trans.writeset_length;
  ar[0].isint = 0;
  ar[0].u.ptr = ( int * ) msg.trans.writeset;
  */
  // D Serrano 10/3/04
  // Postgres 7 needs begin before PUT_WRITESET
  /*if (ExecuteTransaction(db, "BEGIN") == -1){
    printf( "Error PQexec postgres <>\n");
    //PQfinish( conn );
    exit( 0 );
  }
  //

  ar[0].len = length;
  ar[0].isint=0;
  ar[0].u.ptr = (int*) writeset;
  (*db).pgdata.state.result = PQfn((*db).pgdata.connection, PUT_WRITESET, (int*) dummy, &dummy_length, 0, ar, 1);
  if (PQresultStatus((*db).pgdata.state.result) != PGRES_COMMAND_OK) {
    printf( "Error executing <PUT_WRITESET> in postgres <>\n");
//          printf( "PGresult = %s\n", PQresStatus( PQresultStatus( res ) ) );
//          printf( "QM%d sending ws tid %d to postgres\n", qid, msg.trans.transaction.tid );
//          printf("\n");
    /*if (ExecuteTransaction(db, "END") == -1){
      printf( "Error executing <end> in postgres <>\n");
      //PQfinish( conn );
      exit( 0 );
    }*/

    /*if (ExecuteTransaction(db, "BEGIN") == -1){
      printf( "Error PQexec postgres <>\n");
      //PQfinish( conn );
      exit( 0 );
    }

    (*db).pgdata.state.result = PQfn((*db).pgdata.connection, PUT_WRITESET, (int*) dummy, &dummy_length, 0, ar, 1);
    if (PQresultStatus((*db).pgdata.state.result) != PGRES_COMMAND_OK) {
       printf( "Error executing <PUT_WRITESET> in postgres <>\n");
       printf( "PGresult = \n");
       //printf( "QM%d sending ws tid %d to postgres\n", qid, msg.trans.transaction);
    }


//          PQfinish( conn );
//          exit( 0 );
  }

   FreeResult(db);
  // D Serrano 10/3/04
  // Postgres 7 needs end after PUT_WRITESET to force commit changes in database
  if (ExecuteTransaction(db, "END") == -1){
    printf( "Error executing <end> in postgres <>\n");
    //PQfinish( conn );
    exit( 0 );
  }
  FreeResult(db);

  //if ((*db).pgdata.connection != NULL){
  /*while ((!ok) && (retry <= MAX_RETRY_WRITESET)) {
    if (ExecuteTransaction(db, "BEGIN") == -1){
      bytes = -2;
      ok = 1;
      FreeResult(db);

    } else {
      FreeResult(db);

      ar[0].len = length;
      ar[0].isint=0;
      ar[0].u.ptr = (int*) writeset;
      (*db).pgdata.state.result = PQfn((*db).pgdata.connection, PUT_WRITESET, (int*) dummy, &dummy_length, 0, ar, 1);
      if ((!(*db).pgdata.state.result) || (PQresultStatus((*db).pgdata.state.result) != PGRES_COMMAND_OK)){
        /* ExecuteUpdate fault */
    /*    bytes = -1;
        Append(&(*db).pgdata.state.message, "Not possible apply writeset");
        printf("SetWriteSet: <%s> | <%d> | <%s> \n", (*db).pgdata.repldata.database, bytes, (*db).pgdata.state.message);
        retry++;
        continue;

      }else{
        Append(&(*db).pgdata.state.message, "");
        bytes = 0;
        ok = 1;
      }

      FreeResult(db);

#ifdef DEBUG_SQLMIDDLERTOOLS
      printf("SetWriteSet: <%s> | <%d> | <%s> \n", (*db).pgdata.repldata.database, bytes, (*db).pgdata.state.message);
#endif

      /* PostgresSql v72 needs end after SET WRITESET to force commit changes in database */
   /*   if (ExecuteTransaction(db, "END") == -1){
        bytes = -3;
      }
      FreeResult(db);
    }
    retry++;
  }
*/
#ifdef DEBUG_SQLMIDDLERTOOLS
  printf("return SetWriteSet: <%d>\n");
#endif
  return 1/*bytes*/;

}

/********************************************************************************
Reusable Function.
   char *SQLDeleteTableDB(char *db, char *table);
   Purpose: Get sentence sql for delete table in database replicated
   Limitations: Nothing
   Returns: sentence sql -> "insert into conflict_classes values (maxid, 'db', 'table')
*********************************************************************************/
char *SQLDeleteTableDB(char *db, char *table)
{
  char *sqlcode = NULL;
  if (!(sqlcode = (char*) malloc(sizeof(char) * (strlen(db) + strlen(table) + 70 /* 65 const char */)))) {
    printf ( "%s# %d # Out of memory.\n", __FILE__, __LINE__ );
  }

  strcpy (sqlcode, "delete from conflict_classes where dbname = '");
  strcat (sqlcode, db);
  strcat (sqlcode, "' and tablename = '");
  strcat (sqlcode, table);
  strcat (sqlcode, "'");

  #ifdef DEBUG_SQLMIDDLERTOOLS
    printf("SQL Delete: <%s>\n", sqlcode);
  #endif
  return sqlcode;
}

/********************************************************************************
Reusable Function.
   char *SQLExistDBInDBSchema(char *db);
   Purpose: Get sentence sql for delete table in database replicated
   Limitations: Nothing
   Returns: sentence sql -> "insert into conflict_classes values (maxid, 'db', 'table')
*********************************************************************************/
char *SQLExistDBInDBSchema(char *db)
{
  char *sqlcode = NULL;
  if (!(sqlcode = (char*) malloc(sizeof(char) * (strlen(db) + 55 /* 52 const char */)))) {
    printf ( "%s# %d # Out of memory.\n", __FILE__, __LINE__ );
  }

  strcpy (sqlcode, "select id_cc from conflict_classes where dbname = '");
  strcat (sqlcode, db);
  strcat (sqlcode, "'");

  #ifdef DEBUG_SQLMIDDLERTOOLS
    printf("SQL Exist: <%s>\n", sqlcode);
  #endif
  return sqlcode;
}

/********************************************************************************
Reusable Function.
   char *SQLInsertTableDB(char *maxid, char *db, char *table);
   Purpose: Get sentence sql for insert table in database replicated
   Limitations: Nothing
   Returns: sentence sql -> "insert into conflict_classes values (maxid, 'db', 'table')
*********************************************************************************/
char *SQLInsertTableDB(char *maxid, char *db, char *table)
{
  char *sqlcode = NULL;
  if (!(sqlcode = (char*) malloc(sizeof(char) * (strlen(maxid) + strlen(db) + strlen(table) + 50 /* 45 const char */)))) {
    printf ( "%s# %d # Out of memory.\n", __FILE__, __LINE__ );
  }

  strcpy (sqlcode, "insert into conflict_classes values (");
  strcat (sqlcode, maxid);
  strcat (sqlcode, ",'");
  strcat (sqlcode, db);
  strcat (sqlcode, "', ");
  strcat (sqlcode, "'");
  strcat (sqlcode, table);
  strcat (sqlcode, "')");

  #ifdef DEBUG_SQLMIDDLERTOOLS
    printf("SQL Insert Table: <%s>\n", sqlcode);
  #endif
  return sqlcode;
}

/********************************************************************************
Reusable Function.
   char *SQLLastIDCC();
   Purpose: Get sentence sql for get the last id_cc in database schema
   Limitations: Nothing
   Returns: sentence sql -> "select MAX(id_cc) from conflict_classes"
*********************************************************************************/
char *SQLLastIDCC()
{
  return "select MAX(id_cc) from conflict_classes";
}

/********************************************************************************
Reusable Function.
   char *SQLRecordsDBSchema();
   Purpose: Get sentence sql for get records of database schema
   Limitations: Nothing
   Returns: sentence sql -> "select dbname, tablename, id_cc from conflict_classes"
*********************************************************************************/
char *SQLRecordsDBSchema()
{
  return "select dbname, tablename, id_cc from conflict_classes";
}

/********************************************************************************
Reusable Function.
   char *SQLTablesDB();
   Purpose: Get sentence sql for get all tables in replicated database
   Limitations: Nothing
   Returns: sentence sql -> "select relname from pg_class where relkind = 'r'"
*********************************************************************************/
char *SQLTablesDB()
{
  //return "select relname from pg_class where relkind = 'r'";//CPG
  return "SELECT relname FROM pg_class WHERE relname !~~ 'pg%' AND relkind='r'";
}
