0026fb1489
* NEW: Support for compiler warnings. * BUG: Fix all detected warnings. [COMPILER] * NEW: A new compiler option '-w' to let it emit warnings. * NEW: Emit warnings for unused variables, arguments, functions and for local symbols that override global symbols. Public symbols are of course not taken into account. * NEW: If an argument is surrounded by braces, no warning will be emitted if the argument is not used. [GB.EVAL] * NEW: OPTIONAL keyword is always followed by a space now when rewriting Gambas code. git-svn-id: svn://localhost/gambas/trunk@5122 867c0c6c-44f3-4631-809d-bfa615b0a4ec
1085 lines
24 KiB
C++
1085 lines
24 KiB
C++
/***************************************************************************
|
|
|
|
sqlitedataset.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.
|
|
|
|
***************************************************************************/
|
|
|
|
/**********************************************************************
|
|
* Copyright (c) 2002, Leo Seib, Hannover
|
|
*
|
|
* Project:SQLiteDataset C++ Dynamic Library
|
|
* Module: SQLiteDataset class realisation file
|
|
* Author: Leo Seib E-Mail: lev@almaty.pointstrike.net
|
|
* Begin: 5/04/2002
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
**********************************************************************/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
#include <iostream>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "sqlitedataset.h"
|
|
#include <unistd.h>
|
|
|
|
#include "gambas.h"
|
|
|
|
|
|
|
|
/**************************************************************/
|
|
|
|
// BM: my own sqlite3_exec(), that pass the statement pointer to the callback
|
|
|
|
typedef
|
|
int (*my_sqlite3_callback)(void *, int, char **, char **, sqlite3_stmt *);
|
|
|
|
|
|
static int my_sqlite3_exec(
|
|
sqlite3 *db, /* The database on which the SQL executes */
|
|
const char *zSql, /* The SQL to be executed */
|
|
my_sqlite3_callback xCallback, /* Invoke this callback routine */
|
|
void *pArg, /* First argument to xCallback() */
|
|
char **pzErrMsg /* Write error messages here */
|
|
)
|
|
{
|
|
int rc = SQLITE_OK;
|
|
const char *zLeftover;
|
|
sqlite3_stmt *pStmt = 0;
|
|
char **azCols = 0;
|
|
|
|
int nRetry = 0;
|
|
//int nChange = 0;
|
|
int nCallback;
|
|
|
|
if( zSql==0 ) return SQLITE_OK;
|
|
while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){
|
|
int nCol;
|
|
char **azVals = 0;
|
|
|
|
pStmt = 0;
|
|
rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover);
|
|
if( rc!=SQLITE_OK ){
|
|
if( pStmt ) sqlite3_finalize(pStmt);
|
|
continue;
|
|
}
|
|
if( !pStmt ){
|
|
/* this happens for a comment or white-space */
|
|
zSql = zLeftover;
|
|
continue;
|
|
}
|
|
|
|
//db->nChange += nChange;
|
|
nCallback = 0;
|
|
|
|
nCol = sqlite3_column_count(pStmt);
|
|
if (nCol == 0)
|
|
goto exec_out;
|
|
|
|
//azCols = sqliteMalloc(2*nCol*sizeof(const char *));
|
|
GB.Alloc(POINTER(&azCols), 2*nCol*sizeof(const char *));
|
|
|
|
if( nCol && !azCols ){
|
|
rc = SQLITE_NOMEM;
|
|
goto exec_out;
|
|
}
|
|
|
|
while( 1 ){
|
|
int i;
|
|
rc = sqlite3_step(pStmt);
|
|
|
|
/* Invoke the callback function if required */
|
|
if( xCallback && (SQLITE_ROW==rc ||
|
|
//(SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){
|
|
(SQLITE_DONE==rc && !nCallback && 1)) ){
|
|
if( 0==nCallback ){
|
|
for(i=0; i<nCol; i++){
|
|
azCols[i] = (char *)sqlite3_column_name(pStmt, i);
|
|
}
|
|
nCallback++;
|
|
}
|
|
if( rc==SQLITE_ROW ){
|
|
azVals = &azCols[nCol];
|
|
for(i=0; i<nCol; i++){
|
|
if (sqlite3_column_type(pStmt, i) == SQLITE_BLOB)
|
|
azVals[i] = (char *)sqlite3_column_blob(pStmt, i);
|
|
else
|
|
azVals[i] = (char *)sqlite3_column_text(pStmt, i);
|
|
}
|
|
}
|
|
if( xCallback(pArg, nCol, azVals, azCols, pStmt) ){
|
|
rc = SQLITE_ABORT;
|
|
goto exec_out;
|
|
}
|
|
}
|
|
|
|
if( rc!=SQLITE_ROW ){
|
|
rc = sqlite3_finalize(pStmt);
|
|
pStmt = 0;
|
|
//if( db->pVdbe==0 ){
|
|
// nChange = db->nChange;
|
|
//}
|
|
if( rc!=SQLITE_SCHEMA ){
|
|
nRetry = 0;
|
|
zSql = zLeftover;
|
|
while( isspace((unsigned char)zSql[0]) ) zSql++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
GB.Free(POINTER(&azCols));
|
|
azCols = 0;
|
|
}
|
|
|
|
exec_out:
|
|
if( pStmt ) sqlite3_finalize(pStmt);
|
|
if( azCols )
|
|
GB.Free(POINTER(&azCols));
|
|
|
|
//if( sqlite3_malloc_failed ){
|
|
// rc = SQLITE_NOMEM;
|
|
//}
|
|
|
|
// GB.Free(POINTER(pzErrMsg));
|
|
//
|
|
// if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){
|
|
// //*pzErrMsg = malloc(1+strlen(sqlite3_errmsg(db)));
|
|
// GB.Alloc(POINTER(pzErrMsg), 1+strlen(sqlite3_errmsg(db)));
|
|
// if( *pzErrMsg ){
|
|
// strcpy(*pzErrMsg, sqlite3_errmsg(db));
|
|
// }
|
|
// }
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
//************* Callback function ***************************
|
|
|
|
static int callback(void *res_ptr, int ncol, char **reslt, char **cols, sqlite3_stmt *stmt)
|
|
{
|
|
|
|
/* NG: Type definition */
|
|
// typedef vector<string> Tables;
|
|
Tables tables;
|
|
|
|
Tables::iterator it;
|
|
|
|
char *item;
|
|
char *table;
|
|
|
|
result_set *r = (result_set *) res_ptr; //dynamic_cast<result_set*>(res_ptr);
|
|
int sz = r->records.size();
|
|
|
|
//if (reslt == NULL ) cout << "EMPTY!!!\n";
|
|
if (!r->record_header.size())
|
|
{
|
|
/*IF*/ for (int i = 0; i < ncol; i++)
|
|
{
|
|
item = strchr(cols[i], (int) '.');
|
|
if (!item)
|
|
{ /* Field does not include table info */
|
|
item = cols[i];
|
|
r->record_header[i].name = item; //NG
|
|
table = NULL;
|
|
r->record_header[i].field_table = "";
|
|
r->record_header[i].type = ft_String; //default type to string
|
|
}
|
|
else
|
|
{
|
|
//table = strndup(cols[i], strchr(cols[i], (int)'.') - cols[i]);
|
|
table = GB.NewString(cols[i], strchr(cols[i], (int) '.') - cols[i]);
|
|
// table = strdup(reslt[0]);
|
|
r->record_header[i].name = item + 1;
|
|
r->record_header[i].field_table = table;
|
|
r->record_header[i].type = ft_String; //default type to string
|
|
}
|
|
|
|
if (!table)
|
|
{
|
|
/* Field does not contain table info,
|
|
* so let's default to string. This
|
|
* has probably happened because aliases
|
|
* are being used */
|
|
}
|
|
else
|
|
{
|
|
/* Check Table Name and add to list */
|
|
bool TableRegistered = false;
|
|
|
|
for (it = tables.begin(); it != tables.end(); it++)
|
|
{
|
|
if (strcmp((*it).data(), table) == 0)
|
|
TableRegistered = true;
|
|
}
|
|
if (TableRegistered == false)
|
|
{
|
|
tables.push_back(table);
|
|
}
|
|
}
|
|
GB.FreeString(&table); //from strdup
|
|
}
|
|
|
|
SetFieldType(r, tables); // Set all the field types
|
|
|
|
for (int i = 0; i < ncol; i++)
|
|
{
|
|
/* Should table name be included in field name */
|
|
if (tables.size() > 1)
|
|
{
|
|
r->record_header[i].name = cols[i];
|
|
}
|
|
|
|
/* if (tables.size() < 1){
|
|
r->record_header[i].type = ft_String;//Where type cannot be found
|
|
//default to string
|
|
}*/
|
|
}
|
|
} /* IF close */
|
|
|
|
//sql_record rec;
|
|
|
|
if (reslt != NULL)
|
|
{
|
|
for (int i = 0; i < ncol; i++)
|
|
{
|
|
//fprintf(stderr, "callback: [%d] %s: %s\n", i, cols[i], reslt[i]);
|
|
if (reslt[i] == NULL)
|
|
{
|
|
r->records[sz][i].set_isNull(r->record_header[i].type);
|
|
}
|
|
else
|
|
{
|
|
switch (r->record_header[i].type)
|
|
{
|
|
case ft_Blob:
|
|
if (stmt)
|
|
r->records[sz][i].set_asBlob(reslt[i], sqlite3_column_bytes(stmt, i));
|
|
break;
|
|
default:
|
|
r->records[sz][i].set_asString(reslt[i], r->record_header[i].type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//printf("Fsz: [%i]\n",r->record_header.size());
|
|
//printf("Recs: [%i]\n",r->records.size());
|
|
//cout << " val |" <<reslt<<"|"<<cols<<"|"<<"\n\n";
|
|
return 0;
|
|
}
|
|
|
|
static int old_callback(void *res_ptr, int ncol, char **reslt, char **cols)
|
|
{
|
|
return callback(res_ptr, ncol, reslt, cols, NULL);
|
|
}
|
|
|
|
|
|
//************* SqliteDatabase implementation ***************
|
|
|
|
SqliteDatabase::SqliteDatabase()
|
|
{
|
|
active = false;
|
|
_in_transaction = false; // for transaction
|
|
|
|
error = "Unknown database error"; //S_NO_CONNECTION;
|
|
host = "";
|
|
port = "";
|
|
db = ":memory:";
|
|
login = "";
|
|
passwd = "";
|
|
}
|
|
|
|
SqliteDatabase::~SqliteDatabase()
|
|
{
|
|
disconnect();
|
|
}
|
|
|
|
|
|
Dataset *SqliteDatabase::CreateDataset() const
|
|
{
|
|
return new SqliteDataset((SqliteDatabase *) this);
|
|
}
|
|
|
|
int SqliteDatabase::status(void)
|
|
{
|
|
if (active == false)
|
|
return DB_CONNECTION_NONE;
|
|
return DB_CONNECTION_OK;
|
|
}
|
|
|
|
int SqliteDatabase::setErr(int err_code)
|
|
{
|
|
last_err = err_code;
|
|
|
|
switch (err_code)
|
|
{
|
|
case SQLITE_OK:
|
|
error = "Successful result";
|
|
break;
|
|
case SQLITE_ERROR:
|
|
error = "SQL error or missing database";
|
|
break;
|
|
case SQLITE_INTERNAL:
|
|
error =
|
|
"Internal logic error - Report this error on the mailing-list at sqlite.org";
|
|
break;
|
|
case SQLITE_PERM:
|
|
error = "Access permission denied";
|
|
break;
|
|
case SQLITE_ABORT:
|
|
error = "Callback routine requested an abort";
|
|
break;
|
|
case SQLITE_BUSY:
|
|
error = "The database file is locked";
|
|
break;
|
|
case SQLITE_LOCKED:
|
|
error = "A table in the database is locked";
|
|
break;
|
|
case SQLITE_NOMEM:
|
|
error = "Out of memory";
|
|
break;
|
|
case SQLITE_READONLY:
|
|
error = "Attempt to write a readonly database";
|
|
break;
|
|
case SQLITE_INTERRUPT:
|
|
error = "Operation terminated by sqlite_interrupt()";
|
|
break;
|
|
case SQLITE_IOERR:
|
|
error = "Some kind of disk I/O error occurred";
|
|
break;
|
|
case SQLITE_CORRUPT:
|
|
error = "The database disk image is malformed";
|
|
break;
|
|
case SQLITE_NOTFOUND:
|
|
error = "(Internal Only) Table or record not found";
|
|
break;
|
|
case SQLITE_FULL:
|
|
error = "Insertion failed because database is full";
|
|
break;
|
|
case SQLITE_CANTOPEN:
|
|
error = "Unable to open the database file";
|
|
break;
|
|
case SQLITE_PROTOCOL:
|
|
error = "Database lock protocol error";
|
|
break;
|
|
case SQLITE_EMPTY:
|
|
error = "(Internal Only) Database table is empty";
|
|
break;
|
|
case SQLITE_SCHEMA:
|
|
error = "The database schema changed";
|
|
break;
|
|
case SQLITE_TOOBIG:
|
|
error = "Too much data for one row of a table";
|
|
break;
|
|
case SQLITE_CONSTRAINT:
|
|
error = "Abort due to constraint violation";
|
|
break;
|
|
case SQLITE_MISMATCH:
|
|
error = "Data type mismatch";
|
|
break;
|
|
default:
|
|
error = "Undefined SQLite error";
|
|
}
|
|
return err_code;
|
|
}
|
|
|
|
const char *SqliteDatabase::getErrorMsg()
|
|
{
|
|
return error.c_str();
|
|
}
|
|
|
|
int SqliteDatabase::connect()
|
|
{
|
|
disconnect();
|
|
|
|
if (sqlite3_open(db.c_str(), &conn) == SQLITE_OK)
|
|
{
|
|
//cout << "Connected!\n";
|
|
//char *err = NULL;
|
|
|
|
if (setErr
|
|
(sqlite3_exec
|
|
(getHandle(), "PRAGMA empty_result_callbacks=ON", NULL, NULL,
|
|
NULL)) != SQLITE_OK)
|
|
{
|
|
GB.Error(getErrorMsg());
|
|
}
|
|
active = true;
|
|
/* NG 29/12/2005 - 3.2.1 introduced a problem with columns names
|
|
* which is resolved by setting short columns off first */
|
|
if (setErr
|
|
(sqlite3_exec
|
|
(getHandle(), "PRAGMA short_column_names=OFF", NULL, NULL,
|
|
NULL)) != SQLITE_OK)
|
|
{ //NG
|
|
GB.Error(getErrorMsg()); //NG
|
|
}
|
|
if (setErr
|
|
(sqlite3_exec
|
|
(getHandle(), "PRAGMA full_column_names=ON", NULL, NULL,
|
|
NULL)) != SQLITE_OK)
|
|
{ //NG
|
|
GB.Error(getErrorMsg()); //NG
|
|
}
|
|
return DB_CONNECTION_OK;
|
|
sqlite3_close(conn);
|
|
}
|
|
return DB_CONNECTION_NONE;
|
|
};
|
|
|
|
void SqliteDatabase::disconnect(void)
|
|
{
|
|
if (active == false)
|
|
return;
|
|
sqlite3_close(conn);
|
|
active = false;
|
|
};
|
|
|
|
int SqliteDatabase::create()
|
|
{
|
|
return connect();
|
|
};
|
|
|
|
int SqliteDatabase::drop()
|
|
{
|
|
if (active == false)
|
|
return DB_ERROR;
|
|
disconnect();
|
|
if (!unlink(db.c_str()))
|
|
return DB_ERROR;
|
|
return DB_COMMAND_OK;
|
|
};
|
|
|
|
|
|
long SqliteDatabase::nextid(const char *sname)
|
|
{
|
|
if (!active)
|
|
return DB_UNEXPECTED_RESULT;
|
|
int id;
|
|
result_set res;
|
|
char sqlcmd[512];
|
|
|
|
sprintf(sqlcmd, "select nextid from %s where seq_name = '%s'",
|
|
sequence_table.c_str(), sname);
|
|
res.conn = getHandle(); //NG
|
|
if ((last_err =
|
|
my_sqlite3_exec(getHandle(), sqlcmd, &callback, &res, NULL)) != SQLITE_OK)
|
|
{
|
|
return DB_UNEXPECTED_RESULT;
|
|
}
|
|
if (res.records.size() == 0)
|
|
{
|
|
id = 1;
|
|
sprintf(sqlcmd, "insert into %s (nextid,seq_name) values (%d,'%s')",
|
|
sequence_table.c_str(), id, sname);
|
|
if ((last_err =
|
|
sqlite3_exec(conn, sqlcmd, NULL, NULL, NULL)) != SQLITE_OK)
|
|
return DB_UNEXPECTED_RESULT;
|
|
return id;
|
|
}
|
|
else
|
|
{
|
|
id = res.records[0][0].get_asInteger() + 1;
|
|
sprintf(sqlcmd, "update %s set nextid=%d where seq_name = '%s'",
|
|
sequence_table.c_str(), id, sname);
|
|
if ((last_err =
|
|
sqlite3_exec(conn, sqlcmd, NULL, NULL, NULL)) != SQLITE_OK)
|
|
return DB_UNEXPECTED_RESULT;
|
|
return id;
|
|
}
|
|
}
|
|
|
|
|
|
// methods for transactions
|
|
// ---------------------------------------------
|
|
void SqliteDatabase::start_transaction()
|
|
{
|
|
if (active)
|
|
{
|
|
last_err = sqlite3_exec(conn, "begin", NULL, NULL, NULL);
|
|
_in_transaction = true;
|
|
}
|
|
}
|
|
|
|
void SqliteDatabase::commit_transaction()
|
|
{
|
|
if (active)
|
|
{
|
|
last_err = sqlite3_exec(conn, "commit", NULL, NULL, NULL);
|
|
_in_transaction = false;
|
|
}
|
|
}
|
|
|
|
void SqliteDatabase::rollback_transaction()
|
|
{
|
|
if (active)
|
|
{
|
|
last_err = sqlite3_exec(conn, "rollback", NULL, NULL, NULL);
|
|
_in_transaction = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//************* SqliteDataset implementation ***************
|
|
|
|
SqliteDataset::SqliteDataset():Dataset()
|
|
{
|
|
haveError = false;
|
|
db = NULL;
|
|
// errmsg = NULL;
|
|
}
|
|
|
|
|
|
SqliteDataset::SqliteDataset(SqliteDatabase * newDb):Dataset(newDb)
|
|
{
|
|
haveError = false;
|
|
db = newDb;
|
|
// errmsg = NULL;
|
|
}
|
|
|
|
SqliteDataset::~SqliteDataset()
|
|
{
|
|
/* if (errmsg)
|
|
sqlite3_free_table(&errmsg);*/
|
|
}
|
|
|
|
|
|
|
|
//--------- protected functions implementation -----------------//
|
|
|
|
sqlite3 *SqliteDataset::handle()
|
|
{
|
|
if (db != NULL)
|
|
{
|
|
return dynamic_cast < SqliteDatabase * >(db)->getHandle();
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void SqliteDataset::make_query(StringList & _sql)
|
|
{
|
|
string query;
|
|
|
|
try
|
|
{
|
|
|
|
if (autocommit)
|
|
db->start_transaction();
|
|
|
|
|
|
if (db == NULL)
|
|
GB.Error("No database connection");
|
|
|
|
//close();
|
|
|
|
|
|
for (list < string >::iterator i = _sql.begin(); i != _sql.end(); i++)
|
|
{
|
|
query = *i;
|
|
Dataset::parse_sql(query);
|
|
//cout << "Executing: "<<query<<"\n\n";
|
|
if (db->
|
|
setErr(sqlite3_exec
|
|
(this->handle(), query.c_str(), NULL, NULL,
|
|
NULL)) != SQLITE_OK)
|
|
{
|
|
GB.Error(db->getErrorMsg());
|
|
}
|
|
} // end of for
|
|
|
|
|
|
if (db->in_transaction() && autocommit)
|
|
db->commit_transaction();
|
|
|
|
active = true;
|
|
ds_state = dsSelect;
|
|
refresh();
|
|
|
|
} // end of try
|
|
catch(...)
|
|
{
|
|
if (db->in_transaction())
|
|
db->rollback_transaction();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void SqliteDataset::make_insert()
|
|
{
|
|
make_query(insert_sql);
|
|
last();
|
|
}
|
|
|
|
|
|
void SqliteDataset::make_edit()
|
|
{
|
|
make_query(update_sql);
|
|
}
|
|
|
|
|
|
void SqliteDataset::make_deletion()
|
|
{
|
|
make_query(delete_sql);
|
|
}
|
|
|
|
|
|
// BM: One copy of field_value less by commenting 'edit_object'
|
|
|
|
void SqliteDataset::fill_fields()
|
|
{
|
|
//cout <<"rr "<<result.records.size()<<"|" << frecno <<"\n";
|
|
if ((db == NULL) || (result.record_header.size() == 0)
|
|
|| (result.records.size() < (uint) frecno))
|
|
return;
|
|
if (fields_object->size() == 0) // Filling columns name
|
|
for (uint i = 0; i < result.record_header.size(); i++)
|
|
{
|
|
(*fields_object)[i].props = result.record_header[i];
|
|
//(*edit_object)[i].props = result.record_header[i];
|
|
}
|
|
|
|
//Filling result
|
|
if (result.records.size() != 0)
|
|
{
|
|
for (uint i = 0; i < result.records[frecno].size(); i++)
|
|
{
|
|
(*fields_object)[i].val = result.records[frecno][i];
|
|
//(*edit_object)[i].val = result.records[frecno][i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
field_value tmp;
|
|
|
|
for (uint i = 0; i < result.record_header.size(); i++)
|
|
{
|
|
(*fields_object)[i].val = tmp;
|
|
//(*edit_object)[i].val = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//------------- public functions implementation -----------------//
|
|
|
|
// BM: should retry if error = SQLITE_SCHEMA
|
|
|
|
int SqliteDataset::exec(const string & sql)
|
|
{
|
|
int res;
|
|
int retry;
|
|
|
|
|
|
if (!handle())
|
|
GB.Error("No database connection");
|
|
|
|
exec_res.record_header.clear();
|
|
exec_res.records.clear();
|
|
exec_res.conn = handle(); //NG
|
|
//if ((strncmp("select",sql.c_str(),6) == 0) || (strncmp("SELECT",sql.c_str(),6) == 0))
|
|
|
|
for (retry = 1; retry <= 2; retry++)
|
|
{
|
|
res = sqlite3_exec(handle(), sql.c_str(), old_callback, &exec_res, NULL);
|
|
if (res != SQLITE_SCHEMA)
|
|
break;
|
|
}
|
|
|
|
db->setErr(res);
|
|
//if (res != SQLITE_OK)
|
|
// GB.Error(db->getErrorMsg());
|
|
|
|
return (res == SQLITE_OK);
|
|
}
|
|
|
|
int SqliteDataset::exec()
|
|
{
|
|
return exec(sql);
|
|
}
|
|
|
|
const void *SqliteDataset::getExecRes()
|
|
{
|
|
return &exec_res;
|
|
}
|
|
|
|
|
|
bool SqliteDataset::query(const char *query)
|
|
{
|
|
|
|
int res;
|
|
int retry;
|
|
|
|
|
|
//try{
|
|
if (db == NULL)
|
|
GB.Error("Database is not defined");
|
|
if (dynamic_cast < SqliteDatabase * >(db)->getHandle() == NULL)
|
|
GB.Error("No database connection");
|
|
if ((strncasecmp("select", query, 6) != 0) /*&&
|
|
(strncasecmp("PRAGMA table",query,12) !=0) &&
|
|
(strncasecmp("PRAGMA index",query,12) !=0) */
|
|
)
|
|
GB.Error("Syntax error in request: SELECT expected.");
|
|
|
|
//close();
|
|
|
|
//cout << "Curr size "<<num_rows()<<"\n\n";
|
|
result.conn = handle(); //NG
|
|
|
|
for (retry = 1; retry <= 2; retry++)
|
|
{
|
|
res = my_sqlite3_exec(handle(), query, &callback, &result, NULL);
|
|
if (res != SQLITE_SCHEMA)
|
|
break;
|
|
}
|
|
|
|
db->setErr(res);
|
|
|
|
if (res == SQLITE_OK)
|
|
{
|
|
active = true;
|
|
ds_state = dsSelect;
|
|
//cout << "Curr size2 "<<num_rows()<<"\n";
|
|
this->first();
|
|
//cout << "Curr size3 "<<num_rows()<<"\n";
|
|
//cout << "Curr fcount "<<field_count()<<"\n\n";
|
|
}
|
|
|
|
return (res == SQLITE_OK);
|
|
}
|
|
|
|
bool SqliteDataset::query(const string & q)
|
|
{
|
|
return query(q.c_str());
|
|
}
|
|
|
|
void SqliteDataset::open(const string & sql)
|
|
{
|
|
set_select_sql(sql);
|
|
open();
|
|
}
|
|
|
|
void SqliteDataset::open()
|
|
{
|
|
if (select_sql.size())
|
|
{
|
|
query(select_sql.c_str());
|
|
}
|
|
else
|
|
{
|
|
ds_state = dsInactive;
|
|
}
|
|
}
|
|
|
|
|
|
void SqliteDataset::close()
|
|
{
|
|
Dataset::close();
|
|
result.record_header.clear();
|
|
result.records.clear();
|
|
edit_object->clear();
|
|
fields_object->clear();
|
|
ds_state = dsInactive;
|
|
active = false;
|
|
delete this;
|
|
}
|
|
|
|
|
|
void SqliteDataset::cancel()
|
|
{
|
|
if ((ds_state == dsInsert) || (ds_state == dsEdit))
|
|
{
|
|
if (result.record_header.size())
|
|
ds_state = dsSelect;
|
|
else
|
|
ds_state = dsInactive;
|
|
}
|
|
}
|
|
|
|
|
|
int SqliteDataset::num_rows()
|
|
{
|
|
return result.records.size();
|
|
}
|
|
|
|
|
|
bool SqliteDataset::eof()
|
|
{
|
|
return feof;
|
|
}
|
|
|
|
|
|
bool SqliteDataset::bof()
|
|
{
|
|
return fbof;
|
|
}
|
|
|
|
|
|
void SqliteDataset::first()
|
|
{
|
|
Dataset::first();
|
|
this->fill_fields();
|
|
//cout << "In first "<< fields_object->size()<<"\n";
|
|
}
|
|
|
|
void SqliteDataset::last()
|
|
{
|
|
Dataset::last();
|
|
fill_fields();
|
|
}
|
|
|
|
void SqliteDataset::prev(void)
|
|
{
|
|
Dataset::prev();
|
|
fill_fields();
|
|
}
|
|
|
|
void SqliteDataset::next(void)
|
|
{
|
|
Dataset::next();
|
|
if (!eof())
|
|
fill_fields();
|
|
}
|
|
|
|
|
|
//bool SqliteDataset::seek(int pos=0) {
|
|
bool SqliteDataset::seek(int pos)
|
|
{
|
|
if (ds_state == dsSelect)
|
|
{
|
|
Dataset::seek(pos);
|
|
fill_fields();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
long SqliteDataset::nextid(const char *seq_name)
|
|
{
|
|
if (handle())
|
|
return db->nextid(seq_name);
|
|
else
|
|
return DB_UNEXPECTED_RESULT;
|
|
}
|
|
|
|
/* Helper function */
|
|
void SetFieldType(result_set * r, Tables tables)
|
|
{
|
|
|
|
Tables::iterator it;
|
|
|
|
sqlite3_stmt *vm;
|
|
const char *tail;
|
|
unsigned int len;
|
|
|
|
// result_set res;
|
|
char sqlcmd[512];
|
|
|
|
for (it = tables.begin(); it != tables.end(); it++)
|
|
{
|
|
sprintf(sqlcmd, "PRAGMA table_info('%s')", (*it).data());
|
|
if (sqlite3_prepare(r->conn, sqlcmd, -1, &vm, &tail) != SQLITE_OK)
|
|
{
|
|
return;
|
|
}
|
|
while (sqlite3_step(vm) == SQLITE_ROW)
|
|
{
|
|
/* Type is in 2 */
|
|
/* field name is 1 */
|
|
/* not null indicator is 3 */
|
|
/* dflt value is 4 */
|
|
|
|
for (uint e = 0; e < r->record_header.size(); e++)
|
|
{
|
|
if (r->record_header[e].name ==
|
|
(char *) sqlite3_column_text(vm, 1) &&
|
|
r->record_header[e].field_table == (*it).data())
|
|
{
|
|
r->record_header[e].type = GetFieldType((char *)
|
|
sqlite3_column_text(vm, 2),
|
|
&len);
|
|
r->record_header[e].field_len = len;
|
|
/* Is not null */
|
|
r->record_header[e].notnull = sqlite3_column_text(vm, 3)[0];
|
|
}
|
|
} /* For end */
|
|
} /* while end */
|
|
|
|
sqlite3_finalize(vm);
|
|
}
|
|
}
|
|
|
|
/* Return fType and length from String field*/
|
|
fType GetFieldType(const char *Type, unsigned int *length)
|
|
{
|
|
|
|
char *upper;
|
|
char *_left, *_right;
|
|
fType rType; /* For return */
|
|
unsigned int rTypeLen = 0;
|
|
int i;
|
|
|
|
upper = GB.NewZeroString(Type);
|
|
for (i = 0; i < GB.StringLength(upper); i++)
|
|
upper[i] = toupper(upper[i]);
|
|
|
|
Type = upper;
|
|
if (!Type) Type = "";
|
|
|
|
if (strstr(Type, "CHAR(") /* note the opening bracket */
|
|
|| strstr(Type, "CLOB") || strstr(Type, "TEXT") /* also catches TINYTEXT */
|
|
|| strstr(Type, "VARCHAR")
|
|
|| strstr(Type, "ENUM") || strstr(Type, "SET") || strstr(Type, "YEAR"))
|
|
{ /* MySQL 2 or 4 digit year (string) */
|
|
rType = ft_String;
|
|
}
|
|
else if (strstr(Type, "CHAR") /* this is a 1-byte value */
|
|
|| strstr(Type, "TINYINT")
|
|
|| strstr(Type, "INT1") || strstr(Type, "BOOL"))
|
|
{
|
|
rType = ft_Boolean;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Boolean_Length;
|
|
}
|
|
else if (strstr(Type, "SMALLINT") || strstr(Type, "INT2"))
|
|
{
|
|
rType = ft_Short;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Short_Length;
|
|
}
|
|
else if (strstr(Type, "MEDIUMINT"))
|
|
{
|
|
rType = ft_Short;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Short_Length;
|
|
}
|
|
else if (strstr(Type, "BIGINT") || strstr(Type, "INT8"))
|
|
{
|
|
rType = ft_LongDouble;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_LongDouble_Length;
|
|
}
|
|
else if (strstr(Type, "INTEGER")
|
|
|| strstr(Type, "INT") || strstr(Type, "INT4"))
|
|
{
|
|
rType = ft_Long;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Long_Length;
|
|
}
|
|
else if (strstr(Type, "DECIMAL") || strstr(Type, "NUMERIC"))
|
|
{
|
|
rType = ft_Float;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Float_Length;
|
|
}
|
|
else if (strstr(Type, "TIMESTAMP") || strstr(Type, "DATETIME"))
|
|
{
|
|
rType = ft_Date;
|
|
rTypeLen = ft_Date_Length;
|
|
}
|
|
else if (strstr(Type, "DATE"))
|
|
{
|
|
rType = ft_Date;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Date_Length;
|
|
}
|
|
else if (strstr(Type, "TIME"))
|
|
{
|
|
rType = ft_Date;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Date_Length;
|
|
}
|
|
else if (strstr(Type, "DOUBLE") || strstr(Type, "FLOAT8"))
|
|
{
|
|
rType = ft_Double;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Double_Length;
|
|
}
|
|
else if (strstr(Type, "REAL") /* this is PostgreSQL "real", not
|
|
MySQL "real" which is a
|
|
synonym of "double" */
|
|
|| strstr(Type, "FLOAT")
|
|
|| strstr(Type, "FLOAT4"))
|
|
{
|
|
rType = ft_Float;
|
|
/* Length is for when value is used as a string */
|
|
rTypeLen = ft_Float_Length;
|
|
}
|
|
else if (strstr(Type, "BLOB")) // BM
|
|
{
|
|
rType = ft_Blob;
|
|
}
|
|
else
|
|
{
|
|
rType = ft_String;
|
|
/* most reasonable default */
|
|
}
|
|
|
|
if (rType == ft_String)
|
|
{
|
|
/* if a length has been defined it will be between () */
|
|
_right = (char *)rindex(Type, ')');
|
|
_left = (char *)index(Type, '(');
|
|
if (_right)
|
|
{
|
|
_right = '\0';
|
|
rTypeLen = atoi(_left + 1);
|
|
}
|
|
else
|
|
{
|
|
/* set a default length */
|
|
rTypeLen = DEFAULT_STRING_LENGTH;
|
|
}
|
|
}
|
|
|
|
if (length != NULL)
|
|
*length = rTypeLen;
|
|
|
|
GB.FreeString(&upper);
|
|
|
|
return rType;
|
|
}
|