/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *  $Id: g_sql.c,v 1.3 2000/09/25 23:42:27 gregm Exp $
 *
 *  g_sql -- A simplified, unified interface to various SQL packages.
 *  Copyright 1999 John Schulien
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc.,  59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
 *
 *  g_sql.c  --  Abstract SQL interface API
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <glib.h>
#include <time.h>
#include <stdlib.h>

#define G_SQL_INTERFACE
#include "g_sql.h"

#include "debug.h"

static GList * backends      = NULL;	/* GList of G_sql_backends */
static GList * backend_names = NULL;	/* GList of backend names */

/*****************************************************************************
 *
 *  g_sql_register_backend ()  --  Register a database backend   
 *
 *  Register a database backend plugin, and call the plugin initialize
 *  subroutine.
 *
 *  Return Values:
 *
 *  TRUE  -- The database backend is initialized and ready
 *  FALSE -- A problem occurred; the database backend was not registered.
 *
 ***************************************************************************/
gboolean	
g_sql_register_backend (G_sql_backend *dbb)
{

  D_FUNC_START;
  g_return_val_if_fail (dbb != NULL, FALSE);
  backends      = g_list_append (backends, dbb);
  backend_names = g_list_append (backend_names, dbb->name);
  d_print (DEBUG_TRACE, "initilizing plugin\n");
  if (dbb->initialize (dbb))
    {
      d_print (DEBUG_TRACE, "backend sucessfully registered.\n");
      D_FUNC_END;
      return TRUE;
    }
  
  backends      = g_list_remove (backends, dbb);
  backend_names = g_list_remove (backend_names, dbb->name);
  d_print (DEBUG_TRACE, "Plugin init failed.\n");
  D_FUNC_END;
  return FALSE;
}

/****************************************************************************
 *
 *  g_sql_unregister_backend ()  --  Remove a database backend
 *
 *  Remove a database backend plugin
 *
 *  Return Values:
 *
 *  TRUE  -- The database backend was successfully removed
 *  FALSE -- A problem occurred. 
 *
 ****************************************************************************/
gboolean
g_sql_unregister_backend (G_sql_backend *dbb)
{
  gboolean   rc;
  GList    * gl;
  GList    * gln;
  
  D_FUNC_START;
  g_return_val_if_fail (dbb != NULL, FALSE);
  d_print (DEBUG_TRACE, "Unregister %s\n", dbb->name);
  if (!(gl = g_list_find (backends, dbb)))
    {
      d_print (DEBUG_TRACE "Backend unregistered.\n");
      D_FUNC_END;
      return FALSE;
    }

  if (!(gln = g_list_find (backend_names, dbb->name)))
    {
      d_print (DEBUG_TRACE, "backend not found.\n");
      D_FUNC_END;
      return FALSE;
    }

  backends      = g_list_remove_link (backends,      gl);
  backend_names = g_list_remove_link (backend_names, gln);

  rc = ((G_sql_backend *)(gl->data))->terminate (dbb);

  g_list_free_1 (gl);
  g_list_free_1 (gln);
  D_FUNC_END;
  return rc;
}

/****************************************************************************
 *
 *  g_sql_enumerate_backends ()  --  Enumerate available database packages
 *
 *  Obtain a linked list of backend names
 *
 *  Return Values:
 *
 *  The return value is a GList.  Each data field in the GList points to
 *  a character string containing the name of a database engine.
 *
 ***************************************************************************/
GList * 	
g_sql_enumerate_backends (void)
{
  d_print (DEBUG_TRACE, "\n");
  return backend_names;
}

/****************************************************************************
 *
 *  g_sql_connect ()  --  Connect to a database server
 *
 *  Attempt to connect to a database server. 
 *
 *  Return Values:
 *
 *  The return value is a handle to a private structure, owned by the 
 *  database backend that identifies the open database, or NULL if the 
 *  operation failed.
 *
 ***************************************************************************/
G_sql_connection *
g_sql_connect (G_sql * db)
{
  GList            * gl;
  
  D_FUNC_START;
  g_return_val_if_fail (db != NULL, NULL);
  if (!db->engine)		/* If no database type was specified */
    {
      d_print (DEBUG_TRACE, "No engine type was specified!\n");
      D_FUNC_END;
      return NULL;		/* then we can't connect, now can we? */
    }
  gl = backends;
  d_print (DEBUG_TRACE, "using engine: %s\n", db->engine);
  if (!gl)
      {
          g_warning ("No registered database backends.");
          g_sql_errno = G_SQL_ERROR_BACKEND_NOT_FOUND;
          D_FUNC_END;
          return NULL;
      }
  while (gl)
    {
      G_sql_backend * gsb;

      gsb = (G_sql_backend *)gl->data;
      if (gsb)
	{
	  if (!strcmp (gsb->name, db->engine))
	    {
	      D_FUNC_END;
	      return ((G_sql_backend *)(gsb))->connect (db);
	    }
	}
      gl = gl->next;
    }
  g_warning ("Backend not found\n");
  g_sql_errno = G_SQL_ERROR_BACKEND_NOT_FOUND;
  D_FUNC_END;
  return NULL;
}

/****************************************************************************
 *  g_sql_disconnect ()  --  Disconnect from a database server
 *
 *  This subroutine closes an open database server connection, and releases
 *  all associated control blocks.
 *
 *  Return values:
 *
 *  TRUE  -- if the operation succeeded
 *  FALSE -- if the operation failed
 *****************************************************************************/
gboolean
g_sql_disconnect (G_sql_connection * dbc)
{
  d_print (DEBUG_TRACE, "\n");
  return dbc->backend->disconnect (dbc);
}

/***************************************************************************
 *
 *  g_sql_enumerate_databases ()  --  List available databases
 *
 *  This subroutine returns a list of available databases on an open server
 *  connection.
 *
 *  Return values:
 *
 *  The return value is a pointer to a glist, containing the names of all
 *  available databases, or NULL if the operation failed.
 * 
 **************************************************************************/

GList *
g_sql_enumerate_databases (G_sql_connection * dbc)
{
  d_print (DEBUG_TRACE, "\n");
  return dbc->backend->enumerate_databases (dbc);
}

/*******************************************************************************
**
**  g_sql_select ()  --  Select a database
**
**  This subroutine selects a database on an open server connection
**
**  Return Values:
**
**  TRUE  -- If the operation succeeded
**  FALSE -- If the operation failed
**
*******************************************************************************/

gboolean	
g_sql_select (G_sql_connection * dbc, gchar * database)
{
  d_print (DEBUG_TRACE, "\n");
  return dbc->backend->select (dbc, database);
}

/*******************************************************************************
**
**  g_sql_query ()  --  Issue an SQL query to a database
**
**  Issue a query request to an open SQL database
**
**  Return values:
**
**  The return value is the handle of an open query structure, or NULL if the
**  operation failed.
**
*******************************************************************************/

G_sql_query *
g_sql_query (G_sql_connection * dbc, gchar * query, gint querylen)
{
  G_sql_query * dbq;
  D_FUNC_START;
  if (!(dbq = dbc->backend->query (dbc, query, querylen)))
    {
      D_FUNC_END;
      return NULL;
    }
  D_FUNC_END;
  return dbq;
}

/*******************************************************************************
**
**  g_sql_free_query ()  --  Free resources associated with an SQL query
**
**  This subroutine frees any resources connected with a specified SQL query.
**
**  Return values:
**
**  TRUE  --  The operation completed successfully
**  FALSE --  The operation failed
**
*******************************************************************************/

gboolean     
g_sql_free_query (G_sql_query * dbq)
{
  d_print (DEBUG_TRACE, "\n");
  return dbq->connection->backend->free_query (dbq);
}

/*******************************************************************************
**
**  g_sql_next_row ()  --  Select the next row in a query result
**
**  Return values:
**
**  TRUE  --  The operation completed successfully
**  FALSE --  The operation failed, or no more rows are available
**
*******************************************************************************/

gboolean
g_sql_next_row (G_sql_query * dbq)
{
  d_print (DEBUG_TRACE, "\n");
  return dbq->connection->backend->next_row (dbq);
}

/*******************************************************************************
**
**  g_sql_field ()  --  Retrieve a field by name from the current row
**
**  Return values:
**
**  The return value is a pointer to the selected field in the row.  
**  This pointer is only valid until the next g_sql_field() call, or until
**  the next g_sql_free_query() call.  In other words, you must memcpy it.
**
**  If the field is not found, NULL is returned.
**
**  The accel field may be used to speed up subsequent calls to g_sql_field
**  as follows:  
**
**  Performing a field name lookup can be somewhat expensive, especially
**  if a lot of rows are to be read.  The **accel field provides a way
**  for the application to cache the results of the field name lookup.
**
**  If accel != NULL, then it is assumed to point to a user-provided
**  gpointer.  If this gpointer contains NULL, the field lookup is done
**  normally, and a value is stored in the gpointer that will be used by
**  subsequent g_sql_field() calls to bypass the field lookup.
**
**  If the application uses this feature, it must be careful to zero the
**  accel pointers after each query, because each query returns its own
**  set of field names.
**
*******************************************************************************/

gchar *
g_sql_field (G_sql_query *dbq, void ** accel, gchar * field, gint *length)
{
    d_print (DEBUG_TRACE "\n");
    return dbq->connection->backend->get_field (dbq, accel, field, length);
}

/*******************************************************************************
**
**  g_sql_field_string ()  --  Find a named field from a database row,
**			       and return a copy of the data as a string.
**
**  This subroutine returns a copy of the named field from the selected  
**  database row.  This is really just a convenience function for 
**  g_sql_field ().  Data is only copied up to the first NULL.
**
**  Return values:
**
**  If the function succeeds, the data contained in the string is duplicated
**  and a pointer to the string is stored in the *stryng field.
** 
**  The return code is either:
**
**  TRUE  -- if the operation succeeded
**  FALSE -- if the operation failed.
**
**  The caller is responsible for g_free()'ing the allocated storage.
**
*******************************************************************************/

gboolean
g_sql_field_string (G_sql_query * dbq, void ** accel, 
		    gchar * field, gchar **stryng)
{
  gchar  *str;
  int     length;

  D_FUNC_START;
  str = g_sql_field (dbq, accel, field, &length);

  if ((str == NULL) || (length == 0))
    {
      D_FUNC_END;
      return FALSE;
    }

  *stryng = g_strdup(str);
  D_FUNC_END;
  return TRUE;
}

/*******************************************************************************
**
**  g_sql_field_int ()  --  Read a named field from a database row,
**		            and return the value as a signed integer.
**
**  This subroutine returns the named field from a selected database row as
**  a signed integer.  This is a convenience function for g_sql_field ().
**
**  Return values:
**
**  If the function succeeds, the integer is stored in the *int field.
**
**  The return code is either:
**
**  TRUE  --  if the operation succeeded
**  FALSE --  if the operation failed.
**
*******************************************************************************/

gboolean
g_sql_field_int (G_sql_query * dbq, void ** accel, 
		 gchar * field, gint *integer)
{

  gchar    *  fieldstart;
  gchar    *  fieldend;
  gint        fieldlen;
  long int    val;

  D_FUNC_START;
  if (!(fieldstart = g_sql_field (dbq, accel, field, &fieldlen)))
    {
      D_FUNC_END;
      return FALSE;
    }

  val = strtol (fieldstart, &fieldend, 10);

  if ((*fieldstart != '\0') && (*fieldend == '\0'))  	/* See man strtol */
    {
      *integer = val;
      D_FUNC_END;
      return TRUE;
    }
  D_FUNC_END;
  return FALSE;
}

/*******************************************************************************
**
**  g_sql_field_double ()  --  Read a named field from a database row,
**                             and return the value as a double.         
**
**  This subroutine returns the named field from a selected database row as
**  a (floating point) double.  This is a convenience function for 
**  g_sql_field ().
**
**  Return values:
**
**  If the function succeeds, the double is stored in the *value field.
**
**  The return code is either:
**
**  TRUE  --  if the operation succeeded
**  FALSE --  if the operation failed.
**
*******************************************************************************/

gboolean
g_sql_field_double (G_sql_query * dbq,   void    ** accel, 
		    gchar       * field, gdouble  * dubble)
{

  gchar    *  fieldstart;
  gchar    *  fieldend;
  gint        fieldlen;
  double      val;
  D_FUNC_START;
 if (!(fieldstart = g_sql_field (dbq, accel, field, &fieldlen)))
   {
     D_FUNC_END;
     return FALSE;
   }

  val = strtod (fieldstart, &fieldend);

  if ((*fieldstart != '\0') && (*fieldend == '\0'))   
    {
      *dubble = val;
      D_FUNC_END;
      return TRUE;
    }
  D_FUNC_END;
  return FALSE;
}


/* EOF */
