Logo Search packages:      
Sourcecode: gambas version File versions  Download package

CResult.c

/***************************************************************************

  CResult.c

  The Result class

  (c) 2000-2003 Beno� Minisini <gambas@users.sourceforge.net>

  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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.

***************************************************************************/

#define __CRESULT_C

#include "main.h"

#include "CResultField.h"
#include "CResult.h"


static GB_SUBCOLLECTION_DESC _fields_desc =
{
  ".ResultFields",
  (void *)CRESULTFIELD_get,
  (void *)CRESULTFIELD_exist,
  (void *)NULL
};


static int check_result(CRESULT *_object)
{
  return (THIS->conn->handle == NULL);
}

static bool check_available(CRESULT *_object)
{
  if (!THIS->available)
  {
    GB.Error("Result is not available");
    return TRUE;
  }
  else
    return FALSE;
}

static void init_buffer(CRESULT *_object)
{
  int i;

  if (THIS->info.nfield == 0)
    return;

  GB.Alloc((void **)&THIS->buffer, sizeof(GB_VARIANT_VALUE) * THIS->info.nfield);

  for (i = 0; i < THIS->info.nfield; i++)
    THIS->buffer[i].type = GB_T_NULL;
}

static void void_buffer(CRESULT *_object)
{
  int i;

  if (THIS->info.nfield == 0)
    return;

  for (i = 0; i < THIS->info.nfield; i++)
    GB.StoreVariant(NULL, &THIS->buffer[i]);
}


static void release_buffer(CRESULT *_object)
{
  if (THIS->buffer)
  {
    void_buffer(THIS);
    GB.Free((void **)&THIS->buffer);
  }
}


static void load_buffer(CRESULT *_object, int pos)
{
  int i, ind;

  if (pos < 0 || pos >= THIS->count || THIS->info.nfield == 0)
  {

      /* Andrea Bortolan's changes for the ODBC modules*/

      /* ODBC does return the number of rows affected by the query when execute a insert,apdate or delete query,
         for all others case it returns -1 even if the query was execute without errors         */
      /* Here the check for this case -1 means that the query was executed correctly
         so get the result.                                                   */
      /* If the pos (the result row) does not exist because pos is > of the rows available than the ODBC module
         will rise an Error ODBC_END_OF_DATA that must be catched by the application */

      if (THIS->count == -1)
      {
            if (THIS->handle && pos != THIS->pos)
            {
                        THIS->driver->Result.Fill(THIS->handle, pos, THIS->buffer,(pos > 0) && (pos == (THIS->pos + 1)));
            }
            THIS->pos = pos;
            THIS->available = TRUE;
      }
      else
      /* End of Andrea's changes */
      {
            THIS->pos = -1;
            THIS->available = FALSE;
      }
  }
  else
  {
    if (THIS->handle && pos != THIS->pos)
    {
      THIS->driver->Result.Fill(THIS->handle, pos, THIS->buffer,
        (pos > 0) && (pos == (THIS->pos + 1)));

      if (THIS->mode == RESULT_EDIT)
      {
        q_init();

        for (i = 0; i < THIS->info.nindex; i++)
        {
          ind = THIS->info.index[i];
          if (i > 0) q_add(" AND ");
          q_add(THIS->info.field[ind].name);
          if (THIS->buffer[ind].type == GB_T_NULL)
            q_add(" IS NULL");
          else
          {
            q_add(" = ");
                DB_FormatVariant(THIS->driver, &THIS->buffer[ind], q_add_length);
          }
        }

        GB.FreeString(&THIS->edit);
        THIS->edit = q_steal();
      }
    }

    THIS->pos = pos;
    THIS->available = TRUE;
  }
}


static void table_release(DB_DATABASE db, DB_INFO *info)
{
  int i;

  if (info->table)
    GB.FreeString(&info->table);

  if (info->field)
  {
    for (i = 0; i < info->nfield; i++)
      GB.FreeString(&info->field[i].name);

    GB.Free((void **)&info->field);
  }

  if (info->index)
    GB.Free((void **)&info->index);
}


CRESULT *DB_MakeResult(CCONNECTION *conn, int mode, char *table, char *query)
{
  CRESULT *_object;
  DB_RESULT res;
  char *duplicate;
  char *token;
  char *error = NULL;

  switch (mode)
  {
    case RESULT_FIND:

      if (conn->driver->Exec(conn->handle, query, &res, "Query failed: &1"))
        return NULL;

      break;

    case RESULT_CREATE:

      res = NULL;
      break;

    case RESULT_EDIT:

      if (conn->driver->Exec(conn->handle, query, &res, "Query failed: &1"))
        return NULL;

      break;
  }

  GB.New((void **)&_object, GB.FindClass("Result"), NULL, NULL);

  THIS->conn = conn;
  GB.Ref(conn);

  THIS->driver = conn->driver;
  THIS->available = FALSE;
  THIS->mode = mode;
  THIS->handle = res;
  THIS->pos = -1;

  switch (mode)
  {
    case RESULT_FIND:

      THIS->driver->Result.Init(THIS->handle, &THIS->info, &THIS->count);
      break;

    case RESULT_CREATE:

      if (THIS->driver->Table.Init(conn->handle, table, &THIS->info))
        goto ERROR;

      THIS->count = 1;
      break;

    case RESULT_EDIT:

      THIS->driver->Result.Init(THIS->handle, &THIS->info, &THIS->count);

      if (THIS->driver->Table.Init(conn->handle, table, &THIS->info))
        goto ERROR;

      if (THIS->driver->Table.Index(conn->handle, table, &THIS->info))
      {
        error = "Table '&1' has no primary key";
        goto ERROR;
      }

      break;
  }

  init_buffer(THIS);
  load_buffer(THIS, 0);

  return THIS;

ERROR:

  GB.Unref((void **)&_object);

  if (error)
    GB.Error(error, table);
  else if (strchr(table, (int)',') == NULL)
  {
    if (!THIS->driver->Table.Exist(conn->handle, table, conn->desc.version)){
       GB.Error("Unknown table: &1", table);
    }
    else {
       GB.Error("Cannot read information about table &1", table);
    }
  }
  else
  {
    duplicate = strdup(table);
    token = strtok(duplicate,",");
    do {
       if (!THIS->driver->Table.Exist(conn->handle, token, conn->desc.version))
          GB.Error("Unknown table: &1", token);
       else
          GB.Error("Cannot read information about table &1", token);
    }
    while ((token = strtok(NULL, ".")) != NULL);
    free(duplicate);
  }

  return NULL;
}


BEGIN_METHOD_VOID(CRESULT_free)

  release_buffer(THIS);

  if (THIS->mode != RESULT_CREATE)
    THIS->driver->Result.Release(THIS->handle, &THIS->info);

  if (THIS->mode != RESULT_FIND)
    table_release(THIS->conn, &THIS->info);

  if (THIS->edit)
    GB.FreeString(&THIS->edit);

  GB.Unref((void **)&THIS->conn);

END_METHOD


BEGIN_PROPERTY(CRESULT_count)

  GB.ReturnInteger(THIS->count);

END_PROPERTY


BEGIN_PROPERTY(CRESULT_index)

  GB.ReturnInteger(THIS->pos);

END_PROPERTY


BEGIN_PROPERTY(CRESULT_available)

  GB.ReturnBoolean(THIS->available);

END_PROPERTY


BEGIN_METHOD(CRESULT_get, GB_STRING field)

  int index;

  if (check_available(THIS))
    return;

  index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE);
  if (index < 0)
    return;

  GB.ReturnPtr(GB_T_VARIANT, &THIS->buffer[index]);

END_METHOD


BEGIN_METHOD(CRESULT_put, GB_VARIANT value; GB_STRING field)

  int index;

  if (check_available(THIS))
    return;

  if (THIS->mode == RESULT_FIND)
  {
    GB.Error("Result is read-only");
    return;
  }

  index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE);
  if (index < 0)
    return;

  if (VARG(value).type != GB_T_NULL && VARG(value).type != THIS->info.field[index].type)
  /*{
    GB.Error("Type mismatch");
    return;
  }*/
  {
    if (GB.Conv((GB_VALUE *)ARG(value), THIS->info.field[index].type))
      return;

    GB.Conv((GB_VALUE *)ARG(value), GB_T_VARIANT);
  }

  GB.StoreVariant(ARG(value), &THIS->buffer[index]);

END_METHOD

#if 0
BEGIN_METHOD(CRESULT_copy, GB_OBJECT result)

  CRESULT *result = (CRESULT *)VARG(result);
  int index;

  if (THIS->mode == RESULT_FIND)
  {
    GB.Error("Result is read-only");
    return;
  }

  for (index = 0; index <

  index = find_field(THIS, GB.ToZeroString(ARG(field)));
  if (index < 0)
    return;

  if (VARG(value).type != GB_T_NULL && VARG(value).type != THIS->info.types[index])
  /*{
    GB.Error("Type mismatch");
    return;
  }*/
  {
    if (GB.Conv((GB_VALUE *)ARG(value), THIS->info.types[index]))
      return;

    GB.Conv((GB_VALUE *)ARG(value), GB_T_VARIANT);
  }

  GB.StoreVariant(ARG(value), &THIS->buffer[index]);

END_METHOD
#endif

BEGIN_METHOD_VOID(CRESULT_move_first)

  load_buffer(THIS, 0);
  GB.ReturnBoolean(THIS->available);

END_METHOD


BEGIN_METHOD_VOID(CRESULT_move_last)

  load_buffer(THIS, THIS->count - 1);
  GB.ReturnBoolean(THIS->available);

END_METHOD


BEGIN_METHOD_VOID(CRESULT_move_previous)

  load_buffer(THIS, THIS->pos - 1);
  GB.ReturnBoolean(THIS->available);

END_METHOD


BEGIN_METHOD_VOID(CRESULT_move_next)

  load_buffer(THIS, THIS->pos + 1);
  GB.ReturnBoolean(THIS->available);

END_METHOD


BEGIN_METHOD(CRESULT_move_to, GB_INTEGER pos)

  load_buffer(THIS, VARG(pos));
  GB.ReturnBoolean(THIS->available);

END_METHOD


BEGIN_METHOD_VOID(CRESULT_next)

  int *pos = (int *)GB.GetEnum();

  load_buffer(THIS, *pos);
  if (THIS->available)
    (*pos)++;
  else
    GB.StopEnum();

END_METHOD


BEGIN_METHOD_VOID(CRESULT_update)

  int i;
  bool comma;
  DB_INFO *info = &THIS->info;

  if (check_available(THIS))
    return;

  q_init();

  switch(THIS->mode)
  {
    case RESULT_CREATE:

      q_add("INSERT INTO ");
      q_add(THIS->driver->GetQuote());
      q_add(info->table);
      q_add(THIS->driver->GetQuote());
      q_add(" ( ");

      comma = FALSE;
      for (i = 0; i < info->nfield; i++)
      {
        if (THIS->buffer[i].type == GB_T_NULL)
          continue;
        if (comma) q_add(", ");
        q_add(info->field[i].name);
        comma = TRUE;
      }

      q_add(" ) VALUES ( ");

      comma = FALSE;
      for (i = 0; i < info->nfield; i++)
      {
        if (THIS->buffer[i].type == GB_T_NULL)
          continue;
        if (comma) q_add(", ");
        DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length);
        comma = TRUE;
      }

      q_add(" )");

      if (!THIS->driver->Exec(THIS->conn->handle, q_get(), NULL, "Cannot create record: &1"))
        void_buffer(THIS);

      break;

    case RESULT_EDIT:

      q_add("UPDATE ");
      q_add(THIS->driver->GetQuote());
      q_add(info->table);
      q_add(THIS->driver->GetQuote());
      q_add(" SET ");

      for (i = 0; i < info->nfield; i++)
      {
        if (i > 0) q_add(", ");
        q_add(THIS->info.field[i].name);
        q_add(" = ");
            DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length);
      }

      q_add(" WHERE ");
      q_add(THIS->edit);

      THIS->driver->Exec(THIS->conn->handle, q_get(), NULL, "Cannot modify record: &1");

      break;

    default:

      GB.Error("Result is read-only");
      break;
  }

END_METHOD


BEGIN_METHOD_VOID(CRESULT_delete)

  DB_INFO *info = &THIS->info;

  if (check_available(THIS))
    return;

  q_init();

  switch(THIS->mode)
  {
    case RESULT_CREATE:

      void_buffer(THIS);
      break;

    case RESULT_EDIT:

      q_add("DELETE FROM ");
      q_add(THIS->driver->GetQuote());
      q_add(info->table);
      q_add(THIS->driver->GetQuote());
      q_add(" WHERE ");
      q_add(THIS->edit);

      THIS->driver->Exec(THIS->conn->handle, q_get(), NULL, "Cannot delete record: &1");

      break;

    default:

      GB.Error("Result is read-only");
      break;
  }

END_METHOD




BEGIN_PROPERTY(CRESULT_fields)

  GB.SubCollection.New(&THIS->fields, &_fields_desc, THIS);
  GB.ReturnObject(THIS->fields);

END_PROPERTY


BEGIN_PROPERTY(CRESULT_connection)

  GB.ReturnObject(THIS->conn);

END_PROPERTY


GB_DESC CResultDesc[] =
{
  GB_DECLARE("Result", sizeof(CRESULT)), GB_NOT_CREATABLE(),

  GB_HOOK_CHECK(check_result),

  GB_METHOD("_free", NULL, CRESULT_free, NULL),

  GB_PROPERTY_READ("Count", "i", CRESULT_count),
  GB_PROPERTY_READ("Length", "i", CRESULT_count),
  GB_PROPERTY_READ("Available", "b", CRESULT_available),
  GB_PROPERTY_READ("Index", "i", CRESULT_index),

  GB_METHOD("_get", "v", CRESULT_get, "(Field)s"),
  GB_METHOD("_put", NULL, CRESULT_put, "(Value)v(Field)s"),
  GB_METHOD("_next", NULL, CRESULT_next, NULL),

  GB_METHOD("MoveFirst", "b", CRESULT_move_first, NULL),
  GB_METHOD("MoveLast", "b", CRESULT_move_last, NULL),
  GB_METHOD("MovePrevious", "b", CRESULT_move_previous, NULL),
  GB_METHOD("MoveNext", "b", CRESULT_move_next, NULL),
  GB_METHOD("MoveTo", "b", CRESULT_move_to, "(Index)i"),

  GB_METHOD("Update", NULL, CRESULT_update, NULL),
  GB_METHOD("Delete", NULL, CRESULT_delete, NULL),

  GB_PROPERTY_READ("Fields", ".ResultFields", CRESULT_fields),
  GB_PROPERTY_READ("Connection", "Connection", CRESULT_connection),

  GB_END_DECLARE
};



Generated by  Doxygen 1.6.0   Back to index