/*
 *	tclSQL.c	- Source for sql commands
 *
 * Copyright (c) 1994   Brad Pepers
 *
 * Permission to use, copy, and distribute for non-commercial purposes,
 * is hereby granted without fee, providing that the above copyright
 * notice appear in all copies and that both the copyright notice and this
 * permission notice appear in supporting documentation.
 *
 * This software is provided "as is" without any expressed or implied
 * warranty.
 *
 * $Id: .~tclLCAa14505,v 1.1.1.1 1996/09/05 14:29:49 heyes Exp $
 *
 * $Log: .~tclLCAa14505,v $
 * Revision 1.1.1.1  1996/09/05 14:29:49  heyes
 * Initial Import
 *
 * Revision 1.6  1994/08/25  00:28:07  pepers
 * Changed for mSQL version 0.2
 *
 * Revision 1.5  1994/07/28  23:18:05  pepers
 * Added rows and cols commands
 *
 * Revision 1.4  1994/07/28  22:44:43  pepers
 * Changed around how commands work
 *
 * Revision 1.3  1994/07/28  02:04:54  pepers
 * Changed close command to disconnect
 *
 * Revision 1.2  1994/07/27  22:43:27  pepers
 * Fixed log files
 *
 * Revision 1.1  1994/07/27  22:34:48  pepers
 * First version of mSQL to Tcl/Tk interface
 *
*/

#include "tclSQL.h"

char RCS_ID[] = "$Id: .~tclLCAa14505,v 1.1.1.1 1996/09/05 14:29:49 heyes Exp $";

m_result *query_result = NULL;

static msqlConn *msqlConnHead = NULL;

void MSQL_Init(Tcl_Interp *interp)
{
  Tcl_CreateCommand(interp, "msql", MSQL_Cmd, 0, 0);
}

int MSQL_Cmd(ClientData clientData, Tcl_Interp *interp, int argc,
	    char **argv)
{
  if (argc < 2) {
    Tcl_AppendResult(interp, argv[0], ": missing command name.", 0);
    return TCL_ERROR;
  }

  if (strcmp(argv[1], "connect") == 0)
    return MSQL_Cmd_Connect(clientData, interp, argc, argv);
  else if (strcmp(argv[1], "connections") == 0)
    return MSQL_Cmd_Connections(clientData, interp, argc, argv);

  Tcl_AppendResult(interp, argv[0], ": unsupported command.", 0);
  return TCL_ERROR;
}

int MSQL_Cmd_Connect(ClientData clientData, Tcl_Interp *interp, int argc,
	    char **argv)
{
  char *name = NULL;
  char *host = NULL;
  char tmp[40];
  int socket;
  FILE *fp;
  msqlConn *c,*cl;

  if (argc > 4 || argc < 3|| *argv[2] == '\0') {
    Tcl_AppendResult(interp, "usage: sql connect name ?host?.", 0);
    return TCL_ERROR;
  }

  name = argv[2];

  if (argc > 3 && *argv[3] != '\0')
    host = argv[3];

  socket = msqlConnect(host);

  if (socket < 0) {
    Tcl_AppendResult(interp, argv[0], ": ", msqlErrMsg, 0);
    return TCL_ERROR;
  }

  c = msqlConnHead;

  while (c != NULL) {
    if (strcmp(c->name,name) == 0) 
      break;
    cl = c;
  }

  if (c == NULL) {
    c = (msqlConn *) malloc(sizeof(msqlConn));
    bzero((char *) c, sizeof(msqlConn));
  } 

  c->socket = socket;
  strcpy (c->name,name);
  if (host == NULL) {
    c->host = NULL;
  } else {
    c->host = (char *) malloc(strlen(host) + 1);
    strcpy (c->host,host);
  }

  c->next = msqlConnHead;
  msqlConnHead = c;

  if (c->database[0] != 0) {
    msqlSelectDB(c->socket,c->database);
  }

  Tcl_CreateCommand(interp, name, MSQL_Conn_Cmd, (ClientData) c, 0);

  return TCL_OK;
}

int MSQL_Cmd_Connections(ClientData clientData, Tcl_Interp *interp, int argc,
	    char **argv)
{
  msqlConn *c;

  c = msqlConnHead;
  
  while (c != NULL) {
    Tcl_AppendResult(interp, c->name, 0);
    c = c->next;
  }
  return TCL_OK;
}

int MSQL_Conn_Cmd(ClientData clientData, Tcl_Interp *interp, int argc,
	    char **argv)
{
  if (argc < 2) {
    Tcl_AppendResult(interp, argv[0], ": missing command name.", 0);
    return TCL_ERROR;
  }

  if (strcmp(argv[1], "get") == 0)
    return MSQL_Cmd_Get((msqlConn *)clientData, interp, argc, argv);
  else if (strcmp(argv[1], "set") == 0)
    return MSQL_Cmd_Set((msqlConn *)clientData, interp, argc, argv);
  else if (strcmp(argv[1], "query") == 0)
    return MSQL_Cmd_Query((msqlConn *)clientData, interp, argc, argv);
  else if (strcmp(argv[1], "disconnect") == 0) {
    return MSQL_Cmd_Disconnect((msqlConn *)clientData, interp, argc, argv);
  }

  Tcl_AppendResult(interp, argv[0], ": unsupported command.", 0);
  return TCL_ERROR;
}

int MSQL_Cmd_Disconnect(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  if (argc != 2) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " disconnect.", 0);
    return TCL_ERROR;
  }

  msqlClose(c->socket);

  return Tcl_DeleteCommand(interp, argv[0]);
}

int MSQL_Cmd_Set(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  if (argc < 3) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " set ???.", 0);
    return TCL_ERROR;
  }

  if (strcmp(argv[2], "database") == 0)
    return MSQL_Set_DB(c, interp, argc, argv);
  else if (strcmp(argv[2], "row") == 0)
    return MSQL_Set_Row(c, interp, argc, argv);

  Tcl_AppendResult(interp, argv[0], ": unknown set name ", argv[2], ".", 0);
  return TCL_ERROR;
}

int MSQL_Set_DB(msqlConn *c, Tcl_Interp *interp, int argc,
	       char **argv)
{
  int res;

  if (argc != 4) {
    Tcl_AppendResult(interp, argv[0], " ", argv[1], " ", argv[2],
		     ": missing database name", 0);
    return TCL_ERROR;
  }

  strcpy(c->database,argv[3]);
 retry:
  if ((res = msqlSelectDB(c->socket, argv[3])) == -1) {
    if ((strcmp(msqlErrMsg,"MSQL server has gone away")==0) && ((c->socket = msqlConnect((char *) c->host)) != -1 )) {
      printf("msqld may have been down.. retry command\n");
      if (c->database[0] != 0) {
	msqlSelectDB(c->socket,c->database);
      }

      goto retry;
    } else {
      Tcl_AppendResult(interp, argv[0], " ", argv[1], " ", argv[2],
		       ": ", msqlErrMsg, 0);
      return TCL_ERROR;
    }
  }

  return TCL_OK;
}

int MSQL_Set_Row(msqlConn *c, Tcl_Interp *interp, int argc,
	       char **argv)
{
  int res;

  if (argc != 4) {
    Tcl_AppendResult(interp, argv[0], " ", argv[1], " ", argv[2],
		     ": missing row number", 0);
    return TCL_ERROR;
  }

  if (query_result == NULL) {
    Tcl_AppendResult(interp, argv[0], " ", argv[1], " ", argv[2],
		     ": no current query", 0);
    return TCL_ERROR;
  }
  msqlDataSeek(query_result, atoi(argv[3]));
  return TCL_OK;
}

int MSQL_Cmd_Get(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  if (argc < 3) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " get ???.", 0);
    return TCL_ERROR;
  }

  if (strcmp(argv[2], "databases") == 0)
    return MSQL_Get_DBs(c, interp, argc, argv);
  else if (strcmp(argv[2], "tables") == 0)
    return MSQL_Get_Tables(c, interp, argc, argv);
  else if (strcmp(argv[2], "fields") == 0)
    return MSQL_Get_Fields(c, interp, argc, argv);
  else if (strcmp(argv[2], "rest") == 0)
    return MSQL_Get_Rest(c, interp, argc, argv);
  else if (strcmp(argv[2], "next") == 0)
    return MSQL_Get_Next(c, interp, argc, argv);
  else if (strcmp(argv[2], "rows") == 0)
    return MSQL_Get_Rows(c, interp, argc, argv);
  else if (strcmp(argv[2], "cols") == 0)
    return MSQL_Get_Cols(c, interp, argc, argv);

  Tcl_AppendResult(interp, argv[0], ": unknown get name ", argv[2], ".", 0);
  return TCL_ERROR;
}

void free_query_result(void)
{
  if (query_result != NULL) {
    msqlFreeResult(query_result);
    query_result = NULL;
  }
}

int MSQL_Get_DBs(msqlConn *c, Tcl_Interp *interp, int argc,
	       char **argv)
{
  Tcl_DString results;
  m_result *result;
  int num_cols;
  m_row row;
  int pos;

  if (argc != 3) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " get databases.", 0);
    return TCL_ERROR;
  }
 retry:
  result = msqlListDBs(c->socket);
  
  if (result == NULL) {
    if ((strcmp(msqlErrMsg,"MSQL server has gone away")==0) && ((c->socket = msqlConnect((char *) c->host)) != -1 )) {
      printf("msqld may have been down.. retry command\n");
      if (c->database[0] != 0) {
	msqlSelectDB(c->socket,c->database);
      }

      goto retry;
    } 
    if (msqlErrMsg[0] != '\0') {
      Tcl_AppendResult(interp, argv[0], " ", argv[1], " ", argv[2],
		       ": ", msqlErrMsg, 0);
      return TCL_ERROR;
    }
    return TCL_OK;
  }

  Tcl_DStringInit(&results);

  num_cols = 1;
  while ((row = msqlFetchRow(result)) != NULL) {
    Tcl_DStringStartSublist(&results);
    for (pos = 0; pos < num_cols; pos++) {
      Tcl_DStringAppendElement(&results, row[pos]);
    }
    Tcl_DStringEndSublist(&results);
  }

  msqlFreeResult(result);

  Tcl_DStringResult(interp, &results);
  return TCL_OK;
}

int MSQL_Get_Tables(msqlConn *c, Tcl_Interp *interp, int argc,
	       char **argv)
{
  Tcl_DString results;
  m_result *result;
  int num_cols;
  m_row row;
  int pos;

  if (argc != 3) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " get tables.", 0);
    return TCL_ERROR;
  }
 retry:
  result = msqlListTables(c->socket);

  if (result == NULL) {
    if ((strcmp(msqlErrMsg,"MSQL server has gone away")==0) && ((c->socket = msqlConnect((char *) c->host)) != -1 )) {
      printf("msqld may have been down.. retry command\n");
      
      if (c->database[0] != 0) {
	msqlSelectDB(c->socket,c->database);
      }

      goto retry;
    }
    if (msqlErrMsg[0] != '\0') {
      Tcl_AppendResult(interp, argv[0], " ", argv[1], " ", argv[2],
		       ": ", msqlErrMsg, 0);
      return TCL_ERROR;
    }
    return TCL_OK;
  }

  Tcl_DStringInit(&results);

  num_cols = 1;
  while ((row = msqlFetchRow(result)) != NULL) {
    Tcl_DStringStartSublist(&results);

    for (pos = 0; pos < num_cols; pos++)
      Tcl_DStringAppendElement(&results, row[pos]);

    Tcl_DStringEndSublist(&results);
  }

  msqlFreeResult(result);

  Tcl_DStringResult(interp, &results);
  return TCL_OK;
}

int MSQL_Get_Fields(msqlConn *c, Tcl_Interp *interp, int argc,
	       char **argv)
{
  Tcl_DString results;
  m_result *result;
  m_field *field;
  char buf[40];

  if (argc != 4) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " get fields <table>.", 0);
    return TCL_ERROR;
  }
 retry:
  result = msqlListFields(c->socket, argv[3]);
  
  if (result == NULL) {
    if ((strcmp(msqlErrMsg,"MSQL server has gone away") == 0) && ((c->socket = msqlConnect((char *) c->host)) != -1 )) {
      printf("msqld may have been down.. retry command\n");
      if (c->database[0] != 0) {
	msqlSelectDB(c->socket,c->database);
      }

      goto retry;
    }  
    
    if (msqlErrMsg[0] != '\0') {
      Tcl_AppendResult(interp, argv[0], " ", argv[1], " ", argv[2],
		       ": ", msqlErrMsg, 0);
      return TCL_ERROR;
    }
    return TCL_OK;
  }

Tcl_DStringInit(&results);

  while ((field = msqlFetchField(result)) != NULL) {
    Tcl_DStringStartSublist(&results);
    
    Tcl_DStringAppendElement(&results, field->name);

    switch (field->type) {
    case INT_TYPE:
      Tcl_DStringAppendElement(&results, "INT");
      break;
    case REAL_TYPE:
      Tcl_DStringAppendElement(&results, "REAL");
      break;
    case CHAR_TYPE:
      sprintf(buf, "CHAR(%d)", field->length);
      Tcl_DStringAppendElement(&results, buf);
      break;
    }

    sprintf(buf, "%d", field->flags);
    Tcl_DStringAppendElement(&results, buf);
    
    Tcl_DStringEndSublist(&results);
  }

  msqlFreeResult(result);

  Tcl_DStringResult(interp, &results);
  return TCL_OK;
}

int MSQL_Get_Rest(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  Tcl_DString results;
  int num_cols;
  m_row row;
  int pos;

  if (query_result == NULL) {
    return TCL_OK;
  }

  Tcl_DStringInit(&results);

  num_cols = msqlNumFields(query_result);
  while (row = msqlFetchRow(query_result)) {
    Tcl_DStringStartSublist(&results);

    for (pos = 0; pos < num_cols; pos++)
      Tcl_DStringAppendElement(&results, row[pos]);

    Tcl_DStringEndSublist(&results);
  }

  Tcl_DStringResult(interp, &results);
  return TCL_OK;
}

int MSQL_Get_Next(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  Tcl_DString results;
  int num_cols;
  m_row row;
  int pos;

  if (query_result == NULL) {
    return TCL_OK;
  }

  num_cols = msqlNumFields(query_result);
  row = msqlFetchRow(query_result);
  
  if (row == NULL) {
    return TCL_OK;
  }

  Tcl_DStringInit(&results);

  for (pos = 0; pos < num_cols; pos++)
    Tcl_DStringAppendElement(&results, row[pos]);

  Tcl_DStringResult(interp, &results);
  return TCL_OK;
}

int MSQL_Get_Rows(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  char buf[80];

  if (argc != 3) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " get rows.", 0);
    return TCL_ERROR;
  }

  if (query_result == NULL) {
    Tcl_AppendResult(interp, "0", 0);
    return TCL_OK;
  }

  sprintf(buf, "%d", msqlNumRows(query_result));
  Tcl_AppendResult(interp, buf, 0);
  return TCL_OK;
}

int MSQL_Get_Cols(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  char buf[80];

  if (argc != 3) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " get cols.", 0);
    return TCL_ERROR;
  }

  if (query_result == NULL) {
    Tcl_AppendResult(interp, "0", 0);
    return TCL_OK;
  }

  sprintf(buf, "%d", msqlNumFields(query_result));
  Tcl_AppendResult(interp, buf, 0);
  return TCL_OK;
}

int MSQL_Cmd_Query(msqlConn *c, Tcl_Interp *interp, int argc,
	    char **argv)
{
  int res;

  if (argc != 3) {
    Tcl_AppendResult(interp, "usage: ", argv[0], " query <query>.", 0);
    return TCL_ERROR;
  }

 retry:
  if ((res = msqlQuery(c->socket, argv[2])) == -1) 
    {
      if ((strcmp(msqlErrMsg,"MSQL server has gone away") ==0) && ((c->socket = msqlConnect((char *) c->host)) != -1 )) {
	printf("msqld may have been down.. retry command\n");
        if (c->database[0] != 0) {
	 msqlSelectDB(c->socket,c->database);
      } 

	goto retry;
      } else {
	Tcl_AppendResult(interp, argv[0], " ", argv[1], ": ", msqlErrMsg, 0);
	return TCL_ERROR;
      }
    }
  
  free_query_result();
  query_result = msqlStoreResult(c->socket);

  return TCL_OK;
}
