/* $Id: fix.pc 3956 2010-01-05 20:56:56Z zacheiss $
 *
 * User interface routines for dbck (Moira database consistency checker)
 *
 * (c) Copyright 1988-1998 by the Massachusetts Institute of Technology.
 * For copying and distribution information, please see the file
 * <mit-copyright.h>.
 */

#include <mit-copyright.h>
#include <moira.h>
#include "dbck.h"

#include <stdio.h>

EXEC SQL INCLUDE sqlca;  /* SQL Communications Area */
EXEC SQL INCLUDE sqlda;  /* SQL Descriptor Area */

RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/dbck/fix.pc $ $Id: fix.pc 3956 2010-01-05 20:56:56Z zacheiss $");

EXEC SQL BEGIN DECLARE SECTION;
char *_table;
char *_idfield;
char stmt_buf[500];
EXEC SQL END DECLARE SECTION;

extern SQLDA *mr_sqlda;
void generic_ffunc(void *id);

void generic_ffunc(void *id)
{
  int rowcount;

  sprintf(stmt_buf, "DELETE FROM %s WHERE %s.%s = %d",
	  _table, _table, _idfield, (int)(long)id);
  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
  rowcount = sqlca.sqlerrd[2];
  if (rowcount > 0)
    printf("%d entr%s deleted\n", rowcount, rowcount == 1 ? "y" : "ies");
  else
    printf("Not deleted\n");
  modified(_table);
}


void generic_delete(struct save_queue *sq, int (*pfunc)(void *), char *table,
		    char *idfield, int preen)
{
  _table = table;
  _idfield = idfield;
  generic_fix(sq, pfunc, "Delete", generic_ffunc, preen);
}


void single_delete(char *table, char *idfield, int id)
{
  _table = table;
  _idfield = idfield;
  generic_ffunc((void *)(long)id);
}


void zero_fix(char *tbl, char *zrfield, char *idfield, int id)
{
  int rowcount;

  sprintf(stmt_buf, "UPDATE %s SET %s = 0 WHERE %s.%s = %d",
	  tbl, zrfield, tbl, idfield, id);
  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
  rowcount = sqlca.sqlerrd[2];
  if (rowcount > 0)
    printf("%d entr%s fixed\n", rowcount, rowcount == 1 ? "y" : "ies");
  else
    printf("Not fixed\n");
  modified(tbl);
}


int single_fix(char *msg, int preen)
{
  if (mode == MODE_PREEN)
    return preen;

  switch (mode)
    {
    case MODE_ASK:
      if (!prompt(msg))
	break;
    case MODE_YES:
      return 1;
      break;
    case MODE_NO:
      ;
    }
  return 0;
}


void generic_fix(struct save_queue *sq, int (*pfunc)(void *), char *msg,
		 void (*ffunc)(void *), int preen)
{
  int id;

  while (sq_get_data(sq, &id))
    {
      if ((*pfunc)((void *)(long)id) == 0 && single_fix(msg, preen))
	(*ffunc)((void *)(long)id);
    }
  sq_destroy(sq);
}


int prompt(char *msg)
{
  char buf[BUFSIZ];
  EXEC SQL BEGIN DECLARE SECTION;
  extern int dcmenable;
  EXEC SQL END DECLARE SECTION;

  while (1)
    {
      printf("%s (Y/N/Q)? ", msg);
      fflush(stdout);
      fgets(buf, sizeof(buf), stdin);
      if (buf[0] == 'Y' || buf[0] == 'y')
	return 1;
      if (buf[0] == 'N' || buf[0] == 'n')
	return 0;
      if (buf[0] == 'Q' || buf[0] == 'q')
	{
	  if (prompt("Are you sure you want to quit"))
	    {
	      if (prompt("Save database changes"))
		{
		  EXEC SQL COMMIT WORK;
		  cleanup();
		  exit(0);
		}
	      else
		{
		  EXEC SQL ROLLBACK WORK;
		  EXEC SQL UPDATE numvalues SET value = :dcmenable
		    WHERE name = 'dcm_enable';
		  exit(1);
		}
	    }
	}
    }
}


/**
 ** set_next_object_id - set next object id in values table
 **
 ** Inputs: object - object name in values table and in objects
 **	    table - name of table objects are found in
 **
 ** - called before an APPEND operation to set the next object id to
 **   be used for the new record to the next free value
 **
 **/

int set_next_object_id(char *object, char *tablename)
{
  EXEC SQL BEGIN DECLARE SECTION;
  int value;
  char stmt_buf[256], out_buf[256];
  EXEC SQL END DECLARE SECTION;
  int starting_value, errcode = 0;

  EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :object;
  if (sqlca.sqlerrd[2] != 1)
    return MR_NO_ID;

  starting_value = value;
  while (1)
    {
      if (value > MAX_ID_VALUE)
	value = MIN_ID_VALUE;

      sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = %d",
	      object, tablename, object, value);
      EXEC SQL PREPARE inc_stmt FROM :stmt_buf;
      EXEC SQL DECLARE inc_crs CURSOR FOR inc_stmt;
      EXEC SQL OPEN inc_crs;
      mr_sqlda->N = 1;
      EXEC SQL DESCRIBE SELECT LIST FOR inc_stmt INTO mr_sqlda;
      mr_sqlda->N = mr_sqlda->F;
      mr_sqlda->V[0] = out_buf;
      mr_sqlda->T[0] = 97;
      mr_sqlda->L[0] = 255;
      EXEC SQL FETCH inc_crs USING DESCRIPTOR mr_sqlda;

      /* if we got an error from the FETCH, we have to preserve it or the
	 close will reset it and the caller with think nothing happened */
      if (sqlca.sqlcode)
	errcode = sqlca.sqlcode;

      EXEC SQL CLOSE inc_crs;
      if (errcode < 0)
	return MR_DBMS_ERR;
      if (errcode == 1403)
	break;

      value++;
      if (value == starting_value)
	return MR_NO_ID;
    }

  printf("setting ID %s to %d\n", object, value);
  EXEC SQL UPDATE numvalues SET value = :value WHERE name = :object;
  modified("values");
  return MR_SUCCESS;
}


int generic_fix_id(char *tbl, char *idfield, char *txtfield,
		   int oldid, char *name)
{
  EXEC SQL BEGIN DECLARE SECTION;
  int rowcount, id;
  EXEC SQL END DECLARE SECTION;

  set_next_object_id(tbl, idfield);
  EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = :idfield;
  sprintf(stmt_buf, "UPDATE %s SET %s = %d WHERE %s = %d AND %s = '%s'",
	  tbl, idfield, id, idfield, oldid, txtfield, name);
  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
  rowcount = sqlca.sqlerrd[2];
  if (rowcount == 1)
    printf("Fixed\n");
  else
    printf("Not fixed, rowcount = %d\n", rowcount);
  modified(tbl);
  return id;
}
