-
Adam Knapp authored
Signed-off-by:
Adam Knapp <adam.knapp@ericsson.com>
Adam Knapp authoredSigned-off-by:
Adam Knapp <adam.knapp@ericsson.com>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
SQL_SQLite_interface.cc 9.51 KiB
/******************************************************************************
* Copyright (c) 2000-2023 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* Gabor Szalai - initial implementation and initial documentation
* Zoltan Medve
* Janos Kovesdi
******************************************************************************/
//
// File: SQL_SQLite_interface.cc
// Description: SQL test port SQLite interface source
// Rev: R6A
// Prodnr: CNL 113 760
//
#include "SQL_interface.hh"
#include "sqlite3.h"
#include "memory.h"
#include "string.h"
namespace SQL_engine{
SQL_engine_base *new_SQLite_engine();
}
static const SQL_engine::SQL_Engine_register_data SQLite_register_data=SQL_engine::SQL_Engine_register_data(SQL_engine::SQLite_engine, &SQL_engine::new_SQLite_engine);
static const SQL_engine::SQL_registrator local_iff=SQL_engine::SQL_registrator(&SQLite_register_data);
namespace SQL_engine{
class SQL_SQLite_engine: public SQL_engine_base{
public:
sqlite3 *sql_handle;
int state; // 0 - not connected
// 1 - connected, sqlite_close() must be called.
virtual int open_database_connection(const SQL_config_data*, SQL_error&);
virtual int execute_sql_command(const unsigned char *sql,
const unsigned long sql_length,
SQL_result_data &sql_res,
SQL_error&);
virtual int close_database_connection(SQL_error&);
SQL_SQLite_engine();
virtual ~SQL_SQLite_engine();
private:
typedef enum {utf8=0, utf16=1, v2=2} connection_types;
connection_types conn_type;
int busy_retries;
};
SQL_SQLite_engine::SQL_SQLite_engine(){
engine=SQLite_engine;
state=0;
sql_handle=NULL;
busy_retries = -1;
conn_type=utf8;
}//constructor
SQL_SQLite_engine::~SQL_SQLite_engine(){
if(state){
sqlite3_close(sql_handle);
}
}//desructor
int SQL_SQLite_engine::open_database_connection(const SQL_config_data *
config_data, SQL_error &err){
const char *database=NULL;//:memory: (memory db), or empty string "" (tmp file db)
int flags=0;
//SQLITE_OPEN_READONLY 0x00000001
//SQLITE_OPEN_READWRITE 0x00000002
//SQLITE_OPEN_CREATE 0x00000004
//SQLITE_OPEN_URI 0x00000040
//SQLITE_OPEN_MEMORY 0x00000080
//SQLITE_OPEN_NOMUTEX 0x00008000
//SQLITE_OPEN_FULLMUTEX 0x00010000
//SQLITE_OPEN_SHAREDCACHE 0x00020000
//SQLITE_OPEN_PRIVATECACHE 0x00040000
const char *zVfs=NULL; //if NULL pointer then the default sqlite3_vfs object
//is used ONLY DEFAULT SUPPORTED
int i=0;
int open=0;
while(config_data[i].name){
if(!strcasecmp(config_data[i].name, "database")){
database = config_data[i].value;
} else if(!strcasecmp(config_data[i].name, "type")){
if(!strcasecmp(config_data[i].value, "utf8")){
conn_type=utf8;
} else if(!strcasecmp(config_data[i].value, "utf16")){
conn_type=utf16;
} else if(!strcasecmp(config_data[i].value, "v2")){
conn_type=v2;
}
} else if(!strcasecmp(config_data[i].name, "flags")){
flags=atoi(config_data[i].value);
} else if(!strcasecmp(config_data[i].name, "max_busy_retries")) {
busy_retries = atoi((const char*)config_data[i].value);
if(busy_retries < 0) {
err.error_code=7;
err.error_text=mprintf("You have to specify positive max_busy_retries parameter.");
return 1;
}
}
++i;
}//while
if(database == NULL){
err.error_code=4;
err.error_text=mprintf("Database attribute is not specified. eg: file:/home/path/mydb.db");
return 1;
}
if(conn_type==utf8){
open = sqlite3_open(database, &sql_handle);
} else if(conn_type==utf16){
open = sqlite3_open16(database, &sql_handle);
} else if(conn_type==v2){
open = sqlite3_open_v2(database, &sql_handle, flags, zVfs);
}
if(open != SQLITE_OK){
err.error_code=5;
err.error_text=mprintf("SQLite connection error: %s",
sqlite3_errmsg(sql_handle));
return 1;
}
sqlite3_extended_result_codes(sql_handle,1);
state = 1;
return 0;
}//open_database_connection
int SQL_SQLite_engine::execute_sql_command(const unsigned char *sql,
const unsigned long sql_length,
SQL_result_data &sql_res,
SQL_error &err){
sqlite3_stmt *statement=NULL;
const char *tail=NULL;
int ncols=-1;
int nrows=0;
if(state==0){
err.error_code=5;
err.error_text=mprintf("The SQLite engine is not connected to the database. Please connect first.");
return 1;
}
if(sqlite3_prepare_v2(sql_handle, (const char*)sql, sql_length, &statement, &tail) == SQLITE_OK){
ncols = sqlite3_column_count(statement);
int step = sqlite3_step(statement);
for(int i = 1; (busy_retries != -1) ? (i <= busy_retries) : true; ++i){
if(step == SQLITE_DONE){
sql_res.number_of_rows=sqlite3_changes(sql_handle);
sql_res.rows=NULL;
break;
} // INSERT, UPDATE, DELETE
else if(step == SQLITE_ROW){
while(step == SQLITE_ROW){
nrows++;
step = sqlite3_step(statement);
}
sqlite3_reset(statement);
sql_res.number_of_rows=nrows;
sql_res.rows=(SQL_row_data *)Malloc(sql_res.number_of_rows*sizeof(SQL_row_data));
for(int i = 0; i < nrows; ++i){
if((step = sqlite3_step(statement)) != SQLITE_ROW){
err.error_code = 7;
err.error_text = mprintf("Step is %d not SQLITE_ROW!", step);
return 1;
}
SQL_row_data *row=sql_res.rows+i;
row->num_of_fields=ncols;
row->field_data=(SQL_field_data*)Malloc(row->num_of_fields*sizeof(SQL_field_data));
for(int j = 0; j < ncols; ++j){
SQL_field_data *act_field=row->field_data+j;
if(i == 0){
act_field->field_name=mcopystr(sqlite3_column_name(statement,j));
} else {
act_field->field_name=NULL;
}
act_field->data_length=sqlite3_column_bytes(statement, j);
switch(sqlite3_column_type(statement, j)){
case SQLITE_INTEGER:
act_field->field_type=int_data;
act_field->field_data.int_data=sqlite3_column_int(statement, j);
break;
case SQLITE_TEXT:
act_field->field_type=text;
act_field->field_data.text_binary_data=(unsigned char*)Malloc(act_field->data_length*sizeof(unsigned char));
memcpy(act_field->field_data.text_binary_data, sqlite3_column_text(statement, j), act_field->data_length);
break;
case SQLITE_FLOAT:
act_field->field_type=real_data;
act_field->field_data.real_data=sqlite3_column_double(statement, j);
break;
case SQLITE_BLOB:
act_field->field_type=binary_data;
act_field->field_data.text_binary_data=(unsigned char*)Malloc(act_field->data_length*sizeof(unsigned char));
memcpy(act_field->field_data.text_binary_data, sqlite3_column_blob(statement, j), act_field->data_length);
break;
case SQLITE_NULL:
act_field->field_type=null_data_value;
break;
default:
err.error_code=7;
err.error_text=mprintf("Unknown field type");
sqlite3_finalize(statement);
return 1;
};
}//for ncols
}//for nrows
break;
}//if statement has rows (SELECT)
else if (step == SQLITE_BUSY || step == SQLITE_IOERR_BLOCKED){
if(busy_retries != -1 && i == busy_retries) {
err.error_code=7;
err.error_text=mprintf("Max retry reached. Giving up.");
return 1;
}
if(sqlite3_busy_timeout(sql_handle, i*15)==0){
step = sqlite3_step(statement);
}
log(SQLTP_LOG_WARNING, "Database is busy. %d. retry: %s.",i, sql);
continue;
}//if step SQLITE_BUSY
else {
err.error_code=7;
err.error_text=mprintf("Error while query: %d", step);
sqlite3_finalize(statement);
return 1;
}
}//for true
sqlite3_finalize(statement);
} else {
err.error_code=6;
if(tail != NULL){
err.error_text=mprintf("Error with query: %s", tail);
} else {
err.error_text=mprintf("Error with query.");
}
return 1;
}
return 0;
}//execute_sql_command
int SQL_SQLite_engine::close_database_connection(SQL_error &err){
if(sqlite3_close(sql_handle) == SQLITE_OK){
state=0;
return 0;
}
err.error_code=7;
err.error_text=mprintf("The database connection is associated with unfinalized prepared statements or unfinished sqlite3_backup objects.");
return 1;
}//close_database_connection
SQL_engine_base* new_SQLite_engine(){
return new SQL_SQLite_engine;
}
}