dd3b512daf
* NEW: Update copyrights. git-svn-id: svn://localhost/gambas/trunk@4384 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2471 lines
60 KiB
C++
2471 lines
60 KiB
C++
/***************************************************************************
|
|
|
|
main.cpp
|
|
|
|
(c) 2000-2012 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 __MAIN_C
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <libgen.h> /* For basename function */
|
|
#include <pwd.h> /* For passwd file functions */
|
|
#include <grp.h> /* For group file functions */
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include "sqlitedataset.h"
|
|
|
|
#include "main.h"
|
|
|
|
extern "C" {
|
|
GB_INTERFACE GB EXPORT;
|
|
DB_INTERFACE DB EXPORT;
|
|
} //end extern "C"
|
|
|
|
static char _buffer[32];
|
|
|
|
static int _print_query = FALSE;
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
The driver interface
|
|
|
|
*****************************************************************************/
|
|
|
|
DECLARE_DRIVER(_driver, "sqlite2");
|
|
|
|
|
|
/* Internal function to convert a database type into a Gambas type */
|
|
|
|
static GB_TYPE conv_type(int type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ft_Boolean:
|
|
return GB_T_BOOLEAN;
|
|
case ft_Short:
|
|
case ft_UShort:
|
|
case ft_Long:
|
|
case ft_ULong:
|
|
return GB_T_INTEGER;
|
|
|
|
case ft_Float:
|
|
case ft_Double:
|
|
return GB_T_FLOAT;
|
|
|
|
case ft_LongDouble:
|
|
return GB_T_LONG;
|
|
|
|
case ft_Date:
|
|
return GB_T_DATE;
|
|
|
|
case ft_String:
|
|
case ft_WideString:
|
|
case ft_Char:
|
|
case ft_WChar:
|
|
return GB_T_STRING;
|
|
|
|
default:
|
|
return GB_T_STRING;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Internal function to convert a database value into a Gambas variant value */
|
|
|
|
static void conv_data(const char *data, GB_VARIANT_VALUE *val, fType type)
|
|
{
|
|
GB_VALUE conv;
|
|
GB_DATE_SERIAL date;
|
|
double sec;
|
|
|
|
switch (type)
|
|
{
|
|
case ft_Boolean:
|
|
|
|
val->type = GB_T_BOOLEAN;
|
|
if (data[0] == 't' || data[0] == 'T')
|
|
val->value._boolean = -1;
|
|
else
|
|
val->value._boolean = atoi(data) ? -1 : 0;
|
|
break;
|
|
|
|
case ft_Short:
|
|
case ft_UShort:
|
|
case ft_Long:
|
|
case ft_ULong:
|
|
|
|
GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv);
|
|
|
|
val->type = GB_T_INTEGER;
|
|
val->value._integer = conv._integer.value;
|
|
|
|
break;
|
|
|
|
case ft_LongDouble:
|
|
|
|
GB.NumberFromString(GB_NB_READ_LONG, data, strlen(data), &conv);
|
|
|
|
val->type = GB_T_LONG;
|
|
val->value._long = conv._long.value;
|
|
|
|
break;
|
|
|
|
case ft_Float:
|
|
case ft_Double:
|
|
|
|
GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv);
|
|
|
|
val->type = GB_T_FLOAT;
|
|
val->value._float = conv._float.value;
|
|
|
|
break;
|
|
|
|
case ft_Date:
|
|
|
|
memset(&date, 0, sizeof(date));
|
|
|
|
switch (strlen(data))
|
|
{
|
|
case 14:
|
|
sscanf(data, "%4hu%2hu%2hu%2hu%2hu%lf", &date.year, &date.month,
|
|
&date.day, &date.hour, &date.min, &sec);
|
|
date.sec = (short) sec;
|
|
date.msec = (short) ((sec - date.sec) * 1000 + 0.5);
|
|
break;
|
|
case 12:
|
|
sscanf(data, "%2hu%2hu%2hu%2hu%2hu%lf", &date.year, &date.month,
|
|
&date.day, &date.hour, &date.min, &sec);
|
|
date.sec = (short) sec;
|
|
date.msec = (short) ((sec - date.sec) * 1000 + 0.5);
|
|
break;
|
|
case 10:
|
|
if (sscanf(data, "%4hu-%2hu-%2hu", &date.year, &date.month,
|
|
&date.day) != 3)
|
|
{
|
|
if (sscanf(data, "%4hu/%2hu/%2hu", &date.year, &date.month,
|
|
&date.day) != 3)
|
|
{
|
|
if (sscanf(data, "%4hu:%2hu:%lf", &date.hour, &date.min,
|
|
&sec) == 3)
|
|
{
|
|
date.sec = (short) sec;
|
|
date.msec = (short) ((sec - date.sec) * 1000 + 0.5);
|
|
}
|
|
else
|
|
{
|
|
sscanf(data, "%2hu%2hu%2hu%2hu%2hu", &date.year,
|
|
&date.month, &date.day, &date.hour, &date.min);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
case 8:
|
|
if (sscanf(data, "%4hu%2hu%2hu", &date.year, &date.month,
|
|
&date.day) != 3)
|
|
{
|
|
sscanf(data, "%2hu/%2hu/%2hu", &date.year, &date.month,
|
|
&date.day);
|
|
}
|
|
break;
|
|
case 6:
|
|
sscanf(data, "%2hu%2hu%2hu", &date.year, &date.month, &date.day);
|
|
break;
|
|
case 4:
|
|
sscanf(data, "%2hu%2hu", &date.year, &date.month);
|
|
break;
|
|
case 2:
|
|
sscanf(data, "%2hu", &date.year);
|
|
break;
|
|
default:
|
|
sscanf(data, "%4hu-%2hu-%2hu %2hu:%2hu:%lf", &date.year,
|
|
&date.month, &date.day, &date.hour, &date.min, &sec);
|
|
date.sec = (short) sec;
|
|
date.msec = (short) ((sec - date.sec) * 1000 + 0.5);
|
|
}
|
|
if (date.year < 100)
|
|
date.year += 1900;
|
|
|
|
GB.MakeDate(&date, (GB_DATE *) & conv);
|
|
|
|
val->type = GB_T_DATE;
|
|
val->value._date.date = conv._date.value.date;
|
|
val->value._date.time = conv._date.value.time;
|
|
|
|
break;
|
|
|
|
case ft_String:
|
|
case ft_WideString:
|
|
case ft_Char:
|
|
case ft_WChar:
|
|
default:
|
|
|
|
val->type = GB_T_CSTRING;
|
|
val->value._string = (char *)data;
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/* Internal function to substitute the table name into a query */
|
|
|
|
static char *query_param[3];
|
|
|
|
static void query_get_param(int index, char **str, int *len, char quote)
|
|
{
|
|
if (index > 3)
|
|
return;
|
|
|
|
index--;
|
|
*str = query_param[index];
|
|
*len = strlen(*str);
|
|
|
|
if (quote == '\'')
|
|
{
|
|
*str = DB.QuoteString(*str, *len, quote);
|
|
*len = GB.StringLength(*str);
|
|
}
|
|
}
|
|
|
|
/* Internal function to run a query */
|
|
|
|
static int do_query(DB_DATABASE *db, const char *error, Dataset **pres,
|
|
const char *qtemp, int nsubst, ...)
|
|
{
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
va_list args;
|
|
int i;
|
|
const char *query;
|
|
Dataset *res = conn->CreateDataset();
|
|
int ret;
|
|
|
|
if (nsubst)
|
|
{
|
|
va_start(args, nsubst);
|
|
if (nsubst > 3)
|
|
nsubst = 3;
|
|
for (i = 0; i < nsubst; i++)
|
|
query_param[i] = va_arg(args, char *);
|
|
|
|
query = DB.SubstString(qtemp, 0, query_get_param);
|
|
}
|
|
else
|
|
query = qtemp;
|
|
|
|
if (_print_query)
|
|
{
|
|
_print_query = FALSE;
|
|
}
|
|
|
|
if (DB.IsDebug())
|
|
fprintf(stderr, "sqlite2: %p: %s\n", conn, query);
|
|
|
|
if (strncasecmp("select",query,6) == 0){
|
|
|
|
if(res->query(query)){
|
|
ret = FALSE;
|
|
if (pres){
|
|
*pres = res;
|
|
}
|
|
}
|
|
else {
|
|
ret = TRUE;
|
|
GB.Error(error, conn->getErrorMsg());
|
|
}
|
|
}
|
|
else {
|
|
if(res->exec(query)){
|
|
ret = FALSE;
|
|
if (pres){
|
|
*pres = res;
|
|
}
|
|
}
|
|
else {
|
|
ret = TRUE;
|
|
GB.Error(error, conn->getErrorMsg());
|
|
}
|
|
}
|
|
|
|
if (!pres)
|
|
res->close();
|
|
|
|
if (ret)
|
|
db->error = conn->lastError();
|
|
else
|
|
db->error = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Internal function to check whether a file is a sqlite database file */
|
|
static bool IsDatabaseFile (const char *filename)
|
|
{
|
|
/* SQLite databases start with the string:
|
|
* ** This file contains an SQLite 2.1 database **
|
|
* */
|
|
FILE* fp;
|
|
bool res;
|
|
char magic_text[48];
|
|
|
|
fp = fopen(filename, "r");
|
|
if (!fp)
|
|
return false;
|
|
|
|
res = fread(magic_text, 1, 47, fp) == 47;
|
|
fclose(fp);
|
|
|
|
if (!res)
|
|
return false;
|
|
|
|
magic_text[47] = '\0';
|
|
|
|
if (strcmp(magic_text, "** This file contains an SQLite 2.1 database **"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Internal function to locate database and return full qualified */
|
|
/* path. */
|
|
static char *FindDatabase (const char *name, const char *hostName)
|
|
{
|
|
char *dbhome = NULL;
|
|
char *fullpath = NULL;
|
|
|
|
/* Does Name includes fullpath */
|
|
if (*name == '/')
|
|
{
|
|
if (IsDatabaseFile(name))
|
|
fullpath = GB.NewZeroString(name);
|
|
|
|
return fullpath;
|
|
}
|
|
|
|
/* Hostname contains home area */
|
|
fullpath = GB.NewZeroString(hostName);
|
|
fullpath = GB.AddChar(fullpath, '/');
|
|
fullpath = GB.AddString(fullpath, name, 0);
|
|
if (IsDatabaseFile(fullpath))
|
|
return fullpath;
|
|
|
|
GB.FreeString(&fullpath);
|
|
|
|
/* Check the GAMBAS_SQLITE_DBHOME setting */
|
|
dbhome = getenv("GAMBAS_SQLITE_DBHOME");
|
|
if (dbhome)
|
|
{
|
|
fullpath = GB.NewZeroString(dbhome);
|
|
fullpath = GB.AddChar(fullpath, '/');
|
|
fullpath = GB.AddString(fullpath, name, 0);
|
|
|
|
if (IsDatabaseFile(fullpath))
|
|
return fullpath;
|
|
}
|
|
|
|
#if 0
|
|
/* Now check for database in current working directory */
|
|
if (getcwd(cwd, MAX_PATH) == NULL){
|
|
GB.Error("Unable to get databases: &1", "Can't find current directory");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
fullpath = GB.NewZeroString(GB.TempDir());
|
|
fullpath = GB.AddString(fullpath, "/sqlite/", 0);
|
|
fullpath = GB.AddString(fullpath, name, 0);
|
|
|
|
if (IsDatabaseFile(fullpath))
|
|
return fullpath;
|
|
|
|
GB.FreeString(&fullpath);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Internal function to return database home directory */
|
|
/* - GAMBAS_SQLITE_HOMEDB if set or temporary directory /tmp/gambas.%pid%/ */
|
|
|
|
static char *GetDatabaseHome()
|
|
{
|
|
char *env = NULL;
|
|
char *dbhome = NULL;
|
|
|
|
GB.Alloc(POINTER(&dbhome), MAX_PATH);
|
|
|
|
/* Check for Environment variable */
|
|
|
|
env = getenv("GAMBAS_SQLITE_DBHOME");
|
|
|
|
/* if not set then set to current working directory */
|
|
if (env == NULL){
|
|
/*
|
|
if (getcwd(dbhome, MAX_PATH) == NULL){
|
|
GB.Error("Unable to get databases: &1", "Can't find current directory");
|
|
GB.Free((void **)&dbhome);
|
|
return NULL;
|
|
}
|
|
*/
|
|
|
|
sprintf(dbhome, "%s/sqlite", GB.TempDir());
|
|
}
|
|
else {
|
|
strcpy(dbhome, env);
|
|
}
|
|
|
|
return dbhome;
|
|
}
|
|
|
|
|
|
// Internal function to walk a directory and list files
|
|
// Used by database_list
|
|
|
|
static int WalkDirectory(const char *dir, char ***databases)
|
|
{
|
|
DIR *dp;
|
|
struct dirent *entry;
|
|
struct stat statbuf;
|
|
char cwd[MAX_PATH];
|
|
|
|
if ((dp = opendir(dir)) == NULL)
|
|
return -1;
|
|
|
|
if (getcwd(cwd, MAX_PATH) == NULL)
|
|
{
|
|
fprintf(stderr, "gb.db.sqlite2: warning: getcwd: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (chdir(dir))
|
|
{
|
|
fprintf(stderr, "gb.db.sqlite2: warning: chdir: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
while ((entry = readdir(dp)) != NULL)
|
|
{
|
|
stat(entry->d_name, &statbuf);
|
|
|
|
if (S_ISREG(statbuf.st_mode))
|
|
{
|
|
if (IsDatabaseFile(entry->d_name))
|
|
*(char **)GB.Add(databases) = GB.NewZeroString(entry->d_name);
|
|
}
|
|
}
|
|
|
|
// BM: you must call closedir()
|
|
closedir(dp);
|
|
|
|
if (chdir(cwd))
|
|
fprintf(stderr, "gb.db.sqlite2: warning: chdir: %s\n", strerror(errno));
|
|
|
|
return GB.Count(databases);
|
|
}
|
|
|
|
/* Internal function to check database version number */
|
|
int db_version()
|
|
{
|
|
//Check db version
|
|
int dbversion =0;
|
|
unsigned int verMain, verMajor, verMinor;
|
|
sscanf(sqlite_version,"%2u.%2u.%2u", &verMain, &verMajor, &verMinor);
|
|
dbversion = ((verMain * 10000) + (verMajor * 100) + verMinor);
|
|
return dbversion;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
get_quote()
|
|
|
|
Returns the character used for quoting object names.
|
|
|
|
*****************************************************************************/
|
|
|
|
static const char *get_quote(void)
|
|
{
|
|
return QUOTE_STRING;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
open_database()
|
|
|
|
Connect to a database.
|
|
|
|
<desc> points at a structure describing each connection parameter.
|
|
|
|
In Sqlite, there is no such thing as a host. If this is set then check
|
|
to see whether this is actually a path to a home area. NG 01/04/04
|
|
|
|
This function must return a database handle, or NULL if the connection
|
|
has failed.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int open_database(DB_DESC *desc, DB_DATABASE *db)
|
|
{
|
|
SqliteDatabase *conn = new SqliteDatabase();
|
|
char *name = NULL;
|
|
char *db_fullpath = NULL;
|
|
bool memory;
|
|
|
|
/* connect by default to memory database */
|
|
|
|
if (desc->name)
|
|
{
|
|
name = GB.NewZeroString(desc->name);
|
|
memory = false;
|
|
}
|
|
else
|
|
{
|
|
name = GB.NewZeroString(":memory:");
|
|
memory = true;
|
|
}
|
|
|
|
if (desc->host)
|
|
conn->setHostName(desc->host);
|
|
|
|
if (memory)
|
|
{
|
|
conn->setDatabase(name);
|
|
}
|
|
else if ((db_fullpath = FindDatabase(name, conn->getHostName())) != NULL)
|
|
{
|
|
conn->setDatabase(db_fullpath);
|
|
}
|
|
else
|
|
{
|
|
GB.Error("Unable to locate database: &1", name);
|
|
GB.FreeString(&name);
|
|
delete conn;
|
|
return TRUE;
|
|
}
|
|
|
|
GB.FreeString(&name);
|
|
GB.FreeString(&db_fullpath);
|
|
|
|
if ( conn->connect() != DB_CONNECTION_OK)
|
|
{
|
|
GB.Error("Cannot open database: &1", conn->getErrorMsg());
|
|
conn->disconnect();
|
|
delete conn;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Character set cannot be set for sqlite. A sqlite re-compile
|
|
* is required. */
|
|
db->charset = GB.NewZeroString(strcmp(sqlite_encoding, "iso8859") == 0 ? "ISO-8859-1" : "UTF-8");
|
|
|
|
/* set dbversion */
|
|
db->version = db_version();
|
|
|
|
/* flags */
|
|
db->flags.no_table_type = TRUE;
|
|
db->flags.no_serial = TRUE;
|
|
db->flags.no_blob = TRUE;
|
|
db->flags.no_nest = TRUE;
|
|
|
|
db->db_name_char = ".";
|
|
|
|
db->handle = conn;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
close_database()
|
|
|
|
Terminates the database connection.
|
|
|
|
<handle> contains the database handle.
|
|
|
|
*****************************************************************************/
|
|
|
|
static void close_database(DB_DATABASE *db)
|
|
{
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
|
|
if (conn)
|
|
{
|
|
conn->disconnect();
|
|
delete conn;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
format_value()
|
|
|
|
This function transforms a gambas value into a string value that can
|
|
be inserted into a SQL query.
|
|
|
|
<arg> points to the value.
|
|
<add> is a callback called to insert the string into the query.
|
|
|
|
This function must return TRUE if it translates the value, and FALSE if
|
|
it does not.
|
|
|
|
If the value is not translated, then a default translation is used.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int format_value(GB_VALUE *arg, DB_FORMAT_CALLBACK add)
|
|
{
|
|
int l;
|
|
GB_DATE_SERIAL *date;
|
|
|
|
switch (arg->type)
|
|
{
|
|
case GB_T_BOOLEAN:
|
|
/*Note this is likely to go to a tinyint */
|
|
if (VALUE((GB_BOOLEAN *)arg))
|
|
add("'1'", 3);
|
|
else
|
|
add("'0'", 3);
|
|
return TRUE;
|
|
|
|
case GB_T_STRING:
|
|
case GB_T_CSTRING:
|
|
|
|
return FALSE; // default
|
|
|
|
case GB_T_DATE:
|
|
|
|
date = GB.SplitDate((GB_DATE *)arg);
|
|
|
|
l = sprintf(_buffer, "'%04d-%02d-%02d %02d:%02d:%02d",
|
|
date->year, date->month, date->day,
|
|
date->hour, date->min, date->sec);
|
|
|
|
add(_buffer, l);
|
|
|
|
if (date->msec)
|
|
{
|
|
l = sprintf(_buffer, ".%03d", date->msec);
|
|
add(_buffer, l);
|
|
}
|
|
|
|
add("'", 1);
|
|
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
format_blob()
|
|
|
|
This function transforms a blob value into a string value that can
|
|
be inserted into a SQL query.
|
|
|
|
<blob> points to the DB_BLOB structure.
|
|
<add> is a callback called to insert the string into the query.
|
|
|
|
*****************************************************************************/
|
|
|
|
static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add)
|
|
{
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
exec_query()
|
|
|
|
Send a query to the server and gets the result.
|
|
|
|
<handle> is the database handle, as returned by open_database()
|
|
<query> is the query string.
|
|
<result> will receive the result handle of the query.
|
|
<err> is an error message used when the query failed.
|
|
|
|
<result> can be NULL, when we don't care getting the result.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err)
|
|
{
|
|
return do_query(db, err, (Dataset **)result, query, 0);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
query_init()
|
|
|
|
Initialize an info structure from a query result.
|
|
|
|
<result> is the handle of the query result.
|
|
<info> points to the info structure.
|
|
<count> will receive the number of records returned by the query.
|
|
|
|
This function must initialize the info->nfield field with the number of
|
|
field in the query result.
|
|
|
|
*****************************************************************************/
|
|
|
|
static void query_init(DB_RESULT result, DB_INFO *info, int *count)
|
|
{
|
|
Dataset *res = (Dataset *)result;
|
|
|
|
if(res)
|
|
{
|
|
*count = res->num_rows();
|
|
info->nfield = res->fieldCount();
|
|
}
|
|
else {
|
|
*count = 0;
|
|
info->nfield = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
query_release()
|
|
|
|
Free the info structure filled by query_init() and the result handle.
|
|
|
|
<result> is the handle of the query result.
|
|
<info> points to the info structure.
|
|
|
|
*****************************************************************************/
|
|
|
|
static void query_release(DB_RESULT result, DB_INFO *info)
|
|
{
|
|
((Dataset *)result)->close();
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
query_fill()
|
|
|
|
Fill a result buffer with the value of each field of a record.
|
|
|
|
<db> is the database handle, as returned by open_database()
|
|
<result> is the handle of the result.
|
|
<pos> is the index of the record in the result.
|
|
<buffer> points to an array having one element for each field in the
|
|
result.
|
|
<next> is a boolean telling if we want the next row.
|
|
|
|
This function must use GB.StoreVariant() to store the value in the
|
|
buffer.
|
|
|
|
*****************************************************************************/
|
|
|
|
#if 0
|
|
static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next)
|
|
{
|
|
Dataset *res = (Dataset *)result;
|
|
int i;
|
|
char *data;
|
|
GB_VARIANT value;
|
|
|
|
if (!next)
|
|
res->seek(pos);/* move to record */
|
|
else
|
|
res->next();
|
|
|
|
for ( i=0; i < res->fieldCount(); i++)
|
|
{
|
|
GB.NewZeroString( &data, res->fv(res->fieldName(i)).get_asString().data());
|
|
|
|
value.type = GB_T_VARIANT;
|
|
value.value._object.type = GB_T_NULL;
|
|
|
|
//if (field->type != FIELD_TYPE_NULL)
|
|
if (data){
|
|
conv_data(data, &value.value, (fType) res->fieldType(i));
|
|
}
|
|
|
|
GB.FreeString(&data);
|
|
GB.StoreVariant(&value, &buffer[i]);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE * buffer, int next)
|
|
{
|
|
Dataset *res = (Dataset *) result;
|
|
int i;
|
|
const char *data;
|
|
GB_VARIANT value;
|
|
|
|
if (!next)
|
|
res->seek(pos);
|
|
else
|
|
res->next();
|
|
|
|
for (i = 0; i < res->fieldCount(); i++)
|
|
{
|
|
if (res->fv(i).get_isNull())
|
|
data = NULL;
|
|
else
|
|
data = res->fv(i).get_asString().data();
|
|
|
|
//fprintf(stderr, "query_fill: %d.%d %s\n", pos, i, data);
|
|
|
|
value.type = GB_T_VARIANT;
|
|
value.value.type = GB_T_NULL;
|
|
|
|
//if (field->type != FIELD_TYPE_NULL)
|
|
if (data)
|
|
conv_data(data, &value.value, (fType) res->fieldType(i));
|
|
|
|
//GB.FreeString(&data);
|
|
GB.StoreVariant(&value, &buffer[i]);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
blob_read()
|
|
|
|
Returns the value of a BLOB field.
|
|
|
|
<result> is the handle of the result.
|
|
<pos> is the index of the record in the result.
|
|
<blob> points at a DB_BLOB structure that will receive a pointer to the
|
|
data and its length.
|
|
|
|
NOTE: this function is always called after query_fill() with the same
|
|
value of <pos>.
|
|
|
|
*****************************************************************************/
|
|
|
|
static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob)
|
|
{
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
field_name()
|
|
|
|
Return the name of a field in a result from its index.
|
|
|
|
<result> is the result handle.
|
|
<field> is the field index.
|
|
|
|
*****************************************************************************/
|
|
|
|
static char *field_name(DB_RESULT result, int field)
|
|
{
|
|
return (char *)(((Dataset *)result)->fieldName(field));
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
field_index()
|
|
|
|
Return the index of a field in a result from its name.
|
|
|
|
<Result> is the result handle.
|
|
<name> is the field name.
|
|
<handle> can be ignored by this driver.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int field_index(DB_RESULT result, const char *name, DB_DATABASE *db)
|
|
{
|
|
char *fld;
|
|
|
|
fld = strchr((char *)name, (int)FLD_SEP);
|
|
if (fld){ //Includes table identity
|
|
fld[0] = '.';
|
|
}
|
|
return (((Dataset *)result)->fieldIndex(name));
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
field_type()
|
|
|
|
Return the Gambas type of a field in a result from its index.
|
|
|
|
<result> is the result handle.
|
|
<field> is the field index.
|
|
|
|
*****************************************************************************/
|
|
|
|
static GB_TYPE field_type(DB_RESULT result, int field)
|
|
{
|
|
return conv_type(((Dataset *)result)->fieldType(field));
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
field_length()
|
|
|
|
Return the length of a field in a result from its index.
|
|
|
|
<result> is the result handle.
|
|
<field> is the field index.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int field_length(DB_RESULT result, int field)
|
|
{
|
|
int len;
|
|
len = ((Dataset *)result)->fieldSize(field);
|
|
GB_TYPE type = conv_type(((Dataset *)result)->fieldType(field));
|
|
|
|
if (type != GB_T_STRING)
|
|
return 0;
|
|
else
|
|
return len;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
begin_transaction()
|
|
|
|
Begin a transaction.
|
|
|
|
<handle> is the database handle.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
In mysql commit/rollback can only be used with transaction safe tables (BDB,
|
|
or InnoDB tables)
|
|
|
|
ISAM, MyISAM and HEAP tables will commit straight away. The transaction
|
|
methods are therefore ignored.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int begin_transaction(DB_DATABASE *db)
|
|
{
|
|
return do_query(db, "Unable to begin transaction: &1", NULL, "BEGIN", 0);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
commit_transaction()
|
|
|
|
Commit a transaction.
|
|
|
|
<handle> is the database handle.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int commit_transaction(DB_DATABASE *db)
|
|
{
|
|
return do_query(db, "Unable to commit transaction: &1", NULL, "COMMIT", 0);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
rollback_transaction()
|
|
|
|
Rolllback a transaction.
|
|
|
|
<handle> is the database handle.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int rollback_transaction(DB_DATABASE *db)
|
|
{
|
|
return do_query(db, "Unable to rollback transaction: &1", NULL, "ROLLBACK", 0);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_init()
|
|
|
|
Initialize an info structure from table fields.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<info> points at the info structure.
|
|
|
|
This function must initialize the following info fields:
|
|
- info->nfield must contain the number of fields in the table.
|
|
- info->fields is a char*[] pointing at the name of each field.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info);
|
|
|
|
static int table_init(DB_DATABASE *db, const char *table, DB_INFO *info)
|
|
{
|
|
const char *qfield = "PRAGMA table_info('&1')";
|
|
|
|
Dataset *res;
|
|
int i, n;
|
|
DB_FIELD *f;
|
|
const char *field;
|
|
|
|
/* Nom de la table */
|
|
|
|
info->table = GB.NewZeroString(table);
|
|
|
|
/* Liste des champs */
|
|
|
|
if (do_query(db, "Unable to get table fields: &1", &res, qfield, 1, table))
|
|
return TRUE;
|
|
|
|
result_set* r = (result_set*) res->getExecRes();
|
|
|
|
info->nfield = n = r->records.size();
|
|
if (n == 0){
|
|
res->close();
|
|
return TRUE;
|
|
}
|
|
|
|
GB.Alloc(POINTER(&info->field), sizeof(DB_FIELD) * n);
|
|
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
f = &info->field[i];
|
|
field = r->records[i][1].get_asString().data();
|
|
|
|
if (field_info(db, table, field, f))
|
|
{
|
|
res->close();
|
|
return TRUE;
|
|
}
|
|
|
|
f->name = GB.NewZeroString(field);
|
|
/*f->length = 0;
|
|
f->type = conv_type(GetFieldType((char *) r->records[i][2].get_asString().data(), (unsigned int *) &f->length));*/
|
|
}
|
|
|
|
res->close();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_index()
|
|
|
|
Initialize an info structure from table primary index.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<info> points at the info structure.
|
|
|
|
This function must initialize the following info fields:
|
|
- info->nindex must contain the number of fields in the primary index.
|
|
- info->index is a int[] giving the index of each index field in
|
|
info->fields.
|
|
|
|
This function must be called after table_init().
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int table_index(DB_DATABASE *db, const char *table, DB_INFO *info)
|
|
{
|
|
const char *qindex1 = "PRAGMA index_list('&1')";
|
|
const char *qindex2 = "PRAGMA index_info('&1')";
|
|
|
|
Dataset *res;
|
|
char *sql = NULL;
|
|
int n = 0;
|
|
|
|
/* Index primaire */
|
|
|
|
if (do_query(db, "Unable to get primary index: &1", &res, qindex1, 1, table))
|
|
return TRUE;
|
|
|
|
result_set* r = (result_set*) res->getExecRes();
|
|
if (( n = r->records.size()) <= 0){
|
|
/* no indexes returned */
|
|
/* sqlite will use a PRIMARY INDEX on ROWID */
|
|
/* BM: without a true primary index, Edit() does not work */
|
|
GB.Error("Table '&1' has no primary index", table);
|
|
res->close();
|
|
return TRUE;
|
|
}
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
if (strstr(r->records[i][1].get_asString().data(), "autoindex") != NULL){
|
|
sql = GB.NewZeroString(r->records[i][1].get_asString().data());
|
|
res->close();
|
|
|
|
if (do_query(db, "Unable to get information on primary index: &1", &res, qindex2, 1, sql)){
|
|
res->close();
|
|
GB.FreeString(&sql);
|
|
return TRUE;
|
|
}
|
|
GB.FreeString(&sql);
|
|
|
|
r = (result_set*) res->getExecRes();
|
|
info->nindex = r->records.size();
|
|
GB.Alloc(POINTER(&info->index), sizeof(int) * info->nindex);
|
|
|
|
for (i = 0; i < info->nindex; i++){
|
|
info->index[i] = r->records[i][1].get_asInteger();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
res->close();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
table_release()
|
|
|
|
Free the info structure filled by table_init() and/or table_index()
|
|
|
|
<handle> is the database handle.
|
|
<info> points at the info structure.
|
|
|
|
*****************************************************************************/
|
|
|
|
static void table_release(DB_DATABASE *db, DB_INFO *info)
|
|
{
|
|
/* All is done outside the driver */
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_exist()
|
|
|
|
Returns if a table exists
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
|
|
This function returns TRUE if the table exists, and FALSE if not.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int table_exist(DB_DATABASE *db, const char *table)
|
|
{
|
|
|
|
const char *query = "select tbl_name from "
|
|
"( select tbl_name from sqlite_master where type = 'table' union "
|
|
"select tbl_name from sqlite_temp_master where type = 'table' ) "
|
|
"where tbl_name = '&1'";
|
|
|
|
if ( strcmp(table,"sqlite_master") == 0 || strcmp(table,"sqlite_temp_master") == 0){
|
|
return TRUE;
|
|
}
|
|
|
|
Dataset *res;
|
|
int exist;
|
|
|
|
if (do_query(db, "Unable to check table: &1", &res, query, 1, table))
|
|
return FALSE;
|
|
|
|
exist = res->num_rows();
|
|
res->close();
|
|
return exist;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_list()
|
|
|
|
Returns an array containing the name of each table in the database
|
|
|
|
<handle> is the database handle.
|
|
<tables> points to a variable that will receive the char* array.
|
|
|
|
This function returns the number of tables, or -1 if the command has
|
|
failed.
|
|
|
|
Be careful: <tables> can be NULL, so that just the count is returned.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int table_list(DB_DATABASE *db, char ***tables)
|
|
{
|
|
const char *query =
|
|
"select tbl_name from "
|
|
"( select tbl_name from sqlite_master where type = 'table' union "
|
|
" select tbl_name from sqlite_temp_master where type = 'table')";
|
|
|
|
Dataset *res;
|
|
int rows;
|
|
int i = 0;
|
|
|
|
if (do_query(db, "Unable to get tables: &1", &res, query, 0))
|
|
return -1;
|
|
|
|
rows = res->num_rows();
|
|
GB.NewArray(tables, sizeof(char *), rows + 2);//sqlite_master and sqlite_temp_master need to be
|
|
//added to the list
|
|
|
|
while ( !res->eof()){
|
|
(*tables)[i] = GB.NewZeroString(res->fv("tbl_name").get_asString().data());
|
|
res->next();
|
|
i++;
|
|
}
|
|
|
|
|
|
res->close();
|
|
|
|
(*tables)[i] = GB.NewZeroString("sqlite_master");
|
|
(*tables)[++i] = GB.NewZeroString("sqlite_temp_master");
|
|
return rows;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_primary_key()
|
|
|
|
Returns a string representing the primary key of a table.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<key> points to a string that will receive the primary key.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary)
|
|
{
|
|
const char *qindex1 = "PRAGMA index_list('&1')";
|
|
const char *qindex2 = "PRAGMA index_info('&1')";
|
|
|
|
Dataset *res;
|
|
int i, n;
|
|
char *sql;
|
|
|
|
if (do_query(db, "Unable to get primary key: &1", &res, qindex1, 1, table))
|
|
return TRUE;
|
|
|
|
GB.NewArray(primary, sizeof(char *), 0);
|
|
|
|
result_set* r = (result_set*) res->getExecRes();
|
|
//if ((n = r->records.size()) < 1){ //No Indexes found
|
|
// GB.NewString((char **)GB.Add(primary), "ROWID", 0);
|
|
// res->close();
|
|
// return FALSE;
|
|
//}
|
|
|
|
n = r->records.size();
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
if (strstr(r->records[i][1].get_asString().data(), "autoindex")){
|
|
sql = GB.NewZeroString(r->records[i][1].get_asString().data());
|
|
res->close();
|
|
|
|
if (do_query(db, "Unable to get primary key: &1", &res, qindex2, 1, sql)){
|
|
res->close();
|
|
GB.FreeString(&sql);
|
|
return TRUE;
|
|
}
|
|
GB.FreeString(&sql);
|
|
|
|
r = (result_set*) res->getExecRes();
|
|
if ((n = r->records.size()) < 1 ){
|
|
// No information returned for key
|
|
res->close();
|
|
return TRUE;
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
*(char **)GB.Add(primary) = GB.NewZeroString(r->records[i][2].get_asString().data());
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
res->close();
|
|
/*
|
|
if (GB.Count(*primary) == 0){
|
|
GB.NewString((char **)GB.Add(primary), "ROWID", 0);
|
|
}
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_is_system()
|
|
|
|
Returns if a table is a system table.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
|
|
This function returns TRUE if the table is a system table, and FALSE if
|
|
not.
|
|
|
|
Note: There are only two tables in Sqlite used by the system.
|
|
sqlite_master and sqlite_temp_master
|
|
|
|
*****************************************************************************/
|
|
|
|
static int table_is_system(DB_DATABASE *db, const char *table)
|
|
{
|
|
if (strcmp(table,"sqlite_master") == 0 ||
|
|
strcmp(table, "sqlite_temp_master") == 0)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_type()
|
|
Not Valid in Sqlite
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
*****************************************************************************/
|
|
static char *table_type(DB_DATABASE *db, const char *table, const char *type)
|
|
{
|
|
if (type)
|
|
GB.Error("SQLite does not have any table types");
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_delete()
|
|
|
|
Deletes a table.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int table_delete(DB_DATABASE *db, const char *table)
|
|
{
|
|
return
|
|
do_query(db, "Unable to delete table: &1", NULL,
|
|
"drop table '&1'", 1, table);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
table_create()
|
|
|
|
Creates a table.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<fields> points to a linked list of field descriptions.
|
|
<key> is the primary key.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *not_used)
|
|
{
|
|
DB_FIELD *fp;
|
|
int comma;
|
|
const char *type;
|
|
int i;
|
|
|
|
DB.Query.Init();
|
|
|
|
DB.Query.Add("CREATE TABLE ");
|
|
DB.Query.Add(QUOTE_STRING);
|
|
DB.Query.Add(table);
|
|
DB.Query.Add(QUOTE_STRING);
|
|
DB.Query.Add(" ( ");
|
|
|
|
comma = FALSE;
|
|
for (fp = fields; fp; fp = fp->next)
|
|
{
|
|
if (comma)
|
|
DB.Query.Add(", ");
|
|
else
|
|
comma = TRUE;
|
|
|
|
DB.Query.Add(QUOTE_STRING);
|
|
DB.Query.Add(fp->name);
|
|
DB.Query.Add(QUOTE_STRING);
|
|
|
|
switch (fp->type)
|
|
{
|
|
case GB_T_BOOLEAN: type = "BOOL"; break;
|
|
case GB_T_INTEGER: type = "INT4"; break;
|
|
case GB_T_LONG: type = "BIGINT"; break;
|
|
case GB_T_FLOAT: type = "FLOAT8"; break;
|
|
case GB_T_DATE: type = "DATETIME"; break;
|
|
case GB_T_STRING:
|
|
|
|
if (fp->length <= 0)
|
|
type = "TEXT";
|
|
else
|
|
{
|
|
sprintf(_buffer, "VARCHAR(%d)", fp->length);
|
|
type = _buffer;
|
|
}
|
|
|
|
break;
|
|
|
|
default: type = "TEXT"; break;
|
|
}
|
|
|
|
DB.Query.Add(" ");
|
|
DB.Query.Add(type);
|
|
|
|
if (fp->def.type != GB_T_NULL)
|
|
{
|
|
DB.Query.Add(" NOT NULL DEFAULT ");
|
|
DB.FormatVariant(&_driver, &fp->def, DB.Query.AddLength);
|
|
}
|
|
else if (DB.StringArray.Find(primary, fp->name) >= 0)
|
|
{
|
|
DB.Query.Add(" NOT NULL ");
|
|
}
|
|
}
|
|
|
|
if (primary)
|
|
{
|
|
DB.Query.Add(", PRIMARY KEY (");
|
|
|
|
for (i = 0; i < GB.Count(primary); i++)
|
|
{
|
|
if (i > 0)
|
|
DB.Query.Add(",");
|
|
|
|
DB.Query.Add(primary[i]);
|
|
}
|
|
|
|
DB.Query.Add(")");
|
|
}
|
|
|
|
DB.Query.Add(" )");
|
|
|
|
return do_query(db, "Cannot create table: &1", NULL, DB.Query.Get(), 0);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
field_exist()
|
|
|
|
Returns if a field exists in a given table
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<field> is the field name.
|
|
|
|
This function returns TRUE if the field exists, and FALSE if not.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int field_exist(DB_DATABASE *db, const char *table, const char *field)
|
|
{
|
|
const char *query = "PRAGMA table_info('&1')";
|
|
|
|
int i, n;
|
|
Dataset *res;
|
|
int exist = 0;
|
|
|
|
if (do_query(db, "Unable to find field: &1.&2", &res, query, 2, table, field)){
|
|
return FALSE;
|
|
}
|
|
|
|
result_set* r = (result_set*) res->getExecRes();
|
|
|
|
n = r->records.size();
|
|
|
|
for (i = 0; i < n; i++){
|
|
if (strcmp(field, r->records[i][1].get_asString().data()) == 0)
|
|
exist++;
|
|
}
|
|
|
|
res->close();
|
|
return exist;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
field_list()
|
|
|
|
Returns an array containing the name of each field in a given table
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<fields> points to a variable that will receive the char* array.
|
|
|
|
This function returns the number of fields, or -1 if the command has
|
|
failed.
|
|
|
|
Be careful: <fields> can be NULL, so that just the count is returned.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int field_list(DB_DATABASE *db, const char *table, char ***fields)
|
|
{
|
|
const char *query = "PRAGMA table_info('&1')";
|
|
|
|
int i, n;
|
|
Dataset *res;
|
|
|
|
if (do_query(db, "Unable to get fields: &1", &res, query, 1, table)){
|
|
return -1;
|
|
}
|
|
|
|
result_set* r = (result_set*) res->getExecRes();
|
|
|
|
n = r->records.size();
|
|
|
|
if (fields) /* (BM) see the function commentary */
|
|
{
|
|
GB.NewArray(fields, sizeof(char *), n);
|
|
|
|
for (i = 0; i < n; i++){
|
|
(*fields)[i] = GB.NewZeroString(r->records[i][1].get_asString().data());
|
|
}
|
|
}
|
|
|
|
res->close();
|
|
return n;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
field_info()
|
|
|
|
Get field description
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<field> is the field name.
|
|
<info> points to a structure filled by the function.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info)
|
|
{
|
|
const char *query = "PRAGMA table_info('&1')";
|
|
|
|
Dataset *res;
|
|
GB_VARIANT def;
|
|
char *val;
|
|
char *_fieldName = NULL;
|
|
char *_fieldType = NULL;
|
|
char *_defaultValue = NULL;
|
|
bool _fieldNotNull = FALSE;
|
|
int i, n;
|
|
fType type;
|
|
|
|
if (do_query(db, "Unable to get fields: &1", &res, query,1, table))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
result_set* r = (result_set*) res->getExecRes();
|
|
if ((n = r->records.size()) == 0)
|
|
{
|
|
GB.Error("Unable to find field &1.&2", table, field);
|
|
return TRUE;
|
|
}
|
|
|
|
for ( i =0; i < n; i++){
|
|
_fieldName = (char *)r->records[i][1].get_asString().data();
|
|
|
|
if (strcmp(_fieldName, field) == 0){
|
|
_fieldType = (char *)r->records[i][2].get_asString().data();
|
|
_fieldNotNull = (char *)r->records[i][3].get_asBool();
|
|
_defaultValue = (char *)r->records[i][4].get_asString().data();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= n)
|
|
{
|
|
GB.Error("Unable to find field &1.&2", table, field);
|
|
return TRUE;
|
|
}
|
|
|
|
info->name = NULL;
|
|
|
|
type = GetFieldType(_fieldType, (unsigned int *) &info->length);
|
|
info->type = conv_type(type);
|
|
|
|
info->def.type = GB_T_NULL;
|
|
|
|
if ( _fieldNotNull)
|
|
{
|
|
def.type = GB_T_VARIANT;
|
|
def.value.type = GB_T_NULL;
|
|
|
|
val = _defaultValue;
|
|
|
|
if (val && *val)
|
|
{
|
|
conv_data(val, &def.value, type);
|
|
GB.StoreVariant(&def, &info->def);
|
|
}
|
|
}
|
|
|
|
res->close();
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
index_exist()
|
|
|
|
Returns if an index exists in a given table
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<field> is the index name.
|
|
|
|
This function returns TRUE if the index exists, and FALSE if not.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int index_exist(DB_DATABASE *db, const char *table, const char *index)
|
|
{
|
|
const char *query = "select tbl_name from "
|
|
"( select tbl_name from sqlite_master where type = 'index' and "
|
|
" name = '&2' union "
|
|
"select tbl_name from sqlite_temp_master where type = 'index' and "
|
|
" name = '&2' ) "
|
|
"where tbl_name = '&1'";
|
|
|
|
Dataset *res;
|
|
int exist;
|
|
|
|
if (do_query(db, "Unable to check table: &1", &res, query, 2, table, index))
|
|
return FALSE;
|
|
|
|
exist = res->num_rows();
|
|
res->close();
|
|
return exist;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
index_list()
|
|
|
|
Returns an array containing the name of each index in a given table
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<indexes> points to a variable that will receive the char* array.
|
|
|
|
This function returns the number of indexes, or -1 if the command has
|
|
failed.
|
|
|
|
Be careful: <indexes> can be NULL, so that just the count is returned.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int index_list(DB_DATABASE *db, const char *table, char ***indexes)
|
|
{
|
|
|
|
const char *query =
|
|
"select name from "
|
|
"( select name from sqlite_master where type = 'index' and tbl_name = '&1' "
|
|
" union select name from sqlite_temp_master where type = 'index' and "
|
|
" tbl_name = '&1')";
|
|
|
|
Dataset *res;
|
|
int no_indexes, i = 0;
|
|
|
|
if (do_query(db, "Unable to get tables: &1", &res, query, 1, table))
|
|
return -1;
|
|
|
|
no_indexes = res->num_rows();
|
|
GB.NewArray(indexes, sizeof(char *), no_indexes);
|
|
|
|
while ( !res->eof()){
|
|
//(res->fv("tbl_name").get_asString().data());
|
|
(*indexes)[i] = GB.NewZeroString(res->fv(res->fieldName(0)).get_asString().data());
|
|
res->next();
|
|
i++;
|
|
}
|
|
|
|
res->close();
|
|
|
|
return no_indexes;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
index_info()
|
|
|
|
Get index description
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<field> is the index name.
|
|
<info> points to a structure filled by the function.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info)
|
|
{
|
|
/* sqlite indexes are unique to the database, and not to the table */
|
|
|
|
const char *qindex1 = "PRAGMA index_list('&1')";
|
|
const char *qindex2 = "PRAGMA index_info('&1')";
|
|
|
|
Dataset *res;
|
|
int i, j, n;
|
|
|
|
if (do_query(db, "Unable to get index info for table: &1", &res, qindex1, 1, table))
|
|
return TRUE;
|
|
|
|
result_set* r = (result_set*) res->getExecRes();
|
|
if (( n = r->records.size()) == 0){
|
|
/* no indexes found */
|
|
res->close();
|
|
GB.Error("Unable to find index &1.&2", table, index);
|
|
return TRUE;
|
|
}
|
|
|
|
for (i = 0, j = 0; i < n; i++)
|
|
{ /* Find the required index */
|
|
if ( strcmp( index, r->records[i][1].get_asString().data()) == 0){
|
|
j++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( j == 0)
|
|
{
|
|
GB.Error("Unable to find index &1.&2", table, index);
|
|
return TRUE;
|
|
}
|
|
|
|
info->name = NULL;
|
|
info->unique = strncmp("1",r->records[i][2].get_asString().data(),1) == 0 ? TRUE : FALSE;
|
|
info->primary = strstr(r->records[i][1].get_asString().data(), "autoindex")
|
|
!= NULL ? TRUE : FALSE;
|
|
|
|
DB.Query.Init();
|
|
|
|
if (do_query(db, "Unable to get index info for : &1", &res, qindex2, 1, index)){
|
|
res->close();
|
|
return TRUE;
|
|
}
|
|
|
|
r = (result_set*) res->getExecRes();
|
|
n = r->records.size();
|
|
i = 0;
|
|
/* (BM) row can be null if we are seeking the last index */
|
|
while ( i < n )
|
|
{
|
|
if (i > 0)
|
|
DB.Query.Add(",");
|
|
|
|
/* Get fields in key */
|
|
DB.Query.Add(r->records[i][2].get_asString().data());
|
|
i++;
|
|
}
|
|
|
|
res->close();
|
|
info->fields = DB.Query.GetNew();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
index_delete()
|
|
|
|
Deletes an index.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<index> is the index name.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int index_delete(DB_DATABASE *db, const char *table, const char *index)
|
|
{
|
|
return
|
|
do_query(db, "Unable to delete index: &1", NULL,
|
|
"drop index '&1'", 1, index);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
index_create()
|
|
|
|
Creates an index.
|
|
|
|
<handle> is the database handle.
|
|
<table> is the table name.
|
|
<index> is the index name.
|
|
<info> points to a structure describing the index.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info)
|
|
{
|
|
DB.Query.Init();
|
|
|
|
DB.Query.Add("CREATE ");
|
|
if (info->unique)
|
|
DB.Query.Add("UNIQUE ");
|
|
DB.Query.Add("INDEX '");
|
|
DB.Query.Add(index);
|
|
DB.Query.Add("' ON ");
|
|
DB.Query.Add(table);
|
|
DB.Query.Add(" ( ");
|
|
DB.Query.Add(info->fields);
|
|
DB.Query.Add(" )");
|
|
|
|
return do_query(db, "Cannot create index: &1", NULL, DB.Query.Get(), 0);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* database_exist()
|
|
*
|
|
* Returns if a database exists
|
|
*
|
|
* <handle> is any database handle.
|
|
* <name> is the database name.
|
|
*
|
|
* This function returns TRUE if the database exists, and FALSE if not.
|
|
* SQLite: Databases are just files, so we need to ceck to see whether
|
|
* the file exists and is a sqlite file.
|
|
******************************************************************************/
|
|
static int database_exist(DB_DATABASE *db, const char *name)
|
|
{
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
char *fullpath = NULL;
|
|
|
|
if (strcmp(name,":memory:") == 0)
|
|
return TRUE; //Database is loaded in memory only
|
|
|
|
if((fullpath = FindDatabase(name, conn->getHostName())) != NULL){
|
|
GB.FreeString(&fullpath);
|
|
return TRUE;
|
|
}
|
|
|
|
GB.FreeString(&fullpath);
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* database_list()
|
|
*
|
|
* Returns an array containing the name of each database
|
|
*
|
|
* <handle> is any database handle.
|
|
* <databases> points to a variable that will receive the char* array.
|
|
*
|
|
* This function returns the number of databases, or -1 if the command has
|
|
* failed.
|
|
*
|
|
* Be careful: <databases> can be NULL, so that just the count is returned.
|
|
*
|
|
* Sqlite databases are files. Using we will only list
|
|
* files within a designated directory. Propose that all
|
|
* areas are walked through.
|
|
******************************************************************************/
|
|
|
|
static int database_list(DB_DATABASE *db, char ***databases)
|
|
{
|
|
char *dbhome;
|
|
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
GB.NewArray(databases, sizeof(char *), 0);
|
|
|
|
/* Hostname contains home area */
|
|
dbhome = (char *)conn->getHostName();
|
|
WalkDirectory( dbhome, databases );
|
|
|
|
/* Checks GAMBAS_SQLITE_DBHOME if set, or Current Working Directory */
|
|
/* Might have to come back and seperate */
|
|
dbhome = GetDatabaseHome();
|
|
if (dbhome){
|
|
//GB.Error("Unable to get databases: &1", "Can't find current directory");
|
|
WalkDirectory( dbhome, databases );
|
|
GB.Free(POINTER(&dbhome));
|
|
}
|
|
|
|
/*if (getcwd(cwd, MAX_PATH) != NULL){
|
|
if (strcmp(cwd, dbhome) != 0){
|
|
WalkDirectory( cwd, databases );
|
|
}
|
|
}*/
|
|
|
|
return GB.Count(databases);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* database_is_system()
|
|
*
|
|
* Returns if a database is a system database.
|
|
*
|
|
* <handle> is any database handle.
|
|
* <name> is the database name.
|
|
*
|
|
* This function returns TRUE if the database is a system database, and
|
|
* FALSE if not.
|
|
*
|
|
* NOTE: Sqlite doesn't have such a thing.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static int database_is_system(DB_DATABASE *db, const char *name)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* database_delete()
|
|
*
|
|
* Deletes a database.
|
|
*
|
|
* <handle> is the database handle.
|
|
* <name> is the database name.
|
|
*
|
|
* This function returns TRUE if the command has failed, and FALSE if
|
|
* everything was OK.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static int database_delete(DB_DATABASE *db, const char *name)
|
|
{
|
|
char *fullpath = NULL;
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
|
|
if((fullpath = FindDatabase(name, conn->getHostName())) == NULL){
|
|
GB.FreeString(&fullpath);
|
|
GB.Error("Cannot Find database: &1", name);
|
|
return TRUE;
|
|
}
|
|
|
|
if( remove(fullpath) != 0){
|
|
GB.Error("Unable to delete database &1", fullpath);
|
|
GB.FreeString(&fullpath);
|
|
return TRUE;
|
|
}
|
|
|
|
GB.FreeString(&fullpath);
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* database_create()
|
|
*
|
|
* Creates a database.
|
|
*
|
|
* <handle> is the database handle.
|
|
* <name> is the database name.
|
|
*
|
|
* This function returns TRUE if the command has failed, and FALSE if
|
|
* everything was OK.
|
|
*
|
|
* SQLite automatically creates a database on connect if the file
|
|
* does not exist.
|
|
******************************************************************************/
|
|
|
|
static int database_create(DB_DATABASE *db, const char *name)
|
|
{
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
SqliteDatabase conn2;
|
|
char *fullpath = NULL;
|
|
//char *homepath = NULL;
|
|
//DIR *dp;
|
|
char *dir;
|
|
const char *host;
|
|
|
|
/* Does Name include fullpath */
|
|
/* BM: do not use basename(), it can modify its argument */
|
|
if (name && name[0] == '/')
|
|
{
|
|
fullpath = GB.NewZeroString(name);
|
|
goto _CREATE_DATABASE;
|
|
}
|
|
|
|
/* Hostname contains home area */
|
|
//if ((dp = opendir(conn->getHostName())))
|
|
/* BM: Why use opendir(), and why forget to call closedir() ? */
|
|
host = conn->getHostName();
|
|
if (host && host[0])
|
|
{
|
|
fullpath = GB.NewZeroString(host);
|
|
}
|
|
else
|
|
{
|
|
dir = GetDatabaseHome();
|
|
mkdir(dir, S_IRWXU);
|
|
fullpath = GB.NewZeroString(dir);
|
|
GB.Free(POINTER(&dir));
|
|
}
|
|
|
|
if (fullpath[strlen(fullpath) - 1] != '/')
|
|
fullpath = GB.AddChar(fullpath, '/');
|
|
|
|
fullpath = GB.AddString(fullpath, name, 0);
|
|
|
|
_CREATE_DATABASE:
|
|
|
|
conn2.setDatabase(fullpath);
|
|
|
|
GB.FreeString(&fullpath);
|
|
if ( conn2.connect() != DB_CONNECTION_OK){
|
|
GB.Error("Cannot create database: &1", conn2.getErrorMsg());
|
|
conn2.disconnect();
|
|
return TRUE;
|
|
}
|
|
|
|
//Create and remove a table to initialize database
|
|
db->handle = &conn2;
|
|
if (!do_query(db, "Unable to initialise database", NULL, "CREATE TABLE GAMBAS (FIELD1 TEXT)", 0))
|
|
do_query(db, NULL, NULL, "DROP TABLE GAMBAS",0);
|
|
|
|
conn2.disconnect();
|
|
db->handle = conn;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* user_exist()
|
|
*
|
|
* Returns if a user exists.
|
|
*
|
|
* <handle> is any database handle.
|
|
* <name> is the user name.
|
|
*
|
|
* This function returns TRUE if the user exists, and FALSE if not.
|
|
* Sqlite does not have different users. Access is controlled by
|
|
* access rightd on the file.
|
|
* We can check that the user exists on the machine and has access to
|
|
* database file!
|
|
* [Currently only checks against /etc/passwd.
|
|
* Does not check against /etc/shadow or pam.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static int user_exist(DB_DATABASE *db, const char *name)
|
|
{
|
|
struct stat dbbuf;
|
|
struct passwd *fileowner, *user; // /etc/passwd structure
|
|
struct group *Group; // /etc/group structure
|
|
char **Member;
|
|
const char *Databasefile;
|
|
bool in_memory;
|
|
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
|
|
if (( Databasefile = conn->getDatabase()) == NULL){
|
|
GB.Error("User_exist:&1", "Unable to get databasename");
|
|
return FALSE;
|
|
}
|
|
|
|
in_memory = strcmp(Databasefile, ":memory:") == 0;
|
|
|
|
/* Is username in passwd file */
|
|
if ((user = getpwnam(name)) == NULL){
|
|
return FALSE;
|
|
}
|
|
|
|
if (in_memory)
|
|
return (user->pw_uid == getuid());
|
|
|
|
if (stat(Databasefile, &dbbuf) != 0)
|
|
{
|
|
GB.Error("User_exist: Unable to get status of &1", Databasefile);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Now what are the database file permissions */
|
|
/* The order of tests is important */
|
|
|
|
if (( fileowner = getpwuid(dbbuf.st_uid)) != NULL){
|
|
if ( fileowner->pw_uid == user->pw_uid ){
|
|
/* User is owner */
|
|
if (( dbbuf.st_mode & S_IRUSR ) || (dbbuf.st_mode & S_IWUSR)){
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if ( fileowner->pw_gid == user->pw_gid ){
|
|
if (( dbbuf.st_mode & S_IRGRP ) || (dbbuf.st_mode & S_IWGRP)){
|
|
/* User has access to the file via primary group access.*/
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Check whether user is in the same group */
|
|
|
|
Group = getgrgid(dbbuf.st_gid);
|
|
Member = Group->gr_mem;
|
|
while (Member && *Member){
|
|
if (strcmp(*Member, name) == 0){
|
|
//User is a member of the group
|
|
if (( dbbuf.st_mode & S_IRGRP ) || (dbbuf.st_mode & S_IWGRP)){
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
Member++;
|
|
}
|
|
|
|
if (( dbbuf.st_mode & S_IROTH ) || (dbbuf.st_mode & S_IWOTH)){
|
|
/* Any user can access this file */
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* user_list()
|
|
*
|
|
* Returns an array containing the name of each user.
|
|
*
|
|
* <handle> is the database handle.
|
|
* <users> points to a variable that will receive the char* array.
|
|
*
|
|
* This function returns the number of users, or -1 if the command has
|
|
* failed.
|
|
*
|
|
* Be careful: <users> can be NULL, so that just the count is returned.
|
|
* Sqlite does not have users.
|
|
*
|
|
******************************************************************************/
|
|
|
|
|
|
static int user_list(DB_DATABASE *db, char ***users)
|
|
{
|
|
//Should we use GB.HashTable.New etc. to ensure
|
|
//duplicates are not reported back, then transfer
|
|
//the valid elements to the Array.
|
|
//Need to check order of rights. e.g. User overides
|
|
//group and other, group overides other.
|
|
//That is if the user is a member of a group that
|
|
//is given no rights, but other has all rights,
|
|
//they should not be able to access
|
|
char *Databasefile;
|
|
struct stat buf;
|
|
struct passwd *user; // /etc/passwd structure
|
|
struct group *Group; // /etc/group structure
|
|
char **Member;
|
|
int Count = 0;
|
|
bool in_memory;
|
|
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
|
|
if (( Databasefile = (char *)conn->getDatabase()) == NULL){
|
|
GB.Error("Unable to get databasename");
|
|
return -1;
|
|
}
|
|
|
|
in_memory = strcmp(Databasefile, ":memory:") == 0;
|
|
|
|
if (in_memory)
|
|
{
|
|
buf.st_mode = S_IWUSR | S_IRUSR;
|
|
buf.st_uid = getuid();
|
|
}
|
|
else if (stat(Databasefile, &buf) != 0)
|
|
{
|
|
GB.Error("Unable to get status of &1", Databasefile);
|
|
return -1;
|
|
}
|
|
|
|
if (users)
|
|
GB.NewArray(users, sizeof(char *), 0);
|
|
|
|
if (!in_memory)
|
|
{
|
|
|
|
/* If file has other access then any user could use it */
|
|
if (( buf.st_mode & S_IROTH ) || (buf.st_mode & S_IWOTH)){
|
|
/* Any user can access this file */
|
|
|
|
while (( user = getpwent()) != NULL ){
|
|
if (users){
|
|
*(char **)GB.Add(users) = GB.NewZeroString(user->pw_name);
|
|
}
|
|
else {
|
|
Count++;
|
|
}
|
|
}
|
|
|
|
if (users){
|
|
return GB.Count(users);
|
|
}
|
|
else {
|
|
return Count;
|
|
}
|
|
}
|
|
|
|
/* If group access then add all users in the group */
|
|
if (( buf.st_mode & S_IRGRP ) || (buf.st_mode & S_IWGRP)){
|
|
|
|
Group = getgrgid(buf.st_gid);
|
|
Member = Group->gr_mem;
|
|
while (Member && *Member){
|
|
if (users){
|
|
*(char **)GB.Add(users) = GB.NewZeroString(*Member);
|
|
}
|
|
else {
|
|
Count++;
|
|
}
|
|
Member++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Don't forget the owner if that has not already been added */
|
|
if (( buf.st_mode & S_IRUSR ) || (buf.st_mode & S_IWUSR)){
|
|
if (( user = getpwuid(buf.st_uid)) != NULL){
|
|
if (users){
|
|
*(char **)GB.Add(users) = GB.NewZeroString(user->pw_name);
|
|
}
|
|
else {
|
|
Count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (users)
|
|
return GB.Count(users);
|
|
else
|
|
return Count;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* user_info()
|
|
*
|
|
* Get user description
|
|
*
|
|
* <handle> is the database handle.
|
|
* <name> is the user name.
|
|
* <info> points to a structure filled by the function.
|
|
*
|
|
* This function returns TRUE if the command has failed, and FALSE if
|
|
* everything was OK.
|
|
*
|
|
* Sqlite privileges are just file privileges. We will return Admin
|
|
* rights where privilege allows Write. There is no password.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static int user_info(DB_DATABASE *db, const char *name, DB_USER *info )
|
|
{
|
|
char *Databasefile;
|
|
//struct stat buf;
|
|
struct passwd *user; // /etc/passwd structure
|
|
bool in_memory;
|
|
//struct group *Group; // /etc/group structure
|
|
//char **Member;
|
|
//int Count;
|
|
|
|
SqliteDatabase *conn = (SqliteDatabase *)db->handle;
|
|
|
|
/* Is username in passwd file */
|
|
if ((user = getpwnam(name)) == NULL){
|
|
GB.Error("User_info: Invalid user &1", name);
|
|
return TRUE;
|
|
}
|
|
|
|
if (( Databasefile = (char *)conn->getDatabase()) == NULL){
|
|
GB.Error("User_info: &1", "Unable to get databasename");
|
|
return TRUE;
|
|
}
|
|
|
|
in_memory = strcmp(Databasefile, ":memory:") == 0;
|
|
|
|
if (in_memory)
|
|
info->admin = true;
|
|
else
|
|
info->admin = access(Databasefile, W_OK);
|
|
|
|
/* If file has other access then any user could use it */
|
|
//Need to look at order of checks
|
|
info->name = NULL;
|
|
|
|
// if (row[3]) //password does not exist for sqlite
|
|
// GB.NewString(&info->password, row[3], 0); //password is encrypted in mysql
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
user_delete()
|
|
|
|
Deletes a user.
|
|
|
|
<handle> is any database handle.
|
|
<name> is the user name.
|
|
|
|
This function returns TRUE if the command has failed, and FALSE if
|
|
everything was OK.
|
|
|
|
Sqlite users are operated by the O/S
|
|
*****************************************************************************/
|
|
|
|
static int user_delete(DB_DATABASE *db, const char *name)
|
|
{
|
|
GB.Error("SQLite users do not exist.");
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* user_create()
|
|
*
|
|
* Creates a user.
|
|
*
|
|
* <handle> is the database handle.
|
|
* <name> is the user name.
|
|
* <info> points to a structure describing the user.
|
|
*
|
|
* This function returns TRUE if the command has failed, and FALSE if
|
|
* everything was OK.
|
|
*
|
|
*
|
|
* Sqlite: No user create
|
|
******************************************************************************/
|
|
|
|
static int user_create(DB_DATABASE *db, const char *name, DB_USER *info)
|
|
{
|
|
GB.Error("SQLite users do not exist.");
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* user_set_password()
|
|
*
|
|
* Change the user password.
|
|
*
|
|
* <handle> is the database handle.
|
|
* <name> is the user name.
|
|
* <password> is the new password
|
|
*
|
|
* This function returns TRUE if the command has failed, and FALSE if
|
|
* everything was OK.
|
|
*
|
|
* Sqlite : No user passwords.
|
|
******************************************************************************/
|
|
|
|
static int user_set_password(DB_DATABASE *db, const char *name, const char *password)
|
|
{
|
|
GB.Error("SQLite users do not exist.");
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
The component entry and exit functions.
|
|
|
|
*****************************************************************************/
|
|
|
|
extern "C" {
|
|
int EXPORT GB_INIT(void)
|
|
{
|
|
GB.GetInterface("gb.db", DB_INTERFACE_VERSION, &DB);
|
|
DB.Register(&_driver);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void EXPORT GB_EXIT()
|
|
{
|
|
}
|
|
|
|
} //extern "C"
|