gambas-source-code/main/lib/db/main.c
Benoît Minisini 7f12844128 [DEVELOPMENT ENVIRONMENT]
* NEW: Support for template array classes in automatic completion.

[INTERPRETER]
* BUG: LIE INPUT and INPUT work correctly on tty streams.

[GB.DB]
* NEW: Add an API that allows a driver function to get the current 
  database when it does not get it from its arguments.

[GB.DB.POSTGRESQL]
* BUG: Fix blob support for some encodings, and when PostgreSQL version is
  greater or equal than 8.2.

[GB.DB.SQLITE3]
* BUG: Field values are not truncated anymore.

[GB.FORM]
* NEW: MessageLabel is a new control made by David Villalobos Cambronero.
  It is a TextLabel that changes its background color for indicating a 
  warning, an error, and so on.

[GB.FORM.MDI]
* NEW: Workspace.ButtonPosition is a new property that allows to put the 
  tab close button on the left or on the right.

[GB.GTK]
* BUG: Speeds up the combo-box control as much as I can. Slowness seems to 
  be a GTK+ feature. :-/

[GB.QT]
* BUG: Fix crash when setting a shortcut on a top-level menu.


git-svn-id: svn://localhost/gambas/trunk@1548 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2008-09-09 11:03:47 +00:00

553 lines
9.4 KiB
C

/***************************************************************************
main.c
The database component
(c) 2000-2007 Benoit 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 __MAIN_C
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "gb_common.h"
#include "CConnection.h"
#include "CDatabase.h"
#include "CUser.h"
#include "CTable.h"
#include "CField.h"
#include "CIndex.h"
#include "CResult.h"
#include "CResultField.h"
#include "sqlite.h"
#include "main.h"
GB_INTERFACE GB EXPORT;
static DB_DRIVER *_drivers[MAX_DRIVER];
static int _drivers_count = 0;
static char *_query = NULL;
static bool _debug = FALSE;
static const char *_try_another = NULL;
DB_DATABASE *DB_CurrentDatabase = NULL;
int DB_CheckNameWith(const char *name, const char *msg, const char *more)
{
unsigned char c;
const char *p = name;
if (!name || !*name)
{
GB.Error("Void &1 name", msg);
return TRUE;
}
while ((c = *p++))
{
if ((c >= 'A' && c <='Z') || (c >= 'a' && c <='z') || (c >= '0' && c <= '9') || c == '_')
continue;
if (more && index(more, c))
continue;
GB.Error("Bad &1 name: &2", msg, name);
return TRUE;
}
return FALSE;
}
// void DB_LowerString(char *s)
// {
// register char c;
//
// for(;;)
// {
// c = *s;
// if (!c)
// return;
// *s++ = tolower(c);
// }
// }
void DB_FreeStringArray(char ***parray)
{
int i;
char **array = *parray;
if (!*parray)
return;
for (i = 0; i < GB.Count(array); i++)
GB.FreeString(&array[i]);
GB.FreeArray(parray);
}
GB_ARRAY DB_StringArrayToGambasArray(char **array)
{
GB_ARRAY garray;
int i, n;
char *str;
n = GB.Count(array);
GB.Array.New(&garray, GB_T_STRING, n);
for (i = 0; i < n; i++)
{
GB.NewString(&str, array[i], 0);
*((char **)GB.Array.Get(garray, i)) = str;
}
return garray;
}
int DB_FindStringArray(char **array, const char *elt)
{
int i;
for (i = 0; i < GB.Count(array); i++)
{
if (!strcasecmp(elt, array[i]))
return i;
}
return -1;
}
static void DB_Register(DB_DRIVER *driver)
{
if (_drivers_count >= MAX_DRIVER)
return;
_drivers[_drivers_count] = driver;
_drivers_count++;
}
void DB_TryAnother(const char *driver)
{
_try_another = driver;
}
static DB_DRIVER *DB_GetDriver(const char *type)
{
int i;
char comp[type ? strlen(type) + 8 : 1];
if (!type)
{
GB.Error("Driver name missing");
return NULL;
}
strcpy(comp, "gb.db.");
strcat(comp, type);
GB.LoadComponent(comp);
GB.Error(NULL); // reset the error flag;
for (i = 0; i < _drivers_count; i++)
{
if (strcasecmp(_drivers[i]->name, type) == 0)
return _drivers[i];
}
GB.Error("Cannot find driver for database: &1", type);
return NULL;
}
bool DB_Open(DB_DESC *desc, DB_DRIVER **driver, DB_DATABASE *db)
{
DB_DRIVER *d;
int res;
const char *type = desc->type;
CLEAR(db);
for(;;)
{
d = DB_GetDriver(type);
if (!d)
return TRUE;
*driver = d;
_try_another = NULL;
res = (*d->Open)(desc, db);
if (!res)
return FALSE;
if (!_try_another)
return TRUE;
type = _try_another;
}
}
void DB_Format(DB_DRIVER *driver, GB_VALUE *arg, DB_FORMAT_CALLBACK add)
{
static char buffer[32];
char *s;
int l;
int i;
if (arg->type == GB_T_VARIANT)
GB.Conv(arg, ((GB_VARIANT *)arg)->value.type);
if (arg->type == (GB_TYPE)CLASS_Blob)
{
(*driver->FormatBlob)((DB_BLOB *)(((GB_OBJECT *)arg)->value), add);
return;
}
if (!(*driver->Format)(arg, add))
{
switch (arg->type)
{
case GB_T_BOOLEAN:
if (VALUE((GB_BOOLEAN *)arg))
add("TRUE", 4);
else
add("FALSE", 5);
return;
case GB_T_BYTE:
case GB_T_SHORT:
case GB_T_INTEGER:
l = sprintf(buffer, "%d", VALUE((GB_INTEGER *)arg));
add(buffer, l);
return;
case GB_T_LONG:
l = sprintf(buffer, "%" PRId64, VALUE((GB_LONG *)arg));
add(buffer, l);
return;
case GB_T_FLOAT:
GB.NumberToString(FALSE, VALUE((GB_FLOAT *)arg), NULL, &s, &l);
add(s, l);
return;
case GB_T_STRING:
case GB_T_CSTRING:
s = VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start;
l = VALUE((GB_STRING *)arg).len;
add("'", 1);
for (i = 0; i < l; i++, s++)
{
add(s, 1);
if (*s == '\'' || *s == '\\')
add(s, 1);
}
add("'", 1);
return;
case GB_T_NULL:
add("NULL", 4);
return;
default:
fprintf(stderr, "gb.db: DB_Format: unsupported datatype: %d\n", (int)arg->type);
return;
}
}
}
void DB_FormatVariant(DB_DRIVER *driver, GB_VARIANT_VALUE *arg, DB_FORMAT_CALLBACK add)
{
GB_VALUE value;
value.type = arg->type;
switch(arg->type)
{
case GB_T_NULL:
break;
case GB_T_STRING:
case GB_T_CSTRING:
{
GB_STRING *val = (GB_STRING *)(void *)&value;
val->value.addr = arg->_string.value;
val->value.start = 0;
if (arg->type == GB_T_STRING)
val->value.len = GB.StringLength(arg->_string.value);
else
val->value.len = strlen(arg->_string.value);
}
break;
default:
// WARNING: That works only if the data value is stored just after the type
// inside the GB_VALUE structure! Yet another stupid hack I should fix...
memcpy(&value, arg, sizeof(GB_VARIANT_VALUE));
break;
}
DB_Format(driver, &value, add);
}
static int query_narg;
static GB_VALUE *query_arg;
static DB_DRIVER *query_driver;
static void mq_add_param(int index)
{
if (index < 1 || index > query_narg)
return;
DB_Format(query_driver, &query_arg[index - 1], (DB_FORMAT_CALLBACK)GB.SubstAdd);
}
char *DB_MakeQuery(DB_DRIVER *driver, const char *pattern, int len, int narg, GB_VALUE *arg)
{
char *query;
query_narg = narg;
query_arg = arg;
query_driver = driver;
if (narg == 0)
GB.TempString(&query, pattern, len);
else
query = GB.SubstStringAdd(pattern, len, mq_add_param);
if (!query || *query == 0)
{
GB.Error("Void query");
return NULL;
}
else
return query;
}
void q_init(void)
{
GB.FreeString(&_query);
_query = NULL;
}
void q_add(const char *str)
{
if (str)
GB.AddString(&_query, str, 0);
}
void q_add_length(const char *str, int len)
{
if (str)
GB.AddString(&_query, str, len);
}
char *q_get(void)
{
return _query;
}
char *q_steal(void)
{
char *query = _query;
_query = NULL;
return query;
}
int q_length(void)
{
return GB.StringLength(_query);
}
void DB_SetDebug(int debug)
{
_debug = debug;
}
int DB_IsDebug(void)
{
return _debug;
}
static char *_quote;
DB_SUBST_CALLBACK _quote_cb;
static void ss_get_param(int index, char **str, int *len)
{
(*_quote_cb)(index, str, len, _quote[index]);
}
char *DB_SubstString(const char *pattern, int len_pattern, DB_SUBST_CALLBACK add)
{
int i;
char last_c, c;
int n;
char quote[20] = {0};
c = 0;
len_pattern--;
for (i = 0; i < len_pattern; i++)
{
last_c = c;
c = pattern[i];
if (c == '&')
{
c = pattern[++i];
if (c == '&')
continue;
if (isdigit(c))
{
n = c - '0';
c = pattern[++i];
if (isdigit(c))
{
n = n * 10 + c - '0';
i++;
}
quote[n] = last_c;
}
}
}
_quote_cb = add;
_quote = quote;
return GB.SubstString(pattern, len_pattern, ss_get_param);
}
char *DB_QuoteString(const char *str, int len, char quote)
{
char *res, *p, c;
int len_res;
int i;
len_res = len;
for (i = 0; i < len; i++)
{
if (str[i] == quote)
len_res++;
}
GB.TempString(&res, NULL, len_res);
p = res;
for (i = 0; i < len; i++)
{
c = str[i];
*p++ = c;
if (c == quote || c == '\\')
*p++ = c;
}
*p = 0;
return res;
}
DB_DATABASE *DB_GetCurrent()
{
return DB_CurrentDatabase;
}
GB_DESC *GB_CLASSES [] EXPORT =
{
CIndexDesc,
CFieldDesc,
CTableFieldsDesc,
CTableIndexesDesc,
CTableDesc,
CUserDesc,
CDatabaseDesc,
CConnectionUsersDesc,
CConnectionDatabasesDesc,
CConnectionTablesDesc,
CConnectionDesc,
CDBDesc,
CBlobDesc,
CResultFieldDesc,
CResultFieldsDesc,
CResultDesc,
NULL
};
void *GB_DB_1[] EXPORT = {
(void *)1,
(void *)DB_Register,
(void *)DB_Format,
(void *)DB_FormatVariant,
(void *)DB_IsDebug,
(void *)DB_TryAnother,
(void *)DB_SubstString,
(void *)DB_QuoteString,
(void *)DB_GetCurrent,
(void *)q_init,
(void *)q_add,
(void *)q_add_length,
(void *)q_get,
(void *)q_steal,
(void *)q_length,
(void *)DB_FindStringArray,
NULL
};
int EXPORT GB_INIT(void)
{
DB_Register(&DB_sqlite_pseudo_driver);
return 0;
}
void EXPORT GB_EXIT()
{
GB.FreeString(&_query);
}