9488c7aff7
* NEW: Automatic completion now displays hidden symbols if the class is part of the current project. * NEW: Support for *.tar.xz source archives. * NEW: Underscores are now allowed inside a class name. * BUG: Enabling or disabling tooltips in the option dialog does not crash anymore. [INTERPRETER] * NEW: Rename many virtual classes everywhere so that the documentation can easily extract the property name of the parent class from the virtual class name. For example, ".ApplicationArgs" is now ".Application.Args". git-svn-id: svn://localhost/gambas/trunk@4028 867c0c6c-44f3-4631-809d-bfa615b0a4ec
907 lines
18 KiB
C
907 lines
18 KiB
C
/***************************************************************************
|
|
|
|
CResult.c
|
|
|
|
(c) 2000-2011 Benoît 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 2, 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., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __CRESULT_C
|
|
|
|
#include "main.h"
|
|
|
|
#include "deletemap.h"
|
|
#include "CField.h"
|
|
#include "CResultField.h"
|
|
#include "CResult.h"
|
|
|
|
GB_CLASS CLASS_Blob;
|
|
|
|
static CBLOB *make_blob(CRESULT *result, int field);
|
|
static void set_blob(CBLOB *_object, char *data, int length);
|
|
|
|
|
|
static GB_SUBCOLLECTION_DESC _fields_desc =
|
|
{
|
|
".Result.Fields",
|
|
(void *)CRESULTFIELD_get,
|
|
(void *)CRESULTFIELD_exist,
|
|
(void *)NULL,
|
|
(void *)CRESULTFIELD_release
|
|
};
|
|
|
|
|
|
static int check_result(CRESULT *_object)
|
|
{
|
|
return (THIS->conn->db.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(POINTER(&THIS->buffer), sizeof(GB_VARIANT_VALUE) * THIS->info.nfield);
|
|
BARRAY_create(&THIS->changed, THIS->info.nfield);
|
|
BARRAY_clear_all(THIS->changed, 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;
|
|
|
|
//fprintf(stderr, "void_buffer\n");
|
|
|
|
if (THIS->info.nfield == 0)
|
|
return;
|
|
|
|
for (i = 0; i < THIS->info.nfield; i++)
|
|
GB.StoreVariant(NULL, &THIS->buffer[i]);
|
|
|
|
BARRAY_clear_all(THIS->changed, THIS->info.nfield);
|
|
}
|
|
|
|
|
|
static void release_buffer(CRESULT *_object)
|
|
{
|
|
if (THIS->buffer)
|
|
{
|
|
void_buffer(THIS);
|
|
GB.Free(POINTER(&THIS->buffer));
|
|
BARRAY_delete(&THIS->changed);
|
|
}
|
|
}
|
|
|
|
|
|
static bool load_buffer(CRESULT *_object, int vpos)
|
|
{
|
|
int i, ind;
|
|
int pos;
|
|
|
|
//fprintf(stderr, "load_buffer: %ld -> %d\n", THIS->pos, vpos);
|
|
|
|
/* if THIS->count < 0, that's mean that the driver couldn't determine it */
|
|
|
|
DB_CurrentDatabase = &THIS->conn->db;
|
|
|
|
if (THIS->count >= 0 && (vpos < 0 || vpos >= 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 0
|
|
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 */
|
|
#endif
|
|
|
|
{
|
|
THIS->pos = -1;
|
|
THIS->available = FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (THIS->handle && vpos != THIS->pos)
|
|
{
|
|
pos = DELETE_MAP_virtual_to_real(THIS->dmap, vpos);
|
|
|
|
//fprintf(stderr, "Result %p: Loading real %ld\n", THIS, pos);
|
|
|
|
void_buffer(THIS);
|
|
|
|
THIS->driver->Result.Fill(&THIS->conn->db, THIS->handle, pos, THIS->buffer,
|
|
(pos > 0) && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, 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 = vpos;
|
|
THIS->available = TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static void reload_buffer(CRESULT *_object)
|
|
{
|
|
int pos = THIS->pos;
|
|
THIS->pos = -1;
|
|
load_buffer(THIS, pos);
|
|
}
|
|
|
|
|
|
static void table_release(DB_INFO *info)
|
|
{
|
|
int i;
|
|
|
|
if (info->table)
|
|
GB.FreeString(&info->table);
|
|
|
|
if (info->field)
|
|
{
|
|
for (i = 0; i < info->nfield; i++)
|
|
CFIELD_free_info(&info->field[i]);
|
|
|
|
GB.Free(POINTER(&info->field));
|
|
}
|
|
|
|
if (info->index)
|
|
GB.Free(POINTER(&info->index));
|
|
}
|
|
|
|
|
|
static GB_TYPE get_field_type(CRESULT *_object, int field)
|
|
{
|
|
GB_TYPE type;
|
|
|
|
if (THIS->info.field)
|
|
type = THIS->info.field[field].type;
|
|
else
|
|
type = THIS->driver->Result.Field.Type(THIS->handle, field);
|
|
|
|
//fprintf(stderr, "get_field_type: %d -> %ld\n", field, type);
|
|
return type;
|
|
}
|
|
|
|
|
|
CRESULT *DB_MakeResult(CCONNECTION *conn, int mode, char *table_temp, char *query)
|
|
{
|
|
CRESULT *_object;
|
|
DB_RESULT res;
|
|
char *duplicate;
|
|
char *token;
|
|
const char *error = NULL;
|
|
char *arg;
|
|
char *table;
|
|
|
|
switch (mode)
|
|
{
|
|
case RESULT_FIND:
|
|
|
|
if (conn->driver->Exec(&conn->db, query, &res, "Query failed: &1"))
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
case RESULT_CREATE:
|
|
|
|
res = NULL;
|
|
break;
|
|
|
|
case RESULT_EDIT:
|
|
|
|
if (conn->driver->Exec(&conn->db, query, &res, "Query failed: &1"))
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
case RESULT_DELETE:
|
|
|
|
conn->driver->Exec(&conn->db, query, &res, "Query failed: &1");
|
|
return NULL;
|
|
}
|
|
|
|
_object = GB.New(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;
|
|
THIS->dmap = NULL;
|
|
|
|
// table must be copied because it can be a temporary string!
|
|
table = GB.NewZeroString(table_temp);
|
|
|
|
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->db, 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->db, table, &THIS->info))
|
|
goto ERROR;
|
|
|
|
if (THIS->driver->Table.Index(&conn->db, table, &THIS->info))
|
|
{
|
|
error = "Table '&1' has no primary key";
|
|
arg = table;
|
|
goto ERROR;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
init_buffer(THIS);
|
|
load_buffer(THIS, 0);
|
|
|
|
GB.FreeString(&table);
|
|
return THIS;
|
|
|
|
ERROR:
|
|
|
|
if (!error)
|
|
{
|
|
if (strchr(table, (int)',') == NULL)
|
|
{
|
|
arg = table;
|
|
if (!THIS->driver->Table.Exist(&conn->db, table))
|
|
error = "Unknown table: &1";
|
|
else
|
|
error = "Cannot read information about table &1";
|
|
}
|
|
else
|
|
{
|
|
duplicate = GB.NewZeroString(table);
|
|
token = strtok(duplicate,",");
|
|
do {
|
|
arg = token;
|
|
if (!THIS->driver->Table.Exist(&conn->db, token))
|
|
error = "Unknown table: &1";
|
|
else
|
|
error = "Cannot read information about table '&1'";
|
|
}
|
|
while ((token = strtok(NULL, ".")) != NULL);
|
|
GB.FreeString(&duplicate);
|
|
}
|
|
}
|
|
|
|
GB.Free(POINTER(&_object));
|
|
GB.Error(error, arg);
|
|
GB.FreeString(&table);
|
|
|
|
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->info);
|
|
|
|
if (THIS->edit)
|
|
GB.FreeString(&THIS->edit);
|
|
|
|
DELETE_MAP_free(&THIS->dmap);
|
|
|
|
GB.Unref(POINTER(&THIS->conn));
|
|
GB.Unref(POINTER(&THIS->fields));
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_PROPERTY(CRESULT_count)
|
|
|
|
GB.ReturnInteger(THIS->count);
|
|
|
|
END_PROPERTY
|
|
|
|
|
|
BEGIN_PROPERTY(CRESULT_max)
|
|
|
|
GB.ReturnInteger(THIS->count - 1);
|
|
|
|
END_PROPERTY
|
|
|
|
|
|
BEGIN_PROPERTY(CRESULT_index)
|
|
|
|
GB.ReturnInteger(THIS->pos);
|
|
|
|
END_PROPERTY
|
|
|
|
|
|
BEGIN_PROPERTY(CRESULT_available)
|
|
|
|
GB.ReturnBoolean(THIS->available);
|
|
|
|
END_PROPERTY
|
|
|
|
|
|
static void check_blob(CRESULT *_object, int field)
|
|
{
|
|
GB_VARIANT val;
|
|
|
|
//fprintf(stderr, "check_blob: %d\n", field);
|
|
|
|
if (THIS->buffer[field].type == GB_T_NULL)
|
|
{
|
|
val.type = GB_T_VARIANT;
|
|
val.value.type = (GB_TYPE)CLASS_Blob;
|
|
val.value.value._object = make_blob(THIS, field);
|
|
|
|
GB.StoreVariant(&val, &THIS->buffer[field]);
|
|
}
|
|
}
|
|
|
|
BEGIN_METHOD(CRESULT_get, GB_STRING field)
|
|
|
|
int index;
|
|
GB_TYPE type;
|
|
|
|
if (check_available(THIS))
|
|
return;
|
|
|
|
index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE);
|
|
if (index < 0)
|
|
return;
|
|
|
|
type = get_field_type(THIS, index);
|
|
|
|
if (type == DB_T_BLOB)
|
|
check_blob(THIS, index);
|
|
|
|
GB.ReturnPtr(GB_T_VARIANT, &THIS->buffer[index]);
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_METHOD(CRESULT_put, GB_VARIANT value; GB_STRING field)
|
|
|
|
int index;
|
|
GB_TYPE type;
|
|
|
|
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;
|
|
|
|
type = get_field_type(THIS, index);
|
|
|
|
if (type == DB_T_SERIAL)
|
|
{
|
|
//GB.Error("Read-only field");
|
|
return;
|
|
}
|
|
|
|
if (type == DB_T_BLOB)
|
|
{
|
|
check_blob(THIS, index);
|
|
|
|
if (VARG(value).type == (GB_TYPE)CLASS_Blob)
|
|
{
|
|
CBLOB *src = VARG(value).value._object;
|
|
set_blob((CBLOB *)THIS->buffer[index].value._object, src->data, src->length);
|
|
}
|
|
else
|
|
{
|
|
GB_STRING *str = (GB_STRING *)(void *)ARG(value);
|
|
|
|
if (GB.Conv((GB_VALUE *)(void *)ARG(value), GB_T_STRING))
|
|
return;
|
|
|
|
set_blob((CBLOB *)THIS->buffer[index].value._object, str->value.addr + str->value.start, str->value.len);
|
|
}
|
|
|
|
BARRAY_set(THIS->changed, index);
|
|
return;
|
|
}
|
|
|
|
if (VARG(value).type != GB_T_NULL && VARG(value).type != type)
|
|
{
|
|
if (GB.Conv((GB_VALUE *)(void *)ARG(value), THIS->info.field[index].type))
|
|
{
|
|
GB.Error("Type mismatch");
|
|
return;
|
|
}
|
|
|
|
GB.Conv((GB_VALUE *)(void *)ARG(value), GB_T_VARIANT);
|
|
}
|
|
|
|
GB.StoreVariant(ARG(value), &THIS->buffer[index]);
|
|
BARRAY_set(THIS->changed, 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)
|
|
|
|
GB.ReturnBoolean(load_buffer(THIS, 0));
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_METHOD_VOID(CRESULT_move_last)
|
|
|
|
GB.ReturnBoolean(load_buffer(THIS, THIS->count - 1));
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_METHOD_VOID(CRESULT_move_previous)
|
|
|
|
GB.ReturnBoolean(load_buffer(THIS, THIS->pos - 1));
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_METHOD_VOID(CRESULT_move_next)
|
|
|
|
GB.ReturnBoolean(load_buffer(THIS, THIS->pos + 1));
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_METHOD(CRESULT_move_to, GB_INTEGER pos)
|
|
|
|
GB.ReturnBoolean(load_buffer(THIS, VARG(pos)));
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_METHOD_VOID(CRESULT_next)
|
|
|
|
int *pos = (int *)GB.GetEnum();
|
|
|
|
if (!load_buffer(THIS, *pos))
|
|
(*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;
|
|
|
|
DB_CurrentDatabase = &THIS->conn->db;
|
|
|
|
q_init();
|
|
|
|
switch(THIS->mode)
|
|
{
|
|
case RESULT_CREATE:
|
|
|
|
q_add("INSERT INTO ");
|
|
q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table));
|
|
q_add(" ( ");
|
|
|
|
comma = FALSE;
|
|
for (i = 0; i < info->nfield; i++)
|
|
{
|
|
if (THIS->buffer[i].type == GB_T_NULL)
|
|
continue;
|
|
if (!BARRAY_test(THIS->changed, i))
|
|
continue;
|
|
if (comma) q_add(", ");
|
|
q_add(THIS->driver->GetQuote());
|
|
q_add(info->field[i].name);
|
|
q_add(THIS->driver->GetQuote());
|
|
comma = TRUE;
|
|
}
|
|
|
|
if (!comma)
|
|
{
|
|
q_add(THIS->driver->GetQuote());
|
|
q_add(info->field[0].name);
|
|
q_add(THIS->driver->GetQuote());
|
|
}
|
|
|
|
q_add(" ) VALUES ( ");
|
|
|
|
comma = FALSE;
|
|
for (i = 0; i < info->nfield; i++)
|
|
{
|
|
if (THIS->buffer[i].type == GB_T_NULL)
|
|
continue;
|
|
if (!BARRAY_test(THIS->changed, i))
|
|
continue;
|
|
if (comma) q_add(", ");
|
|
DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length);
|
|
comma = TRUE;
|
|
}
|
|
|
|
if (!comma)
|
|
{
|
|
DB_FormatVariant(THIS->driver, &THIS->buffer[0], q_add_length);
|
|
}
|
|
|
|
q_add(" )");
|
|
|
|
if (!THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot create record: &1"))
|
|
void_buffer(THIS);
|
|
|
|
break;
|
|
|
|
case RESULT_EDIT:
|
|
|
|
q_add("UPDATE ");
|
|
q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table));
|
|
q_add(" SET ");
|
|
|
|
comma = FALSE;
|
|
for (i = 0; i < info->nfield; i++)
|
|
{
|
|
if (!BARRAY_test(THIS->changed, i))
|
|
continue;
|
|
if (comma) q_add(", ");
|
|
q_add(THIS->driver->GetQuote());
|
|
q_add(THIS->info.field[i].name);
|
|
q_add(THIS->driver->GetQuote());
|
|
q_add(" = ");
|
|
DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length);
|
|
comma = TRUE;
|
|
}
|
|
|
|
q_add(" WHERE ");
|
|
q_add(THIS->edit);
|
|
|
|
THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot modify record: &1");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
GB.Error("Result is read-only");
|
|
break;
|
|
}
|
|
|
|
BARRAY_clear_all(THIS->changed, THIS->info.nfield);
|
|
|
|
END_METHOD
|
|
|
|
|
|
BEGIN_METHOD(CRESULT_delete, GB_BOOLEAN keep)
|
|
|
|
DB_INFO *info = &THIS->info;
|
|
int *pos;
|
|
|
|
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(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table));
|
|
q_add(" WHERE ");
|
|
q_add(THIS->edit);
|
|
|
|
THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot delete record: &1");
|
|
|
|
if (!VARGOPT(keep, FALSE))
|
|
{
|
|
DELETE_MAP_add(&THIS->dmap, THIS->pos);
|
|
THIS->count--;
|
|
reload_buffer(THIS);
|
|
|
|
GB.ListEnum(THIS);
|
|
while (!GB.NextEnum())
|
|
{
|
|
pos = (int *)GB.GetEnum();
|
|
if (*pos > THIS->pos)
|
|
(*pos)--;
|
|
}
|
|
}
|
|
|
|
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_PROPERTY_READ("Max", "i", CRESULT_max),
|
|
|
|
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, "[(Keep)b]"),
|
|
|
|
GB_PROPERTY_READ("Fields", ".Result.Fields", CRESULT_fields),
|
|
GB_PROPERTY_READ("Connection", "Connection", CRESULT_connection),
|
|
|
|
GB_END_DECLARE
|
|
};
|
|
|
|
/** Blob *******************************************************************/
|
|
|
|
/*static int check_blob(CBLOB *_object)
|
|
{
|
|
return check_result(BLOB->result) || (BLOB->result->pos != BLOB->pos);
|
|
}*/
|
|
|
|
static CBLOB *make_blob(CRESULT *result, int field)
|
|
{
|
|
CBLOB *_object;
|
|
|
|
_object = GB.New(CLASS_Blob, NULL, NULL);
|
|
|
|
//BLOB->result = result;
|
|
//GB.Ref(result);
|
|
|
|
//BLOB->field = field;
|
|
//BLOB->pos = result->pos;
|
|
BLOB->data = NULL;
|
|
BLOB->length = 0;
|
|
|
|
if (result->handle && result->pos >= 0)
|
|
result->driver->Result.Blob(result->handle, result->pos, field, BLOB);
|
|
|
|
//fprintf(stderr, "make_blob: %d (%ld) -> %p\n", field, BLOB->length, BLOB);
|
|
|
|
//GB.UnrefKeep(POINTER(&_object), FALSE);
|
|
return BLOB;
|
|
}
|
|
|
|
static void set_blob(CBLOB *_object, char *data, int length)
|
|
{
|
|
//fprintf(stderr, "set_blob: %p %ld\n", BLOB, length);
|
|
|
|
if (!BLOB->constant)
|
|
GB.FreeString((char **)&BLOB->data);
|
|
|
|
if (data && length)
|
|
{
|
|
*((char **)&BLOB->data) = GB.NewString(data, length);
|
|
BLOB->constant = FALSE;
|
|
}
|
|
|
|
BLOB->length = length;
|
|
}
|
|
|
|
BEGIN_METHOD_VOID(CBLOB_init)
|
|
|
|
CLASS_Blob = GB.FindClass("Blob");
|
|
|
|
END_METHOD
|
|
|
|
BEGIN_METHOD_VOID(CBLOB_free)
|
|
|
|
//GB.Unref(POINTER(&BLOB->result));
|
|
set_blob(BLOB, NULL, 0);
|
|
|
|
END_METHOD
|
|
|
|
/*BEGIN_PROPERTY(CBLOB_result)
|
|
|
|
GB.ReturnObject(BLOB->result);
|
|
|
|
END_PROPERTY*/
|
|
|
|
BEGIN_PROPERTY(CBLOB_data)
|
|
|
|
if (READ_PROPERTY)
|
|
{
|
|
if (BLOB->length)
|
|
GB.ReturnConstString(BLOB->data, BLOB->length);
|
|
else
|
|
GB.ReturnNull();
|
|
}
|
|
else
|
|
{
|
|
set_blob(BLOB, PSTRING(), PLENGTH());
|
|
}
|
|
|
|
END_PROPERTY
|
|
|
|
BEGIN_PROPERTY(CBLOB_length)
|
|
|
|
GB.ReturnInteger(BLOB->length);
|
|
|
|
END_PROPERTY
|
|
|
|
|
|
GB_DESC CBlobDesc[] =
|
|
{
|
|
GB_DECLARE("Blob", sizeof(CBLOB)), GB_NOT_CREATABLE(),
|
|
|
|
//GB_HOOK_CHECK(check_blob),
|
|
|
|
GB_STATIC_METHOD("_init", NULL, CBLOB_init, NULL),
|
|
GB_METHOD("_free", NULL, CBLOB_free, NULL),
|
|
|
|
//GB_PROPERTY_READ("Result", "Result", CBLOB_result),
|
|
GB_PROPERTY("Data", "s", CBLOB_data),
|
|
GB_PROPERTY_READ("Length", "i", CBLOB_length),
|
|
//GB_METHOD("_unknown", "v", CBLOB_unknown, "v"),
|
|
|
|
GB_END_DECLARE
|
|
};
|
|
|
|
|