From: Eduardo Ramos Testillano Date: Sun, 7 Jul 2019 02:24:46 +0000 (+0200) Subject: Revert "Remove mysql and oracle resources for anna-ericsson project" X-Git-Url: https://git.teslayout.com/public/public/public/?a=commitdiff_plain;h=78be86969d2f26a9084b0c4af6ce43d5fa4ed3fd;p=anna.git Revert "Remove mysql and oracle resources for anna-ericsson project" This reverts commit a3b95648bd76140ef55e0b5941d423eee6c3856f. --- diff --git a/example/dbms.mysql/insert/libraries.txt b/example/dbms.mysql/insert/libraries.txt new file mode 100644 index 0000000..18c098a --- /dev/null +++ b/example/dbms.mysql/insert/libraries.txt @@ -0,0 +1 @@ +mysqlclient diff --git a/example/dbms.mysql/insert/main.cpp b/example/dbms.mysql/insert/main.cpp new file mode 100644 index 0000000..7ed298e --- /dev/null +++ b/example/dbms.mysql/insert/main.cpp @@ -0,0 +1,141 @@ +#include +#include +#include + +#include + +#define STRING_SIZE 50 + +#define INSERT_SAMPLE "insert into anna_db_test (xx, yy, zz, tt) values (?,?,?,?)" + + +#define MAXCOLUMN 4 + +MYSQL* mysql; +MYSQL_STMT *stmt; +MYSQL_BIND bind[MAXCOLUMN]; +MYSQL_TIME ts; +unsigned long length[MAXCOLUMN]; +int param_count, column_count, row_count; +float float_data; +int int_data; +char str_data[STRING_SIZE]; +my_bool is_null[MAXCOLUMN]; + +/* + * From http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html + */ +int main (int argc, const char** argv) +{ + if ((mysql = mysql_init (NULL)) == NULL) + exit(-12); + + if (mysql_real_connect (mysql, NULL, "sdp", "sdp", "test", 0, NULL, 0L) == NULL) { + fprintf(stderr, " mysql_stmt_prepare(), SELECT failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + /* Prepare a SELECT query to fetch data from test_table */ + stmt = mysql_stmt_init(mysql); + if (!stmt) + { + fprintf(stderr, " mysql_stmt_init(), out of memory\n"); + exit(0); + } + + if (mysql_stmt_prepare(stmt, INSERT_SAMPLE, strlen(INSERT_SAMPLE))) + { + fprintf(stderr, " mysql_stmt_prepare(), INSERT failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + fprintf(stdout, " prepare, INSERT successful\n"); + + /* Get the parameter count from the statement */ + param_count= mysql_stmt_param_count(stmt); + fprintf(stdout, " total parameters in INSERT: %d\n", param_count); + + if (param_count != MAXCOLUMN) /* validate parameter count */ + { + fprintf(stderr, " invalid parameter count returned by MySQL\n"); + exit(0); + } + + /* Bind the result buffers for all 3 columns before fetching them */ + + memset(bind, 0, sizeof(bind)); + + /* INTEGER COLUMN */ + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= (char *)&int_data; + bind[0].is_null= &is_null[0]; + bind[0].length= &length[0]; + + /* STRING COLUMN */ + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= (char *)str_data; + bind[1].buffer_length= STRING_SIZE; + bind[1].is_null= &is_null[1]; + bind[1].length= &length [1]; + + /* FLOAT COLUMN */ + bind[2].buffer_type= MYSQL_TYPE_FLOAT; + bind[2].buffer= (char *)&float_data; + bind[2].is_null= &is_null[2]; + bind[2].length= &length[2]; + + /* TIME COLUMN */ + bind[3].buffer_type= MYSQL_TYPE_DATETIME; + bind[3].buffer= (char *)&ts; + bind[3].is_null= &is_null[2]; + bind[3].length= &length[2]; + + /* Bind the result buffers */ + if (mysql_stmt_bind_param (stmt, bind)) + { + fprintf(stderr, " mysql_stmt_bind_param() failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + int_data = 99; + strcpy (str_data, "insert: 99"); + length [1] = strlen (str_data); + float_data = 0.99; + ts.year = 1996; ts.month = 2; ts.day = 11; + ts.hour = 19; ts.minute = 22; ts.second = 0; + + /* Execute the INSERT query */ + if (mysql_stmt_execute(stmt)) + { + fprintf(stderr, " mysql_stmt_execute(), failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + is_null [0] = 1; + strcpy (str_data, "insert: "); + length [1] = strlen (str_data); + float_data = 0.98; + ts.year = 1999; ts.month = 8; ts.day = 7; + ts.hour = 19; ts.minute = 18; ts.second = 17; + + /* Execute the INSERT query */ + if (mysql_stmt_execute(stmt)) + { + fprintf(stderr, " mysql_stmt_execute(), failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + /* Close the statement */ + if (mysql_stmt_close(stmt)) + { + fprintf(stderr, " failed while closing the statement\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + return 0; +} diff --git a/example/dbms.mysql/select/libraries.txt b/example/dbms.mysql/select/libraries.txt new file mode 100644 index 0000000..18c098a --- /dev/null +++ b/example/dbms.mysql/select/libraries.txt @@ -0,0 +1 @@ +mysqlclient diff --git a/example/dbms.mysql/select/main.cpp b/example/dbms.mysql/select/main.cpp new file mode 100644 index 0000000..3a13785 --- /dev/null +++ b/example/dbms.mysql/select/main.cpp @@ -0,0 +1,188 @@ +#include +#include + +#include + +#define STRING_SIZE 50 + +#define SELECT_SAMPLE "SELECT xx, yy, zz, tt FROM anna_db_test" + +#define MAXCOLUMN 4 + +MYSQL* mysql; +MYSQL_STMT *stmt; +MYSQL_BIND bind[MAXCOLUMN]; +MYSQL_RES *prepare_meta_result; +MYSQL_TIME ts; +unsigned long length[MAXCOLUMN]; +int param_count, column_count, row_count; +float float_data; +int int_data; +char str_data[STRING_SIZE]; +my_bool is_null[MAXCOLUMN]; + +/* + * From http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html + */ +int main (int argc, const char** argv) +{ + if ((mysql = mysql_init (NULL)) == NULL) + exit (-12); + + if (mysql_real_connect (mysql, NULL, "sdp", "sdp", "test", 0, NULL, 0L) == NULL) { + fprintf(stderr, " mysql_stmt_prepare(), SELECT failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + /* Prepare a SELECT query to fetch data from test_table */ + stmt = mysql_stmt_init(mysql); + if (!stmt) + { + fprintf(stderr, " mysql_stmt_init(), out of memory\n"); + exit(0); + } + + if (mysql_stmt_prepare(stmt, SELECT_SAMPLE, strlen(SELECT_SAMPLE))) + { + fprintf(stderr, " mysql_stmt_prepare(), SELECT failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + fprintf(stdout, " prepare, SELECT successful\n"); + + /* Get the parameter count from the statement */ + param_count= mysql_stmt_param_count(stmt); + fprintf(stdout, " total parameters in SELECT: %d\n", param_count); + + if (param_count != 0) /* validate parameter count */ + { + fprintf(stderr, " invalid parameter count returned by MySQL\n"); + exit(0); + } + + /* Fetch result set meta information */ + prepare_meta_result = mysql_stmt_result_metadata(stmt); + if (!prepare_meta_result) + { + fprintf(stderr," mysql_stmt_result_metadata(), returned no meta information\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + /* Get total columns in the query */ + column_count= mysql_num_fields(prepare_meta_result); + fprintf(stdout, " total columns in SELECT statement: %d\n", column_count); + + if (column_count != MAXCOLUMN) /* validate column count */ + { + fprintf(stderr, " invalid column count returned by MySQL\n"); + exit(0); + } + + /* Execute the SELECT query */ + if (mysql_stmt_execute(stmt)) + { + fprintf(stderr, " mysql_stmt_execute(), failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + /* Bind the result buffers for all 4 columns before fetching them */ + + memset(bind, 0, sizeof(bind)); + + /* INTEGER COLUMN */ + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= (char *)&int_data; + bind[0].is_null= &is_null[0]; + bind[0].length= &length[0]; + + /* STRING COLUMN */ + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= (char *)str_data; + bind[1].buffer_length= STRING_SIZE; + bind[1].is_null= &is_null[1]; + bind[1].length= &length[1]; + + /* SMALLINT COLUMN */ + bind[2].buffer_type= MYSQL_TYPE_FLOAT; + bind[2].buffer= (char *)&float_data; + bind[2].is_null= &is_null[2]; + bind[2].length= &length[2]; + + /* TIMESTAMP COLUMN */ + bind[3].buffer_type= MYSQL_TYPE_TIMESTAMP; + bind[3].buffer= (char *)&ts; + bind[3].is_null= &is_null[3]; + bind[3].length= &length[3]; + + /* Bind the result buffers */ + if (mysql_stmt_bind_result(stmt, bind)) + { + fprintf(stderr, " mysql_stmt_bind_result() failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + /* Now buffer all results to client (optional step) */ + if (mysql_stmt_store_result(stmt)) + { + fprintf(stderr, " mysql_stmt_store_result() failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + + /* Fetch all rows */ + row_count= 0; + fprintf(stdout, "Fetching results ...\n"); + while (!mysql_stmt_fetch(stmt)) + { + row_count++; + fprintf(stdout, " row %d\n", row_count); + + /* column 1 */ + fprintf(stdout, " column1 (integer) : "); + if (is_null[0]) + fprintf(stdout, " NULL\n"); + else + fprintf(stdout, " %d(%ld)\n", int_data, length[0]); + + /* column 2 */ + fprintf(stdout, " column2 (string) : "); + if (is_null[1]) + fprintf(stdout, " NULL\n"); + else + fprintf(stdout, " %s(%ld)\n", str_data, length[1]); + + /* column 3 */ + fprintf(stdout, " column3 (float) : "); + if (is_null[2]) + fprintf(stdout, " NULL\n"); + else + fprintf(stdout, " %f(%ld)\n", float_data, length[2]); + + /* column 4 */ + fprintf(stdout, " column4 (timestamp): "); + if (is_null[3]) + fprintf(stdout, " NULL\n"); + else + fprintf(stdout, " %04d-%02d-%02d %02d:%02d:%02d (%ld)\n", + ts.year, ts.month, ts.day, + ts.hour, ts.minute, ts.second, + length[3]); + fprintf(stdout, "\n"); + } + + /* Free the prepared result metadata */ + mysql_free_result(prepare_meta_result); + + /* Close the statement */ + if (mysql_stmt_close(stmt)) + { + fprintf(stderr, " failed while closing the statement\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(0); + } + +} diff --git a/example/dbms.mysql/xInsert/libraries.txt b/example/dbms.mysql/xInsert/libraries.txt new file mode 100644 index 0000000..9806884 --- /dev/null +++ b/example/dbms.mysql/xInsert/libraries.txt @@ -0,0 +1,10 @@ +anna_dbms.mysql_static +anna_dbms_static +anna_comm_static +anna_app_static +anna_xml_static +anna_io_static +anna_core_static +mysqlclient +rt +xml2 diff --git a/example/dbms.mysql/xInsert/main.cpp b/example/dbms.mysql/xInsert/main.cpp new file mode 100644 index 0000000..d52b9a1 --- /dev/null +++ b/example/dbms.mysql/xInsert/main.cpp @@ -0,0 +1,156 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include + +#include +#include + +class Insert : public Application { +public: + Insert (); + ~Insert () { delete a_db; } + +private: + anna::dbms::Database* a_db; + + void initialize () throw (RuntimeException); + void run () throw (RuntimeException); +}; + +using namespace std; + +int main (int argc, const char** argv) +{ + CommandLine& commandLine (CommandLine::instantiate ()); + Insert testNull; + + try { + commandLine.initialize (argv, argc); + commandLine.verify (); + + Logger::setLevel (Logger::Debug); + Logger::initialize ("copy", new TraceWriter ("xinsert.trace", 1024 * 1024)); + + testNull.start (); + } + catch (Exception& ex) { + cout << ex.asString () << endl; + } + + return 0; +} + +Insert::Insert () : + Application ("xinsert", "Copiador de la tabla ad_funcionalidades", "1.0"), + a_db (NULL) +{ + CommandLine& cl (CommandLine::instantiate ()); + + cl.add ("user", CommandLine::Argument::Mandatory, "Nombre del usuario"); + cl.add ("password", CommandLine::Argument::Mandatory, "Clave del usuario"); + cl.add ("host", CommandLine::Argument::Optional, "Nombre de la maquina donde se ubica el MySQL"); + cl.add ("db", CommandLine::Argument::Optional, "Nombre de la base de datos"); +} + +/* + * Las sentencias SQL usadas por este programana estaba originalmente escritas para Oracle, + * pero no hay que cambiar cada sentencia manualmente, s�lo hay que activar el traductor + * correspondiente y anna.dbms.mysql lo hace autom�ticamente. + */ +void Insert::initialize () + throw (RuntimeException) +{ + CommandLine& ccll = CommandLine::instantiate (); + const char* host = ccll.exists ("host") ? ccll.getValue ("host"): NULL; + + a_db = new anna::dbms::mysql::Database (ccll.getValue ("db"), host); + a_db->setStatementTranslator (dbms::mysql::OracleTranslator::instantiate ()); +} + +void Insert::run () + throw (RuntimeException) +{ + LOGMETHOD (TraceMethod tm ("Insert", "run", ANNA_FILE_LOCATION)); + + CommandLine& cl (CommandLine::instantiate ()); + + Statement* create; + Statement* insert; + + dbms::Integer n (true); + dbms::String name (15, true); + dbms::Float zz (true); + dbms::TimeStamp time (true); + + dbms::ResultCode resultCode; + + try { + Connection* connection = a_db->createConnection ("xinsert", cl.getValue ("user"), cl.getValue ("password")); + + { + Guard guard (connection); + + try { + create = a_db->createStatement ( + "create", "create table anna_db_test (xx integer, yy varchar (15), zz float, tt datetime)" + ); + + resultCode = connection->execute (create); + } + catch (Exception& ex) { + ex.trace (); + } + cout << "Creando: " << resultCode.asString () << endl << endl; + } + Guard guard (connection); + + /* + * Observar que la sentencia indica los parámetros tal y como se haría en Oracle. + */ + insert = a_db->createStatement ("select", "insert into anna_db_test (xx, yy, zz, tt) values (:x,:y,:z, :t)"); + insert->bindInput ("XX", n); + insert->bindInput ("YY", name); + insert->bindInput ("zz", zz); + insert->bindInput ("tt", time); + + n = 88; + name = "El 88"; + zz = 0.88; + time.setValue (anna::functions::second ()); + cout << endl << " --- Insertando 1 ---" << endl; + resultCode = connection->execute (insert); + cout << resultCode.asString () << endl << endl; + + n = 1010; + name = "El 1010"; + zz = 0.1010; + time.setNull (true); + cout << endl << " --- Insertando 2 (date=null)---" << endl; + resultCode = connection->execute (insert); + cout << resultCode.asString () << endl << endl; + + n = 89; + name.setNull (true); + zz = 0.89; + time.setValue ((Second)(anna::functions::second () + 100)); + cout << endl << " --- Insertando 2 (name=null)---" << endl; + resultCode = connection->execute (insert); + cout << resultCode.asString () << endl << endl; + } + catch (dbms::DatabaseException& edb) { + throw RuntimeException (edb); + } +} + + + diff --git a/example/dbms.mysql/xSelect/libraries.txt b/example/dbms.mysql/xSelect/libraries.txt new file mode 100644 index 0000000..9806884 --- /dev/null +++ b/example/dbms.mysql/xSelect/libraries.txt @@ -0,0 +1,10 @@ +anna_dbms.mysql_static +anna_dbms_static +anna_comm_static +anna_app_static +anna_xml_static +anna_io_static +anna_core_static +mysqlclient +rt +xml2 diff --git a/example/dbms.mysql/xSelect/main.cpp b/example/dbms.mysql/xSelect/main.cpp new file mode 100644 index 0000000..6f7fdcc --- /dev/null +++ b/example/dbms.mysql/xSelect/main.cpp @@ -0,0 +1,139 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include + +#include +#include + +class Select : public Application { +public: + Select (); + ~Select () { delete a_db; } + +private: + anna::dbms::Database* a_db; + + void initialize () throw (RuntimeException); + void run () throw (RuntimeException); +}; + +using namespace std; + +int main (int argc, const char** argv) +{ + CommandLine& commandLine (CommandLine::instantiate ()); + Select testNull; + + try { + commandLine.initialize (argv, argc); + commandLine.verify (); + + Logger::setLevel (Logger::Debug); + Logger::initialize ("copy", new TraceWriter ("xselect.trace", 1024 * 1024)); + + testNull.start (); + } + catch (Exception& ex) { + cout << ex.asString () << endl; + } + + return 0; +} + +Select::Select () : + Application ("xselect", "Copiador de la tabla ad_funcionalidades", "1.0"), + a_db (NULL) +{ + CommandLine& cl (CommandLine::instantiate ()); + + cl.add ("user", CommandLine::Argument::Mandatory, "Nombre del usuario"); + cl.add ("password", CommandLine::Argument::Mandatory, "Clave del usuario"); + cl.add ("host", CommandLine::Argument::Optional, "Nombre de la maquina donde se ubica el MySQL"); + cl.add ("db", CommandLine::Argument::Optional, "Nombre de la base de datos"); +} + +/* + * Las sentencias SQL usadas por este programana estaba originalmente escritas para Oracle, + * pero no hay que cambiar cada sentencia manualmente, s�lo hay que activar el traductor + * correspondiente y anna.dbms.mysql lo hace autom�ticamente. + */ +void Select::initialize () + throw (RuntimeException) +{ + CommandLine& ccll = CommandLine::instantiate (); + const char* host = ccll.exists ("host") ? ccll.getValue ("host"): NULL; + + a_db = new anna::dbms::mysql::Database (ccll.getValue ("db"), host); + a_db->setStatementTranslator (dbms::mysql::OracleTranslator::instantiate ()); +} + +void Select::run () + throw (RuntimeException) +{ + LOGMETHOD (TraceMethod tm ("Select", "run", ANNA_FILE_LOCATION)); + + CommandLine& cl (CommandLine::instantiate ()); + + Statement* select; + + dbms::Integer n (true); + dbms::String name (15, true); + dbms::Float zz (true); + dbms::TimeStamp tt (true); + + dbms::ResultCode resultCode; + + try { + Connection* connection = a_db->createConnection ("xselect", cl.getValue ("user"), cl.getValue ("password")); + + Guard guard (connection); + + select = a_db->createStatement ("select", "select xx,yy,zz, tt from anna_db_test"); + select->bindOutput ("XX", n); + select->bindOutput ("YY", name); + select->bindOutput ("zz", zz); + select->bindOutput ("tt", tt); + + cout << endl << " --- Leyendo ---" << endl; + resultCode = connection->execute (select); + + if (resultCode.successful () == true) { + while (select->fetch () == true) { + if (n.isNull () == true) + cout << ""; + else + cout << n; + + cout << " | YY: " << ((name.isNull () == true) ? "": name); + + cout << " | ZZ: "; + if (zz.isNull () == true) + cout << ""; + else + cout << zz.getValue (); + + cout << " | TT: " << ((tt.isNull () == true) ? "": tt.getCStringValue ()); + + cout << endl; + } + } + else + cout << resultCode.asString () << endl << endl; + } + catch (dbms::DatabaseException& edb) { + throw RuntimeException (edb); + } +} + + + diff --git a/example/dbos/workdir/filesystem/Abstract.cpp b/example/dbos/workdir/filesystem/Abstract.cpp new file mode 100644 index 0000000..f304470 --- /dev/null +++ b/example/dbos/workdir/filesystem/Abstract.cpp @@ -0,0 +1,52 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include "Abstract.hpp" + +using namespace std; +using namespace anna; +using namespace workdir; + +filesystem::Abstract::Abstract (const Abstract::ClassType::_v classType, const std::string& name) : + a_classType (classType), + a_name (name), a_parent (NULL) +{ + a_path = name; +} + +filesystem::Abstract::Abstract (const Abstract::ClassType::_v classType, filesystem::Abstract* parent, const std::string& name) : + a_classType (classType), + a_name (name), + a_parent (parent) +{ + a_parent->a_children.push_back (this); + a_path = calculePath (parent, name); +} + +/*virtual*/ +filesystem::Abstract::~Abstract () +{ + for (child_iterator ii = child_begin (), maxii = child_end (); ii != maxii; ii ++) + delete child (ii); + + a_children.clear (); +} + +/* static */ +string filesystem::Abstract::calculePath (const filesystem::Abstract* parent, const std::string& shortPath) + throw () +{ + string result; + + if (parent!= NULL) { + result = parent->a_path; + result += '/'; + } + + return result += shortPath; +} diff --git a/example/dbos/workdir/filesystem/Abstract.hpp b/example/dbos/workdir/filesystem/Abstract.hpp new file mode 100644 index 0000000..de28336 --- /dev/null +++ b/example/dbos/workdir/filesystem/Abstract.hpp @@ -0,0 +1,69 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef dbos_workdir_filesystem_Abstract_hpp +#define dbos_workdir_filesystem_Abstract_hpp + +#include +#include + +#include + +namespace workdir { + +namespace filesystem { + +using namespace anna; + +class Abstract { +public: + struct ClassType { enum _v { Directory, File }; }; + + typedef std::vector child_container; + typedef child_container::iterator child_iterator; + typedef child_container::const_iterator const_child_iterator; + + virtual ~Abstract (); + + Abstract* getParent () const throw () { return a_parent; } + ClassType::_v getClassType () const throw () { return a_classType; } + const std::string& getName () const throw () { return a_name; } + const std::string& getPath () const throw () { return a_path; } + + int child_size () const throw () { return a_children.size (); } + + child_iterator child_begin () throw () { return a_children.begin (); } + child_iterator child_end () throw () { return a_children.end (); } + static Abstract* child (child_iterator ii) throw () { return *ii; } + + const_child_iterator child_begin () const throw () { return a_children.begin (); } + const_child_iterator child_end () const throw () { return a_children.end (); } + static const Abstract* child (const_child_iterator ii) throw () { return *ii; } + + static std::string calculePath (const Abstract* parent, const std::string& shortPath) throw (); + + virtual void print (const int level) const throw () = 0; + +protected: + Abstract (const ClassType::_v, const std::string& name); + Abstract (const ClassType::_v, Abstract* parent, const std::string& name); + +private: + const ClassType::_v a_classType; + Abstract* a_parent; + const std::string a_name; + std::string a_path; + child_container a_children; + + Abstract (const Abstract&); +}; + +} +} + +#endif diff --git a/example/dbos/workdir/filesystem/Directory.cpp b/example/dbos/workdir/filesystem/Directory.cpp new file mode 100644 index 0000000..e22c859 --- /dev/null +++ b/example/dbos/workdir/filesystem/Directory.cpp @@ -0,0 +1,69 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include "Directory.hpp" +#include "File.hpp" + +using namespace std; +using namespace anna; +using namespace workdir; + +void filesystem::Directory::print (const int level) const + throw () +{ + filesystem::Directory::const_child_iterator ii; + filesystem::Directory::const_child_iterator maxii = child_end (); + const filesystem::Directory* auxd; + const filesystem::File* auxf; + int row = 0; + bool hasDirectories = false; + + for (int space = 0; space < level; space ++) + cout << " "; + + cout << getName () << "/ (" << child_size () << "): " << endl; + + /* + * Recorre todas las dependencias y visualiza primero todos los ficheros + */ + for (ii = child_begin (); ii != maxii; ii ++) { + auxf = filesystem::File::down_cast (filesystem::Directory::child (ii)); + + if (auxf == NULL) { + hasDirectories = true; + continue; + } + + if (row == 0) { + for (int space = 0; space < level + 1; space ++) + cout << " "; + } + + auxf->print (level + 1); + + if (++ row == 5) { + cout << endl; + row = 0; + } + } + + if (row > 0) + cout << endl; + + /* + * Trata los directorios recursivamente + */ + for (ii = child_begin (); hasDirectories == true && ii != maxii; ii ++) { + auxd = filesystem::Directory::down_cast (filesystem::Directory::child (ii)); + + if (auxd != NULL) + auxd->print (level + 1); + } +} diff --git a/example/dbos/workdir/filesystem/Directory.hpp b/example/dbos/workdir/filesystem/Directory.hpp new file mode 100644 index 0000000..95490d7 --- /dev/null +++ b/example/dbos/workdir/filesystem/Directory.hpp @@ -0,0 +1,38 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef dbos_workdir_filesystem_Directory_hpp +#define dbos_workdir_filesystem_Directory_hpp + +#include "Abstract.hpp" + +namespace workdir { + +namespace filesystem { + +using namespace anna; + +class Directory : public Abstract { +public: + Directory (const std::string& name) : Abstract (ClassType::Directory, name) {;} + Directory (Directory* parent, const std::string& name) : Abstract (ClassType::Directory, parent, name) {;} + + void print (const int level = 0) const throw (); + + static Directory* down_cast (Abstract* abstract) throw () { + return (abstract->getClassType () == ClassType::Directory) ? static_cast (abstract): NULL; + } + static const Directory* down_cast (const Abstract* abstract) throw () { + return (abstract->getClassType () == ClassType::Directory) ? static_cast (abstract): NULL; + } +}; + +} +} + +#endif diff --git a/example/dbos/workdir/filesystem/File.cpp b/example/dbos/workdir/filesystem/File.cpp new file mode 100644 index 0000000..888f46a --- /dev/null +++ b/example/dbos/workdir/filesystem/File.cpp @@ -0,0 +1,21 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include "File.hpp" + +using namespace std; +using namespace anna; +using namespace workdir; + +void filesystem::File::print (const int) const + throw () +{ + cout << getName () << " " << flush; +} diff --git a/example/dbos/workdir/filesystem/File.hpp b/example/dbos/workdir/filesystem/File.hpp new file mode 100644 index 0000000..f3da2df --- /dev/null +++ b/example/dbos/workdir/filesystem/File.hpp @@ -0,0 +1,38 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef dbos_workdir_filesystem_File_hpp +#define dbos_workdir_filesystem_File_hpp + +#include "Abstract.hpp" +#include "Directory.hpp" + +namespace workdir { + +namespace filesystem { + +using namespace anna; + +class File : public Abstract { +public: + File (Directory* parent, const std::string& name) : Abstract (ClassType::File, parent, name) {;} + + void print (const int level) const throw (); + + static File* down_cast (Abstract* abstract) throw () { + return (abstract->getClassType () == ClassType::File) ? static_cast (abstract): NULL; + } + static const File* down_cast (const Abstract* abstract) throw () { + return (abstract->getClassType () == ClassType::File) ? static_cast (abstract): NULL; + } +}; + +} +} + +#endif diff --git a/example/dbos/workdir/libraries.txt b/example/dbos/workdir/libraries.txt new file mode 100644 index 0000000..5a99acb --- /dev/null +++ b/example/dbos/workdir/libraries.txt @@ -0,0 +1,9 @@ +anna_dbos_static +anna_dbms_static +anna_comm_static +anna_app_static +anna_xml_static +anna_io_static +anna_core_static +rt +xml2 diff --git a/example/dbos/workdir/main.cpp b/example/dbos/workdir/main.cpp new file mode 100644 index 0000000..3b9c99f --- /dev/null +++ b/example/dbos/workdir/main.cpp @@ -0,0 +1,351 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "filesystem/Directory.hpp" +#include "filesystem/File.hpp" + +#include "storage/Directory.hpp" +#include "storage/File.hpp" + +using namespace std; +using namespace workdir; + +template void message (const char* text, T* tt) throw () { + if (Logger::isActive (Logger::Debug) == false) + return; + + cout << " " << text << " ("; + cout << anna::functions::asHexString (anna_ptrnumber_cast (tt)); + cout << "): "; + + if (tt != NULL) + cout << tt->asString (); + else + cout << ""; + + cout << endl; +} + +class WorkDirectory : public Application { +public: + struct Flags { enum _v { None = 0, Clear = 1 }; }; + + WorkDirectory (); + + xml::Node* asXML (xml::Node* parent) const throw (); + +private: + typedef vector file_container; + typedef file_container::iterator file_iterator; + typedef file_container::reverse_iterator file_reverse_iterator; + + dbos::Repository a_repository; + filesystem::Directory* a_root; + file_container a_files; + + void initialize () throw (RuntimeException); + void run () throw (RuntimeException); + + void forward (filesystem::Directory*) throw (RuntimeException); + void instantiateOne (filesystem::Directory*) throw (RuntimeException); + void fullCache (filesystem::Directory*, file_container&, const int flags) throw (RuntimeException); + void reuseHoles (filesystem::Directory*, file_container&) throw (RuntimeException); + void destroyObjects (filesystem::Directory* dir, file_container&) throw (RuntimeException); + void clear (file_container&) throw (); + + static void load (filesystem::Directory* parent, const int maxLevel, const int level = 0) throw (RuntimeException); +}; + +using namespace std; + +int main (int argc, const char** argv) +{ + CommandLine& commandLine (CommandLine::instantiate ()); + WorkDirectory storageNull; + + try { + commandLine.initialize (argv, argc); + commandLine.verify (); + + Logger::setLevel (Logger::Local6); + Logger::initialize ("workdir", new TraceWriter ("file.trace", 4 * 1024 * 1024)); + + storageNull.start (); + } + catch (Exception& ex) { + cout << ex.asString () << endl; + } + + return 0; +} + +WorkDirectory::WorkDirectory () : + Application ("workdir", "Dbos workdir", "1.0.0"), + a_repository ("workdir") +{ + CommandLine& cl (CommandLine::instantiate ()); + + cl.add ("dir", CommandLine::Argument::Mandatory, "Nombre del directorio a procesar"); + cl.add ("l", CommandLine::Argument::Optional, "Numero maximo de niveles de profundidad"); + cl.add ("trace", CommandLine::Argument::Optional, "Nivel de trazado"); + cl.add ("s", CommandLine::Argument::Mandatory, "Cache size"); +} + +void WorkDirectory::initialize () + throw (RuntimeException) +{ +} + +void WorkDirectory::run () + throw (RuntimeException) +{ + LOGMETHOD (TraceMethod tm ("WorkDirectory", "run", ANNA_FILE_LOCATION)); + + CommandLine& cl (CommandLine::instantiate ()); + + const std::string& dir = cl.getValue ("dir"); + const int maxLevel = cl.exists ("l") ? cl.getIntegerValue ("l"): -1; + + if (cl.exists ("trace") == true) + Logger::setLevel (Logger::asLevel (cl.getValue ("trace"))); + + const int cacheSize = cl.getIntegerValue ("s"); + + storage::Directory::setup (a_repository, cacheSize); + storage::File::setup (a_repository, cacheSize); + + a_root = new filesystem::Directory (dir); + + load (a_root, maxLevel); + + a_root->print (); + cout << endl; + + forward (a_root); + + writeContext ("file.context"); +} + +xml::Node* WorkDirectory::asXML (xml::Node* parent) const + throw () +{ + xml::Node* result = app::Application::asXML (parent); + a_repository.asXML (result); + return result; +} + +void WorkDirectory::forward (filesystem::Directory* dir) + throw (RuntimeException) +{ + cout << "forward: " << dir->getPath () << endl; + + for (filesystem::Directory::child_iterator ii = dir->child_begin (), maxii = dir->child_end (); ii != maxii; ii ++) { + dir = filesystem::Directory::down_cast (filesystem::Directory::child (ii)); + + if (dir == NULL) + continue; + + try { + instantiateOne (dir); + fullCache (dir, a_files, Flags::Clear); + reuseHoles (dir, a_files); + destroyObjects (dir, a_files); + forward (dir); + } + catch (RuntimeException& ex) { + ex.trace (); + } + } +} + +/* + * Crea una serie de instancia simultáneas sobre el mismo objeto para verificar que sólo lo carga + * de verdad sea necesario. + */ +void WorkDirectory::instantiateOne (filesystem::Directory* dir) + throw (RuntimeException) +{ + TraceMethod tm ("WorkDirectory", "instantiateOne", ANNA_FILE_LOCATION); + + cout << "WorkDirectory::instantiateOne: Instancia varias veces la misma instancia para verificar el reuso" << endl; + + storage::Directory* aux; + + { + dbos::AutoObject root1; + aux = root1 = storage::Directory::instantiate (dir); + message ("Root1", aux); + + dbos::AutoObject root2; + aux = root2 = storage::Directory::instantiate (dir); + message ("Root2", aux); + } + + dbos::AutoObject root3; + aux = root3 = storage::Directory::instantiate (dir); + message ("Root3", aux); + + message ("StorageArea", storage::File::getStorageArea ()); + + cout << endl; +} + +void WorkDirectory::fullCache (filesystem::Directory* dir, WorkDirectory::file_container& files, const int flags) + throw (RuntimeException) +{ + TraceMethod tm ("WorkDirectory", "fullCache", ANNA_FILE_LOCATION); + + cout << "WorkDirectory::fullCache: Llena la cache de objetos para verificar que crece tanto como sea necesario" << endl; + + filesystem::File* file; + + int maxSize = storage::File::getMaxSize (); + + maxSize += rand () % maxSize; + + for (filesystem::Directory::child_iterator ii = dir->child_begin (), maxii = dir->child_end (); ii != maxii; ii ++) { + file = filesystem::File::down_cast (filesystem::Directory::child (ii)); + + if (file != NULL) { + storage::File* storageFile = storage::File::instantiate (file); + message ("File", storageFile); + files.push_back (storageFile); + +// if (files.size () >= maxSize) +// break; + } + } + + message ("StorageArea (full)", storage::File::getStorageArea ()); + + if (flags & Flags::Clear) + clear (files); + + message ("StorageArea (empty)", storage::File::getStorageArea ()); + + cout << endl; +} + +void WorkDirectory::reuseHoles (filesystem::Directory* dir, WorkDirectory::file_container& files) + throw (RuntimeException) +{ + TraceMethod tm ("WorkDirectory", "reuseHoles", ANNA_FILE_LOCATION); + + cout << "WorkDirectory::reuseHoles: Invoca dos veces a fullCache para verificar que el tamano se mantiene la segunda vez" << endl; + fullCache (dir, files, Flags::Clear); + fullCache (dir, files, Flags::Clear); + cout << endl; +} + +void WorkDirectory::destroyObjects (filesystem::Directory* dir, WorkDirectory::file_container& files) + throw (RuntimeException) +{ + TraceMethod tm ("WorkDirectory", "destroyObjects", ANNA_FILE_LOCATION); + + cout << "WorkDirectory::destroyObjects: Carga un directorio distinto, para verificar que destruye los objetos segun se dejan de utilizar" << endl; + filesystem::File* file; + filesystem::Directory* other = NULL; + + for (filesystem::Directory::child_iterator ii = dir->child_begin (), maxii = dir->child_end (); ii != maxii && other == NULL; ii ++) + other = filesystem::Directory::down_cast (filesystem::Directory::child (ii)); + + if (other == NULL) { + cout << dir->getPath () << ": No se puede realizar esta prueba" << endl << endl; + return; + } + + cout << "New Directory: " << other->getPath () << endl; + dir = other; + + for (filesystem::Directory::child_iterator ii = dir->child_begin (), maxii = dir->child_end (); ii != maxii; ii ++) { + file = filesystem::File::down_cast (filesystem::Directory::child (ii)); + + if (file != NULL) { + storage::File* storageFile = storage::File::instantiate (file); + message ("File", storageFile); + storage::File::release (storageFile); + } + } + + message ("StorageArea", storage::File::getStorageArea ()); + + cout << endl; +} + + +void WorkDirectory::clear (WorkDirectory::file_container& files) + throw () +{ + storage::File* file; + + /* Libera los objetos en distinto orden para empeorar el tratamiento huecos */ + + if ((anna::functions::millisecond () % 2) == 0) { + cout << "Clear directo" << endl; + for (file_iterator ii = files.begin (), maxii = files.end (); ii != maxii; ii ++) { + file = *ii; + storage::File::release (file); + } + } + else { + cout << "Clear inverso" << endl; + for (file_reverse_iterator ii = files.rbegin (), maxii = files.rend (); ii != maxii; ii ++) { + file = *ii; + storage::File::release (file); + } + } + + files.clear (); +} + +/*static*/ +void WorkDirectory::load (filesystem::Directory* parent, const int maxLevel, const int level) + throw (RuntimeException) +{ + if (level == maxLevel) + return; + + io::Directory directory; + string fullPath; + + directory.read (parent->getPath (), io::Directory::Mode::ShortPath); + + for (io::Directory::const_iterator ii = directory.begin (), maxii = directory.end (); ii != maxii; ii ++) { + const std::string& name = io::Directory::data (ii); + + fullPath = filesystem::Abstract::calculePath (parent, name); + + if (io::functions::isADirectory (fullPath)) { + try { + filesystem::Directory* dd = new filesystem::Directory (parent, name); + load (dd, maxLevel, level + 1); + } + catch (RuntimeException& ex) { + ex.trace (); + } + } + else { + //Auto association to the parent: + new filesystem::File (parent, name); + } + } +} diff --git a/example/dbos/workdir/storage/Directory.cpp b/example/dbos/workdir/storage/Directory.cpp new file mode 100644 index 0000000..7c35dc6 --- /dev/null +++ b/example/dbos/workdir/storage/Directory.cpp @@ -0,0 +1,129 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include + +#include +#include + +#include + +#include "../filesystem/Directory.hpp" + +#include "Directory.hpp" + +using namespace std; +using namespace anna; +using namespace workdir; + +/*static*/ +storage::Directory::Loader* storage::Directory::st_loader = NULL; + +/*static*/ +ExclusiveHash storage::Directory::st_hash; + +/*static*/ +int storage::Directory::st_maxSize = 0; + +dbos_prepare_object (storage::Directory); + +void storage::Directory::setup (dbos::Repository& repository, const int maxSize) + throw (RuntimeException) +{ + st_loader = new Directory::Loader (); + st_maxSize = maxSize; + + Directory::setStorageArea ( + repository.createStorageArea ( // (1) + Directory::getStorageAreaId (), Directory::getStorageAreaName (), Directory::getMaxSize (), + Directory::allocator, 2 + ) + ); +} + +storage::Directory* storage::Directory::instantiate (const filesystem::Directory* directory) + throw (RuntimeException) +{ + if (st_loader == NULL) + throw RuntimeException ("storage::Directory::setup no ha sido invocado", ANNA_FILE_LOCATION); + + Directory* result = NULL; + + try { + Guard guard (st_loader, "storage::Directory::Loader"); + result = dbos::ObjectFacade ::instance (st_loader->setKey (directory)); + } + catch (dbms::DatabaseException& edb) { + throw RuntimeException (edb); + } + + return result; +} + +void storage::Directory::initialize (dbos::Loader& loader) + throw (RuntimeException, dbms::DatabaseException) +{ + Directory::Loader& dbLoader = static_cast (loader); + + a_filesystemDirectory = dbLoader.getDirectory (); + a_inode = dbLoader.getINode (); + + LOGINFORMATION ( + string msg ("storage::Directory::initialize | "); + msg += asString (); + Logger::information (msg, ANNA_FILE_LOCATION); + ); +} + +void storage::Directory::destroy () + throw () +{ + LOGINFORMATION ( + string msg ("storage::Directory::destroy | "); + msg += asString (); + Logger::information (msg, ANNA_FILE_LOCATION); + ); +} + +string storage::Directory::asString () const + throw () +{ + std::string result ("storage::Directory { Name: "); + result += a_filesystemDirectory->getPath (); + result += functions::asHexText (" | I-Node: ", a_inode); + return result += " }"; +} + +/* + * Transfiere la información del medio físico al primer nivel de C++. + * Posteriormente será interpretada en storage::Directory::initialize + */ +bool storage::Directory::Loader::load (dbms::Connection*, const dbos::StorageArea* ssaa) + throw (RuntimeException) +{ + a_inode = io::functions::getINode (a_filesystemDirectory->getPath ()); + return true; +} + +dbos::Index storage::Directory::Loader::getIndex () const + throw () +{ + return st_hash.calcule (a_filesystemDirectory->getPath ()); +} + +string storage::Directory::Loader::asString () const + throw () +{ + std::string result ("storage::Loader::Directory { Name: "); + result += a_filesystemDirectory->getPath (); + return result += " }"; +} + diff --git a/example/dbos/workdir/storage/Directory.hpp b/example/dbos/workdir/storage/Directory.hpp new file mode 100644 index 0000000..3bffdf3 --- /dev/null +++ b/example/dbos/workdir/storage/Directory.hpp @@ -0,0 +1,93 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef dbos_workdir_storage_Directory_hpp +#define dbos_workdir_storage_Directory_hpp + +#include +#include +#include + +namespace anna { +namespace dbms { +class Database; +class Connection; +} +namespace dbos { +class Repository; +} +} + +namespace workdir { + +namespace filesystem { +class Directory; +} + +namespace storage { + +using namespace anna; + +class Directory : public dbos::Object, public dbos::ObjectFacade { +public: + const filesystem::Directory* getFilesystemDirectory() const throw() { return a_filesystemDirectory; } + int getINode() const throw() { return a_inode; } + + std::string asString() const throw(); + + static void setup(dbos::Repository&, const int maxSize) throw(RuntimeException); + static Directory* instantiate(const filesystem::Directory*) throw(RuntimeException); + + static const char* getStorageAreaName() throw() { return "storage::Directory"; } + static const dbos::Size getMaxSize() throw() { return st_maxSize; } + +private: + class Loader : public dbos::Loader { + public: + Loader() : dbos::Loader() {;} + + Loader& setKey(const filesystem::Directory* directory) throw() { + a_filesystemDirectory = directory; + return *this; + } + + const filesystem::Directory* getDirectory() const throw() { return a_filesystemDirectory; } + int getINode() const throw() { return a_inode; } + + dbos::Index getIndex() const throw(); + std::string asString() const throw(); + + private: + const filesystem::Directory* a_filesystemDirectory; + int a_inode; + + // dbms::Statement is not required + dbms::Statement* initialize(dbms::Database&) throw(RuntimeException) { return NULL; } + bool load(dbms::Connection*, const dbos::StorageArea*) throw(RuntimeException); + }; + + const filesystem::Directory* a_filesystemDirectory; + int a_inode; + + static Loader* st_loader; + static ExclusiveHash st_hash; + static int st_maxSize; + + Directory() { ; } + Directory(const Directory&); + + void initialize(dbos::Loader& loader) throw(RuntimeException, dbms::DatabaseException); + void destroy() throw(); + + dbos_declare_object(Directory); +}; + +} +} + +#endif diff --git a/example/dbos/workdir/storage/File.cpp b/example/dbos/workdir/storage/File.cpp new file mode 100644 index 0000000..6a23430 --- /dev/null +++ b/example/dbos/workdir/storage/File.cpp @@ -0,0 +1,133 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include + +#include +#include + +#include + +#include "../filesystem/File.hpp" +#include "../filesystem/Directory.hpp" + +#include "File.hpp" +#include "Directory.hpp" + +using namespace std; +using namespace anna; +using namespace workdir; + +/*static*/ +storage::File::Loader* storage::File::st_loader = NULL; + +/*static*/ +ExclusiveHash storage::File::st_hash; + +/*static*/ +int storage::File::st_maxSize = 0; + +dbos_prepare_object (storage::File); + +void storage::File::setup (dbos::Repository& repository, const int maxSize) + throw (RuntimeException) +{ + st_loader = new File::Loader (); + st_maxSize = maxSize; + + File::setStorageArea ( + repository.createStorageArea ( // (1) + File::getStorageAreaId (), File::getStorageAreaName (), File::getMaxSize (), + File::allocator, 2 + ) + ); +} + +storage::File* storage::File::instantiate (const filesystem::File* file) + throw (RuntimeException) +{ + if (st_loader == NULL) + throw RuntimeException ("storage::File::setup no ha sido invocado", ANNA_FILE_LOCATION); + + File* result = NULL; + + try { + Guard guard (st_loader, "storage::File::Loader"); + result = dbos::ObjectFacade ::instance (st_loader->setKey (file)); + } + catch (dbms::DatabaseException& edb) { + throw RuntimeException (edb); + } + + return result; +} + +void storage::File::initialize (dbos::Loader& loader) + throw (RuntimeException, dbms::DatabaseException) +{ + File::Loader& dbLoader = static_cast (loader); + + a_filesystemFile = dbLoader.getFile (); + a_parent = Directory::instantiate (filesystem::Directory::down_cast (a_filesystemFile->getParent ())); + a_inode = dbLoader.getINode (); + + LOGINFORMATION ( + string msg ("storage::File::initialize | "); + msg += asString (); + Logger::information (msg, ANNA_FILE_LOCATION); + ); +} + +void storage::File::destroy () + throw () +{ + LOGINFORMATION ( + string msg ("storage::File::destroy | "); + msg += asString (); + Logger::information (msg, ANNA_FILE_LOCATION); + ); + Directory::release (a_parent); +} + +string storage::File::asString () const + throw () +{ + std::string result ("storage::File { Name: "); + result += a_filesystemFile->getPath (); + result += functions::asHexText (" | I-Node: ", a_inode); + return result += " }"; +} + +/* + * Transfiere la información del medio físico al primer nivel de C++. + * Posteriormente será interpretada en storage::File::initialize + */ +bool storage::File::Loader::load (dbms::Connection*, const dbos::StorageArea* ssaa) + throw (RuntimeException) +{ + a_inode = io::functions::getINode (a_filesystemFile->getPath ()); + return true; +} + +dbos::Index storage::File::Loader::getIndex () const + throw () +{ + return st_hash.calcule (a_filesystemFile->getPath ()); +} + +string storage::File::Loader::asString () const + throw () +{ + std::string result ("storage::Loader::File { Name: "); + result += a_filesystemFile->getPath (); + return result += " }"; +} + diff --git a/example/dbos/workdir/storage/File.hpp b/example/dbos/workdir/storage/File.hpp new file mode 100644 index 0000000..51d4293 --- /dev/null +++ b/example/dbos/workdir/storage/File.hpp @@ -0,0 +1,97 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef dbos_workdir_storage_File_hpp +#define dbos_workdir_storage_File_hpp + +#include +#include +#include + +namespace anna { +namespace dbms { +class Database; +class Connection; +} +namespace dbos { +class Repository; +} +} + +namespace workdir { + +namespace filesystem { +class File; +} + +namespace storage { + +class Directory; + +using namespace anna; + +class File : public dbos::Object, public dbos::ObjectFacade { +public: + const Directory* getParent() const throw() { return a_parent; } + const filesystem::File* getFilesystemFile() const throw() { return a_filesystemFile; } + int getINode() const throw() { return a_inode; } + + std::string asString() const throw(); + + static void setup(dbos::Repository&, const int maxSize) throw(RuntimeException); + static File* instantiate(const filesystem::File*) throw(RuntimeException); + + static const char* getStorageAreaName() throw() { return "storage::File"; } + static const dbos::Size getMaxSize() throw() { return st_maxSize; } + +private: + class Loader : public dbos::Loader { + public: + Loader() : dbos::Loader() {;} + + Loader& setKey(const filesystem::File* file) throw() { + a_filesystemFile = file; + return *this; + } + + const filesystem::File* getFile() const throw() { return a_filesystemFile; } + int getINode() const throw() { return a_inode; } + + dbos::Index getIndex() const throw(); + std::string asString() const throw(); + + private: + const filesystem::File* a_filesystemFile; + int a_inode; + + // dbms::Statement is not required + dbms::Statement* initialize(dbms::Database&) throw(RuntimeException) { return NULL; } + bool load(dbms::Connection*, const dbos::StorageArea*) throw(RuntimeException); + }; + + Directory* a_parent; + const filesystem::File* a_filesystemFile; + int a_inode; + + static Loader* st_loader; + static ExclusiveHash st_hash; + static int st_maxSize; + + File() : a_parent(NULL) { ; } + File(const File&); + + void initialize(dbos::Loader& loader) throw(RuntimeException, dbms::DatabaseException); + void destroy() throw(); + + dbos_declare_object(File); +}; + +} +} + +#endif diff --git a/include/anna/dbms.mysql/BaseBind.hpp b/include/anna/dbms.mysql/BaseBind.hpp new file mode 100644 index 0000000..a9a3b85 --- /dev/null +++ b/include/anna/dbms.mysql/BaseBind.hpp @@ -0,0 +1,53 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_BaseBind_hpp +#define anna_dbms_mysql_BaseBind_hpp + +#include + +#include + +namespace anna { + +namespace dbms { + +namespace mysql { + +class BaseBind { +public: + virtual ~BaseBind(); + +protected: + /* + * mysql.h:typedef char my_bool; + */ + char a_nullIndicator; + + /** + * mysql_time.h: typedef st_mysql_time MYSQL_TIME + */ + st_mysql_time* a_time; + + unsigned long a_length; + + BaseBind(const dbms::Data& data) ; + void setupBind(st_mysql_bind&, dbms::Data&) throw(RuntimeException); + +private: + static const int MaxBlobSize = 2 << 16; + + const Data::Type::_v a_type; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.mysql/Connection.hpp b/include/anna/dbms.mysql/Connection.hpp new file mode 100644 index 0000000..ea817c3 --- /dev/null +++ b/include/anna/dbms.mysql/Connection.hpp @@ -0,0 +1,72 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_Connection_hpp +#define anna_dbms_mysql_Connection_hpp + + +#include +#include + +#include + +namespace anna { + +namespace dbms { + +class Database; + +namespace mysql { + +class Database; + +/** + Clase que modela la conexion con el RDBMS MySQL (tm). + + Esta clase no puede usarse directamente, ya que la capa ANNA.dbms obliga a que todas las peticiones + se hagan atraves de una instancia anna::dbms::Connection. + + Para obtener una conexion a una determinada base de datos habra que instanciar dicha base de datos + e invocar al metodo createConnection. Independientemente del tipo de conexion particular que la + base de datos retorne, debemos asignarlo a un puntero de tipo anna::dbms::Connection. +*/ +class Connection : public dbms::Connection { +public: + /** + Devuelve una cadena con la informacion referente a esta instancia. + @return Una cadena con la informacion referente a esta instancia. + */ + std::string asString() const throw(); + + /** + Operador de conversion. + \return El puntero al entorno asociado a esta base de datos. + */ + operator st_mysql*() throw() { return a_mysql; } + +private: + Database& a_mysqlDatabase; + st_mysql* a_mysql; + + Connection(Database& database, const std::string& name, const char* user, const char* password); + + bool isAvailable() const throw(RuntimeException) { return a_mysql != NULL; } + + void do_commit() throw(RuntimeException, DatabaseException); + void do_rollback() throw(); + void open() throw(DatabaseException); + void close() throw(); + + friend class anna::dbms::mysql::Database; +}; + +} +} +} + +#endif diff --git a/include/anna/dbms.mysql/Database.hpp b/include/anna/dbms.mysql/Database.hpp new file mode 100644 index 0000000..b9ad79e --- /dev/null +++ b/include/anna/dbms.mysql/Database.hpp @@ -0,0 +1,105 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_Database_hpp +#define anna_dbms_mysql_Database_hpp + +#include +#include + +namespace anna { + +namespace dbms { + +namespace mysql { + +/** + Clase que modela la interaccion entre la RDMS MySQL (tm) y nuestra aplicacion. +*/ +class Database : public dbms::Database { +public: + /** + Contructor. + \param dbmsName Nombre de la base de datos. + \param host Identificador de la máquina anfitriona, que se usará para hacer las conexiones. Puede ser NULL. + \see http://dev.mysql.com/doc/refman/4.1/en/mysql-real-connect.html + */ + Database(const char* dbmsName, const char* host); + + /** + Contructor. + \param componentName Nombre logico de la base de datos por que el podemos buscar este componente. + \param dbmsName Nombre de la base de datos. + \param host Identificador de la máquina anfitriona, que se usará para hacer las conexiones. Puede ser NULL. + \see http://dev.mysql.com/doc/refman/4.1/en/mysql-real-connect.html + */ + Database(const char* componentName, const char* dbmsName, const char* host); + + /** + Destructor. + */ + virtual ~Database(); + + /** + * Devuelve el nombre de la máquina anfitriona indicado en el constructor. + * \return El nombre de la máquina anfitriona indicado en el constructor. + */ + const char* getHost() const throw() { return a_host; } + + /** + Devuelve la cadena por la que podemos buscar el componente. + \return La cadena por la que podemos buscar el componente. + \see Application::find + */ + static const char* getClassName() { return "anna::dbms::mysql::Database"; } + +private: + char* a_host; + + void do_initialize() throw(RuntimeException); + + dbms::Connection* allocateConnection(const std::string& name, const char* user, const char* password) + throw(RuntimeException); + + dbms::Statement* allocateStatement(const char* name, const std::string& expression, const bool isCritical) + throw(RuntimeException); + + dbms::InputBind* allocateInputBind(const char* name, Data&) + throw(RuntimeException); + void deallocate(dbms::InputBind* inputBind) throw(); + + dbms::OutputBind* allocateOutputBind(const char* name, Data&) + throw(RuntimeException); + void deallocate(dbms::OutputBind* outputBind) throw(); +}; + +#ifdef ANNA_RDBMS_TRACE +#define anna_dbms_mysql_check(a,_mysql) \ + { \ + Logger::write (Logger::Debug, (#a), __FILE__, __LINE__); \ + const int status = (a); \ + if (status != 0) { \ + anna::dbms::mysql::ResultCode resultCode ((_mysql)); \ + throw DatabaseException (resultCode, __FILE__, __LINE__); \ + } \ + } +#else +#define anna_dbms_mysql_check(a,_mysql) \ + { \ + const int status = (a); \ + if (status != 0) { \ + anna::dbms::mysql::ResultCode resultCode ((_mysql)); \ + throw DatabaseException (resultCode, __FILE__, __LINE__); \ + } \ + } +#endif +} +} +} + +#endif diff --git a/include/anna/dbms.mysql/InputBind.hpp b/include/anna/dbms.mysql/InputBind.hpp new file mode 100644 index 0000000..8f58e1e --- /dev/null +++ b/include/anna/dbms.mysql/InputBind.hpp @@ -0,0 +1,57 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_InputBind_hpp +#define anna_dbms_mysql_InputBind_hpp + +#include + +#include + +namespace anna { + +class DataBlock; + +namespace dbms { + +class Data; +class Statement; + +namespace mysql { + +class Statement; + +class InputBind : public dbms::InputBind, public BaseBind { +public: + InputBind(const char* name, dbms::Data& data); + virtual ~InputBind(); + +private: + void code() const throw(RuntimeException); + + void codeShortBlock(dbms::Data&) throw(); + void codeDate(dbms::Data&) throw(); + + static char asCharacter(const char byte) + throw() { + return (byte >= 0 && byte <= 9) ? (byte + '0') : ((byte - 0xa) + 'A'); + } + + /* Funciones virtuales puras */ + void prepare(anna::dbms::Statement*, anna::dbms::Connection*, const int pos) throw(RuntimeException); + void release(anna::dbms::Statement*) throw() {;} + + friend class mysql::Statement; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.mysql/OracleTranslator.hpp b/include/anna/dbms.mysql/OracleTranslator.hpp new file mode 100644 index 0000000..95573e1 --- /dev/null +++ b/include/anna/dbms.mysql/OracleTranslator.hpp @@ -0,0 +1,54 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_OracleTranslator_hpp +#define anna_dbms_mysql_OracleTranslator_hpp + +#include + +namespace anna { + +namespace dbms { + +namespace mysql { + +/** + * Ésta clase permite que sentencias, escritas originalmente para ser ejecutadas sobre + * Oracle (tm) puedan ser ejecutadas desde MySQL (tm) sin ningún tipo de problemas. + * + * Si este traductor se aplica sobre una sentencia SQL escrita originalmente para + * funcionar sobre MySQL el resultado será la misma sentencia. + * + * \see anna::dbms::Database::setStatementTranslator + */ +class OracleTranslator : public StatementTranslator { +public: + /** + * Obtiene la instancia de este traductor de sentencias SQL. + */ + static StatementTranslator* instantiate() throw() { return &st_this; } + +private: + char* a_buffer; + int a_size; + + static OracleTranslator st_this; + + OracleTranslator() : StatementTranslator("dbms::mysql::OracleTranslator"), + a_buffer(NULL), a_size(-1) + {;} + + const char* apply(const char* statement) throw(RuntimeException); + void allocate(const char* statement) throw(); +}; + +} +} +} + +#endif diff --git a/include/anna/dbms.mysql/OutputBind.hpp b/include/anna/dbms.mysql/OutputBind.hpp new file mode 100644 index 0000000..72e9929 --- /dev/null +++ b/include/anna/dbms.mysql/OutputBind.hpp @@ -0,0 +1,68 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_OutputBind_hpp +#define anna_dbms_mysql_OutputBind_hpp + +#include + +#include +#include + +#include + +namespace anna { + +class DataBlock; + +namespace dbms { + +class Statement; + +namespace mysql { + +class OutputBind : public dbms::OutputBind, public BaseBind { +public: + OutputBind(const char* name, dbms::Data& data); + ~OutputBind(); + +private: + struct Blob { + DataBlock buffer; + st_mysql_stmt* stmt; + st_mysql_bind* binds; + int pos; + + Blob(); + }; + + Blob* a_blob; + + void decodeLongBlob(dbms::Data&) const throw(RuntimeException, dbms::DatabaseException); + void decodeDate(dbms::Data&) throw(); + + static unsigned char asByte(const char hex) + throw() { + return (hex >= '0' && hex <= '9') ? (hex - '0') : ((hex - 'A') + 0x0a); + } + + /* Funciones virtuales puras */ + void decode() const throw(RuntimeException); + void prepare(anna::dbms::Statement*, anna::dbms::Connection*, const int pos) throw(RuntimeException); + void release(anna::dbms::Statement*) throw() {;} + void do_write(const dbms::LongBlock&) const throw(RuntimeException, dbms::DatabaseException); + + friend class Statement; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.mysql/ResultCode.hpp b/include/anna/dbms.mysql/ResultCode.hpp new file mode 100644 index 0000000..1c27be9 --- /dev/null +++ b/include/anna/dbms.mysql/ResultCode.hpp @@ -0,0 +1,56 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_ResultCode_hpp +#define anna_dbms_mysql_ResultCode_hpp + +#include + +#include + +namespace anna { + +namespace dbms { + +namespace mysql { + +/** + Clase para acceder a la informacion devuelta por el gestor de base de datos + referente al ultimo comando realizado. + */ +class ResultCode : public dbms::ResultCode { +public: + /** + Constructor. + \param mysql Instancia de la base de datos sobre la que aplicamos la sentencia SQL. + */ + explicit ResultCode(st_mysql* mysql); + + /** + Constructor. + \param stmt Instancia de la sentencia ejecutada. + */ + explicit ResultCode(st_mysql_stmt* stmt); + +private: + class ErrorDecoder : public dbms::ResultCode::ErrorDecoder { + bool notFound(const int errorCode) const throw(); + bool successful(const int errorCode) const throw(); + bool locked(const int errorCode) const throw(); + bool lostConnection(const int errorCode) const throw(); + }; + + static ErrorDecoder st_errorDecoder; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.mysql/Statement.hpp b/include/anna/dbms.mysql/Statement.hpp new file mode 100644 index 0000000..48c9f86 --- /dev/null +++ b/include/anna/dbms.mysql/Statement.hpp @@ -0,0 +1,95 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_Statement_hpp +#define anna_dbms_mysql_Statement_hpp + +#include + +#include + +namespace anna { + +namespace dbms { + +class Connection; + +namespace mysql { + +class Database; + +/** + Clase que facilita la ejecucion de sentencias SQL a traves del RDBMS MySQL (tm). + + Esta clase no puede usarse directamente, ya que la capa ANNA.dbms obliga a que todas las peticiones + se hagan atraves de una instancia anna::dbms::Statement. + + Para obtener la instancia de un comando para una determinada base de datos habra que instanciar + dicha base de datos e invocar al metodo createStatement. Independientemente del tipo de comando + particular que la base de datos retorne, debemos asignarlo a un puntero de tipo anna::dbms::Statement. + */ +class Statement : public dbms::Statement { +public: + /** + Destructor. + */ + virtual ~Statement(); + + /** + Operador de conversion. + \return El puntero MYSQL_STMT de esta sentencia. + */ + operator st_mysql_stmt*() throw() { return a_mysqlStmt; } + + /** + * Obtiene el array asociado a los valores de entrada. + * \return El array asociado a los valores de entrada. + * \warning Exclusivamente uso interno. + */ + st_mysql_bind* getBindParams() throw() { return a_params; } + + /** + * Obtiene el array asociado a los valores de salida. + * \return El array asociado a los valores de salida. + * \warning Exclusivamente uso interno. + */ + st_mysql_bind* getBindResults() throw() { return a_results; } + +private: + st_mysql_stmt* a_mysqlStmt; + + st_mysql_bind* a_params; + st_mysql_bind* a_results; + + Statement(Database& database, const char* name, const char* expression, const bool isCritical) : + dbms::Statement(database, name, expression, isCritical), + a_mysqlStmt(NULL), + a_params(NULL), + a_results(NULL) {} + + Statement(Database& database, const char* name, const std::string& expression, const bool isCritical) : + dbms::Statement(database, name, expression, isCritical), + a_mysqlStmt(NULL), + a_params(NULL), + a_results(NULL) {} + + st_mysql_bind* create(const int size, const char* whatis) throw(RuntimeException); + + void prepare(dbms::Connection* connection) throw(RuntimeException, DatabaseException); + dbms::ResultCode execute(dbms::Connection* connection) throw(RuntimeException, DatabaseException); + bool fetch() throw(RuntimeException, DatabaseException); + + friend class Database; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.mysql/forward.hpp b/include/anna/dbms.mysql/forward.hpp new file mode 100644 index 0000000..36fe912 --- /dev/null +++ b/include/anna/dbms.mysql/forward.hpp @@ -0,0 +1,26 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_forward_hpp +#define anna_dbms_mysql_forward_hpp + +/* + * el MYSQL es un typedef definido sobre la estructura st_mysql + */ +struct st_mysql; + +/* MYSQL_STMT */ +struct st_mysql_stmt; + +/* MYSQL_BIND */ +struct st_mysql_bind; + +/* MYSQL_TIME */ +struct st_mysql_time; + +#endif /*_anna_dbms_mysql_forward_h*/ diff --git a/include/anna/dbms.mysql/internal/sccs.hpp b/include/anna/dbms.mysql/internal/sccs.hpp new file mode 100644 index 0000000..4c8a529 --- /dev/null +++ b/include/anna/dbms.mysql/internal/sccs.hpp @@ -0,0 +1,28 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_internal_sccs_hpp +#define anna_dbms_mysql_internal_sccs_hpp + +namespace anna { + +namespace dbms { + +namespace mysql { + +class sccs { +public: + static void activate() throw(); +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.mysql/mysql.hpp b/include/anna/dbms.mysql/mysql.hpp new file mode 100644 index 0000000..bee3ca9 --- /dev/null +++ b/include/anna/dbms.mysql/mysql.hpp @@ -0,0 +1,44 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_mysql_mysql_hpp +#define anna_dbms_mysql_mysql_hpp + +namespace anna { + +namespace dbms { +/** +Proporciona las clases necesarias para acceder a la ejecucion de sentencias SQL para +MySQL (tm). + +El ejecutable debera enlazarse con las librerias: + \li anna.core.a + \li anna.xml.a + \li anna.app.a + \li anna.comm.a + \li anna.dbms.a + \li anna.dbms.mysql.a + +El Packet Header es anna.dbms.mysql.h +*/ +namespace mysql { +} +} +} + +#include +#include +#include +#include +#include +#include + +using namespace anna::dbms::mysql; + +#endif + diff --git a/include/anna/dbms.oracle/BaseBind.hpp b/include/anna/dbms.oracle/BaseBind.hpp new file mode 100644 index 0000000..c99699c --- /dev/null +++ b/include/anna/dbms.oracle/BaseBind.hpp @@ -0,0 +1,79 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_BaseBind_hpp +#define anna_dbms_oracle_BaseBind_hpp + +struct OCIEnv; +struct OCIError; +struct OCIBind; +struct OCILobLocator; +struct OCISvcCtx; +struct OCIDateTime; + +#include + +namespace anna { + +class DataBlock; + +namespace dbms { + +class Statement; +class Data; + +namespace oracle { + +class Statement; +class Database; +class Connection; + +class BaseBind { +public: + virtual ~BaseBind(); + +protected: + /** + * Número de bytes reservados para un Float cuando se trata como si fuera una cadena. + */ + static const int FloatSize = 63; + struct oci_param { + int type; + int maxLength; + short unsigned int* length; + void* buffer; + }; + struct Blob : public Descriptor { + OCILobLocator* handle; + Blob() : Descriptor((dvoid**) &handle) {;} + }; + struct DateTime : public Descriptor { + OCIDateTime* handle; + DateTime() : Descriptor((dvoid**) &handle) {;} + }; + + short a_nullIndicator; + unsigned short a_length; + anna::DataBlock* a_ofb; // Oracle Formatted Buffer. + Blob a_blob; + DateTime a_datetime; + + BaseBind(const dbms::Data& data) ; + + oci_param getOCIParam(Database&, Connection*, dbms::Data&) throw(RuntimeException); + +private: + const Data::Type::_v a_type; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.oracle/Connection.hpp b/include/anna/dbms.oracle/Connection.hpp new file mode 100644 index 0000000..2259d9e --- /dev/null +++ b/include/anna/dbms.oracle/Connection.hpp @@ -0,0 +1,73 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_Connection_hpp +#define anna_dbms_oracle_Connection_hpp + +struct OCIServer; +struct OCISession; +struct OCISvcCtx; + +#include +#include + +namespace anna { + +namespace dbms { + +class Database; + +namespace oracle { + +class Database; + +/** + Clase que modela la conexion con el RDBMS Oracle (tm). + + Esta clase no puede usarse directamente, ya que la capa ANNA.dbms obliga a que todas las peticiones + se hagan atraves de una instancia anna::dbms::Connection. + + Para obtener una conexion a una determinada base de datos habra que instanciar dicha base de datos + e invocar al metodo createConnection. Independientemente del tipo de conexion particular que la + base de datos retorne, debemos asignarlo a un puntero de tipo anna::dbms::Connection. +*/ +class Connection : public dbms::Connection { +public: + /** + Devuelve una cadena con la informacion referente a esta instancia. + @return Una cadena con la informacion referente a esta instancia. + */ + std::string asString() const throw(); + + /** + Operador de conversion. + \return El puntero al contexto asociado a este conexion. + */ + operator OCISvcCtx*() throw() { return a_context; } + +private: + Database& a_oracleDatabase; + OCISvcCtx* a_context; + OCISession* a_session; + OCIServer* a_server; + + Connection(Database& database, const std::string& name, const char* user, const char* password); + bool isAvailable() const throw(RuntimeException) { return a_context != NULL && a_session != NULL && a_server != NULL; } + void do_commit() throw(RuntimeException, DatabaseException); + void do_rollback() throw(); + void open() throw(DatabaseException); + void close() throw(); + + friend class anna::dbms::oracle::Database; +}; + +} +} +} + +#endif diff --git a/include/anna/dbms.oracle/Database.hpp b/include/anna/dbms.oracle/Database.hpp new file mode 100644 index 0000000..39d9d5f --- /dev/null +++ b/include/anna/dbms.oracle/Database.hpp @@ -0,0 +1,128 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_Database_hpp +#define anna_dbms_oracle_Database_hpp + +#include +#include + +struct OCIEnv; +struct OCIError; + +namespace anna { + +namespace dbms { + +namespace oracle { + +/** + Clase que modela la interaccion entre la RDMS Oracle (tm) y nuestra aplicacion. + + \warning La definicion conexiones y clusters debe hacerse antes de invocar al metodo Application::start, + o bien, en el metodo Application::initialize. +*/ +class Database : public dbms::Database { +public: + /** + Contructor. + \param dbmsName Nombre de la base de datos. + */ + Database(const char* dbmsName); + + /** + Contructor. + \param componentName Nombre logico de la base de datos por que el podemos buscar este compoenente. + \param dbmsName Nombre de la base de datos. + */ + Database(const char* componentName, const char* dbmsName); + + /** + Destructor. + */ + virtual ~Database(); + + /** + Devuelve el manejador de error asociado a esta base de datos. + \return El manejador de error asociado a esta base de datos. + */ + OCIError* getErrorHandler() throw() { return a_error; } + + /** + Operador de conversion. + \return El puntero al entorno asociado a esta base de datos. + */ + operator OCIEnv*() throw() { return a_env; } + + /** + Devuelve la cadena por la que podemos buscar el componente. + \return La cadena por la que podemos buscar el componente. + \see Application::find + */ + static const char* getClassName() { return "anna::dbms::oracle::Database"; } + + /** + * Devuelve el caracter usado como punto decimal, obtenido a partir de la configuración establecida + * por la variables de entorno, LANG, LC_NUMERIC, etc, etc. + * + * \return El caracter usado como punto decimal. + * + * \warning Metodo exclusivamente de uso interno. + */ + static char getDecimalPoint() throw() { return st_decimalPoint; } + +private: + OCIEnv* a_env; + OCIError* a_error; + + static char st_decimalPoint; + + void do_initialize() throw(RuntimeException); + + dbms::Connection* allocateConnection(const std::string& name, const char* user, const char* password) + throw(RuntimeException); + + dbms::Statement* allocateStatement(const char* name, const std::string& expression, const bool isCritical) + throw(RuntimeException); + + dbms::InputBind* allocateInputBind(const char* name, Data&) + throw(RuntimeException); + void deallocate(dbms::InputBind* inputBind) throw(); + + dbms::OutputBind* allocateOutputBind(const char* name, Data&) + throw(RuntimeException); + void deallocate(dbms::OutputBind* outputBind) throw(); + + static void initializeDecimalPoint() throw(RuntimeException); +}; + +#ifdef ANNA_RDBMS_TRACE +#define anna_dbms_oracle_check(a,error) \ + { \ + Logger::write (Logger::Debug, (#a), __FILE__, __LINE__); \ + const sword status = (a); \ + if (status != OCI_SUCCESS) { \ + anna::dbms::oracle::ResultCode resultCode (status, (error)); \ + throw DatabaseException (resultCode, __FILE__, __LINE__); \ + } \ + } +#else +#define anna_dbms_oracle_check(a,error) \ + { \ + const sword status = (a); \ + if (status != OCI_SUCCESS) { \ + anna::dbms::oracle::ResultCode resultCode (status, (error)); \ + throw DatabaseException (resultCode, __FILE__, __LINE__); \ + } \ + } +#endif +} +} +} + +#endif diff --git a/include/anna/dbms.oracle/Descriptor.hpp b/include/anna/dbms.oracle/Descriptor.hpp new file mode 100644 index 0000000..af2395d --- /dev/null +++ b/include/anna/dbms.oracle/Descriptor.hpp @@ -0,0 +1,49 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_Descriptor_hpp +#define anna_dbms_oracle_Descriptor_hpp + +#include + +struct OCIEnv; +struct OCIError; +struct OCIBind; +struct OCILobLocator; +struct OCISvcCtx; + +namespace anna { + +namespace dbms { + +namespace oracle { + +class Database; +class Connection; + +struct Descriptor { + dvoid** reference; + OCIEnv* env; + OCIError* error; + OCISvcCtx* context; + int type; + + Descriptor(dvoid** _handle) : reference(_handle) { + *reference = NULL; env = NULL; error = NULL; context = NULL; type = -1; + } + virtual ~Descriptor(); + + void allocate(Database&, Connection*, const int type) throw(RuntimeException); +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.oracle/InputBind.hpp b/include/anna/dbms.oracle/InputBind.hpp new file mode 100644 index 0000000..3b0b6ee --- /dev/null +++ b/include/anna/dbms.oracle/InputBind.hpp @@ -0,0 +1,60 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_InputBind_hpp +#define anna_dbms_oracle_InputBind_hpp + +#include + +#include + +struct OCIBind; + +namespace anna { + +class DataBlock; + +namespace dbms { + +class Data; +class Statement; + +namespace oracle { + +class Statement; + +class InputBind : public dbms::InputBind, public BaseBind { +public: + InputBind(const char* name, dbms::Data& data); + virtual ~InputBind(); + +private: + OCIBind* a_ociBind; + + void code() const throw(RuntimeException); + void prepare(dbms::Statement*, dbms::Connection*, const int pos) throw(RuntimeException, DatabaseException); + void release(dbms::Statement*) throw() { a_ociBind = NULL; } + + void codeFloat(dbms::Data&) const throw(); + void codeShortBlock(dbms::Data&) const throw(); + void codeDate(dbms::Data&) const throw(RuntimeException, DatabaseException); + + static char asCharacter(const char byte) + throw() { + return (byte >= 0 && byte <= 9) ? (byte + '0') : ((byte - 0xa) + 'A'); + } + + friend class oracle::Statement; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.oracle/OutputBind.hpp b/include/anna/dbms.oracle/OutputBind.hpp new file mode 100644 index 0000000..ce41a22 --- /dev/null +++ b/include/anna/dbms.oracle/OutputBind.hpp @@ -0,0 +1,58 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_OutputBind_hpp +#define anna_dbms_oracle_OutputBind_hpp + +#include + +#include + +struct OCIDefine; + +namespace anna { + +namespace dbms { + +class Statement; + +namespace oracle { + +class OutputBind : public dbms::OutputBind, public BaseBind { +public: + OutputBind(const char* name, dbms::Data& data); + ~OutputBind(); + +private: + OCIDefine* a_ociDefine; + + void decode() const throw(anna::RuntimeException); + void prepare(dbms::Statement*, dbms::Connection*, const int pos) throw(DatabaseException); + void release(dbms::Statement*) throw() { a_ociDefine = NULL; } + + void decodeFloat(dbms::Data&) const throw(RuntimeException); + void decodeShortBlock(dbms::Data&) const throw(RuntimeException); + void decodeLongBlock(dbms::Data&) const throw(RuntimeException); + void decodeDate(dbms::Data&) const throw(RuntimeException, DatabaseException); + + void do_write(const dbms::LongBlock&) const throw(RuntimeException, dbms::DatabaseException); + + static unsigned char asByte(const char hex) + throw() { + return (hex >= '0' && hex <= '9') ? (hex - '0') : ((hex - 'A') + 0x0a); + } + + friend class Statement; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.oracle/ResultCode.hpp b/include/anna/dbms.oracle/ResultCode.hpp new file mode 100644 index 0000000..b9f8fd5 --- /dev/null +++ b/include/anna/dbms.oracle/ResultCode.hpp @@ -0,0 +1,52 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_ResultCode_hpp +#define anna_dbms_oracle_ResultCode_hpp + +#include + +struct OCIError; + +namespace anna { + +namespace dbms { + +namespace oracle { + +/** + Clase para acceder a la informacion devuelta por el gestor de base de datos + referente al ultimo comando realizado. + */ +class ResultCode : public dbms::ResultCode { +public: + /** + Constructor. + + \param status Codigo de resultado de la ultima operacion realizada. + \param error Estructura de datos que contiene la informacion adicional sobre el error. + */ + explicit ResultCode(const int status, OCIError* error); + +private: + class ErrorDecoder : public dbms::ResultCode::ErrorDecoder { + bool notFound(const int errorCode) const throw(); + bool successful(const int errorCode) const throw(); + bool locked(const int errorCode) const throw() { return errorCode == 54; } + bool lostConnection(const int errorCode) const throw(); + }; + + static ErrorDecoder st_errorDecoder; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.oracle/Statement.hpp b/include/anna/dbms.oracle/Statement.hpp new file mode 100644 index 0000000..d959c0b --- /dev/null +++ b/include/anna/dbms.oracle/Statement.hpp @@ -0,0 +1,75 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_Statement_hpp +#define anna_dbms_oracle_Statement_hpp + +#include + +struct OCIStmt; +struct OCIError; + +namespace anna { + +namespace dbms { + +class Connection; + +namespace oracle { + +/** + Clase que facilita la ejecucion de sentencias SQL a traves del RDBMS Oracle (tm). + + Esta clase no puede usarse directamente, ya que la capa ANNA.dbms obliga a que todas las peticiones + se hagan atraves de una instancia anna::dbms::Statement. + + Para obtener la instancia de un comando para una determinada base de datos habra que instanciar + dicha base de datos e invocar al metodo createStatement. Independientemente del tipo de comando + particular que la base de datos retorne, debemos asignarlo a un puntero de tipo anna::dbms::Statement. + */ +class Statement : public dbms::Statement { +public: + /** + Destructor. + */ + virtual ~Statement(); + + /** + Operador de conversion. + \return El puntero OCI de esta sentencia. + */ + operator OCIStmt*() throw() { return a_ociStmt; } + +private: + OCIStmt* a_ociStmt; + OCIError* a_ociError; + bool a_firstFetch; + + Statement(Database& database, const char* name, const char* expression, const bool isCritical) : + dbms::Statement(database, name, expression, isCritical), + a_ociStmt(NULL), + a_ociError(NULL) {} + + Statement(Database& database, const char* name, const std::string& expression, const bool isCritical) : + dbms::Statement(database, name, expression, isCritical), + a_ociStmt(NULL), + a_ociError(NULL) {} + + void prepare(dbms::Connection* connection) throw(RuntimeException, DatabaseException); + dbms::ResultCode execute(dbms::Connection* connection) throw(RuntimeException, DatabaseException); + bool fetch() throw(RuntimeException, DatabaseException); + + friend class Database; +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.oracle/internal/sccs.hpp b/include/anna/dbms.oracle/internal/sccs.hpp new file mode 100644 index 0000000..fd02966 --- /dev/null +++ b/include/anna/dbms.oracle/internal/sccs.hpp @@ -0,0 +1,28 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_internal_sccs_hpp +#define anna_dbms_oracle_internal_sccs_hpp + +namespace anna { + +namespace dbms { + +namespace oracle { + +class sccs { +public: + static void activate() throw(); +}; + +} +} +} + +#endif + diff --git a/include/anna/dbms.oracle/oracle.hpp b/include/anna/dbms.oracle/oracle.hpp new file mode 100644 index 0000000..c34c766 --- /dev/null +++ b/include/anna/dbms.oracle/oracle.hpp @@ -0,0 +1,44 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_oracle_oracle_hpp +#define anna_dbms_oracle_oracle_hpp + +namespace anna { + +namespace dbms { +/** +Proporciona las clases necesarias para acceder a la ejecucion de sentencias SQL para +Oracle (tm). + +El ejecutable debera enlazarse con las librerias: + \li anna.core.a + \li anna.xml.a + \li anna.app.a + \li anna.comm.a + \li anna.dbms.a + \li anna.dbms.oracle.a + +El Packet Header es anna.dbms.oracle.h +*/ +namespace oracle { +} +} +} + +#include +#include +#include +#include +#include +#include + +using namespace anna::dbms::oracle; + +#endif + diff --git a/include/anna/dbms/Bind.hpp b/include/anna/dbms/Bind.hpp new file mode 100644 index 0000000..5bc3b7b --- /dev/null +++ b/include/anna/dbms/Bind.hpp @@ -0,0 +1,53 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Bind_hpp +#define anna_dbms_Bind_hpp + +#include +#include + +#include +#include + +namespace anna { + +namespace dbms { + +class Connection; +class Statement; +class Data; + +class Bind { +public: + dbms::Data& getData() throw() { return a_data; } + const dbms::Data& getData() const throw() { return a_data; } + + virtual void prepare(Statement* statement, Connection* connection, const int pos) + throw(RuntimeException, DatabaseException) = 0; + + virtual void release(Statement* statement) throw() = 0; + virtual void code() const throw(RuntimeException) = 0; + virtual void decode() const throw(RuntimeException) = 0; + virtual std::string asString() const throw(); + +protected: + Bind(const char* name, dbms::Data& data) : a_name(name), a_data(data) {;} + +private: + const std::string a_name; + dbms::Data& a_data; + + friend class Statement; +}; + +} +} + +#endif + diff --git a/include/anna/dbms/Connection.hpp b/include/anna/dbms/Connection.hpp new file mode 100644 index 0000000..4744128 --- /dev/null +++ b/include/anna/dbms/Connection.hpp @@ -0,0 +1,194 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Connection_hpp +#define anna_dbms_Connection_hpp + +#include + +#include +#include + +namespace anna { + +namespace xml { +class Node; +} + +namespace dbms { + +class Database; +class Statement; + +/** + Clase que modela la conexion con la base de datos. + + Para crear una nueva conexion hay que invocar al Metodo Database::createConnection de la base de datos + contra la que deseamos establecer la conexion. + + Para obtener una conexion a una determinada base de datos habria que instanciar dicha base de datos + e invocar al Metodo Database::createConnection. Independientemente del tipo de conexion particular que la base + de datos retorne, debemos asignarlo a un puntero de tipo anna::dbms::Connection. +*/ +class Connection : public comm::Resource { +public: + /** + Devuelve la instancia de la base de datos asociada a esta conexion. + */ + Database& getDatabase() const throw() { return a_database; } + + /** + * Devuelve el usuario con el que fué realizada esta conexión. + * \return el usuario con el que fué realizada esta conexión. + */ + const std::string& getUser() const throw() { return a_user; } + + /** + * Devuelve el password del usuario con el que fué realizada esta conexión. + * \return el password del usuario con el que fué realizada esta conexión. + */ + const std::string& getPassword() const throw() { return a_password; } + + /** + * Establece el password del usuario de esta conexión + * \param password Codigo de acceso del usuario. + */ + void setPassword(const char* password) throw() { a_password = password; } + + /** + Establece el periodo de grabacion de esta conexion. Damos la posibilidad de que la conexion confirme + cambios realizados hasta el momento sin que termine la seccion critica que debemos establecer antes + de usar la conexion. + \param maxCommitPending Numero de transacciones que pueden estar pedientes de confirmacion antes + de invocar a #commit. Un valor 0, desactiva el periodo. + \return El periodo de grabacion que habia antes de invocar a este metodo. + \warning La invocacion a este metodo debera hacerse con una seccion critica activada sobre la + esta conexion. + */ + int setMaxCommitPending(const int maxCommitPending) throw() { + const int result = a_maxCommitPending; + a_maxCommitPending = maxCommitPending; + return result; + } + + /** + Desactiva el indicador de que la conexion requiere una invocacion a #rollback. + \warning La invocacion a este metodo debera hacerse con una seccion critica activada sobre la + esta conexion. + */ + void resetRollbackPending() throw() { a_rollbackPending = false; } + + /** + Activa de forma externa el indicador de que la conexion requiere una invocacion a #rollback. + \warning La invocacion a este metodo debera hacerse con una seccion critica activada sobre la + esta conexion. + */ + void activateRollbackPending() throw() { a_rollbackPending = true; } + + /** + Ejecuta la sentencia recibida como parametro. Si la sentencia no tiene variables de salida consideraria + que es un comando \em update, \em insert o \em delete, lo cual, implica la invocacion automatica a los + #commit o #rollback cuando termine la seccion critica de esta conexion. + + \param statement Sentencia que vamos a ejecuta + + \return La estructura con el resultado de la ejecucion de la sentencia. + + \warning La invocacion a este metodo debera hacerse con una seccion critica activada sobre la + esta conexion, por ejemplo: + \code + Guard guard (connection); + connection.execute (someStatement); + \endcode + */ + ResultCode execute(Statement* statement) throw(RuntimeException, DatabaseException); + + /** + Devuelve una cadena con la informacion referente a esta instancia. + @return Una cadena con la informacion referente a esta instancia. + */ + virtual std::string asString() const throw(); + + /** + Devuelve un documento XML con la informacion referente a esta instancia. + \param parent Nodo XML del que debe colgar la informacion. + @return un documento XML con la informacion referente a esta instancia. + */ + virtual xml::Node* asXML(xml::Node* parent) const throw(); + +protected: + /** + Instancia de la base de datos asociada a esta conexion. + Coincidiria con la indicada en el constructor. + */ + Database& a_database; + std::string a_user; /**< Nombre del usuario */ + std::string a_password; /**< Clave de acceso del usuario. */ + + /** + Contructor. + + @param database Instancia de la base de datos asociada a esta conexion. + @param name Nombre logico de la conexion. + @param user Nombre del usuario con el que realizamos la conexion. + @param password Codigo de acceso del usuario. + */ + Connection(Database& database, const std::string& name, const char* user, const char* password) : + comm::Resource(name), + a_database(database), + a_user(user), + a_password(password), + a_lockingCounter(0), + a_commitPending(0), + a_rollbackPending(false), + a_maxCommitPending(0) {} + + /** + Metodo que fija los cambios realizados en la ejecucion de los comandos SQL. + */ + void commit() throw(RuntimeException, DatabaseException); + + /** + Metodo que debemos re-escribir para descartar los cambios realizados sobre las tablas mediante + esta conexion. + */ + void rollback() throw(); + + /** + Metodo que debemos re-escribir para hacer efectiva esta conexion. + */ + virtual void open() throw(DatabaseException) = 0; + + /** + Metodo que debemos re-escribir para cerrar la conexion. + */ + virtual void close() throw() = 0; + +private: + int a_commitPending; // Numero de sentencias a las que no se han fijado cambios. + bool a_rollbackPending; + int a_maxCommitPending; + int a_lockingCounter; + + Connection(const Connection&); + + void initialize() throw(RuntimeException, DatabaseException); + void lock() throw(RuntimeException); + void unlock() throw(); + + virtual bool do_beginTransaction() throw(RuntimeException, DatabaseException) { return false;} + virtual void do_commit() throw(RuntimeException, DatabaseException) = 0; + virtual void do_rollback() throw() = 0; + + friend class Database; +}; + +} +} + +#endif diff --git a/include/anna/dbms/Data.hpp b/include/anna/dbms/Data.hpp new file mode 100644 index 0000000..03de108 --- /dev/null +++ b/include/anna/dbms/Data.hpp @@ -0,0 +1,147 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Data_hpp +#define anna_dbms_Data_hpp + +#include + +#include +#include +#include + +namespace anna { + +namespace dbms { + +class Bind; + +/** + Clase base para las variables de entrada/salida asociadas a las sentencias SQL. +*/ +class Data { +public: + struct Type { + enum _v { + Integer, /**< Numeros enteros */ + String, /**< Cadenas de caracteres */ + Float, /**< Número en coma flotante */ + ShortBlock, /**< Tipos de dato RAW */ + LongBlock, /**< Tipo de datos CLOB */ + Date, /** Tipo de fecha (dependiendo del gestor de base de datos puede contener tambien la hora) */ + TimeStamp /** Tipo para contener simultáneamente la fecha y la hora */ + }; + }; + + /** + Devuelve el tamano maximo de este dato que coincidiria con el indicado en el constructor. + \return El tamano maximo de este dato que coincidiria con el indicado en el constructor. + */ + int getMaxSize() const throw() { return a_maxSize; } + + /** + Devuelve el tipo de dato. + \return El tipo de datos. + */ + Type::_v getType() const throw() { return a_type; } + + /** + Devuelve el area de memoria asociada a esta variable. + */ + void* getBuffer() throw() { return a_buffer; } + + /** + Devuelve el indicador de nulo de esta instancia. + \return El indicador de nulo de esta instancia. + */ + bool isNull() const throw() { return a_isNull; } + + /** + Devuelve el valor que indica si este dato puede tomar valores nulos. + \return El valor que indica si este dato puede tomar valores nulos. + */ + bool isNulleable() const throw() { return a_isNulleable; } + + /** + Establece el indicador de nulo de esta instancia. + \param isNull Indicador de nulo de esta instancia. + \warning Slo tendr�efecto en caso de haber indicado en el constructor que + el dato puede tomar valores nulos. + */ + void setNull(const bool isNull) throw() { if(a_isNulleable == true) a_isNull = isNull; } + + /** + Incorpora el método clear para todos tipos de datos con lo que podemos obtener información + del medio físico. + + Si el dato está definido como "nuleable" activará el indicador que indica que el dato está vacío, + en otro caso se asignará un valor adecuado dependiendo del tipo del dato, cero para los números, + "" para las cadenas, etc. + */ + void clear() throw() { + setNull(true); + do_clear(); + } + + /** + Devuelve una cadena con la informacion referente a esta instancia. + @return Una cadena con la informacion referente a esta instancia. + */ + virtual std::string asString() const throw(); + +protected: + /** + Constructor. + \param type Tipo de dato de esta instancia. + \param maxSize Tamao maximo que puede tener este dato. Deberia coincidir con el indicado + por la columna con la que vaya a corresponder en la sentencia. + \param isNulleable Indica si el dato puede tomar valores nulos. + + \warning los tipos de datos complejos deberia reimplementar los metodos #code and #decode. + */ + explicit Data(const Type::_v type, const int maxSize, const bool isNulleable) : + a_type(type), + a_maxSize(maxSize), + a_isNulleable(isNulleable), + a_isNull(isNulleable), + a_buffer(NULL) + {;} + + /** + Constructor copia. + \param other Instancia de la que copiar. + */ + Data(const Data& other) : + a_type(other.a_type), + a_maxSize(other.a_maxSize), + a_isNulleable(other.a_isNulleable), + a_isNull(other.a_isNull), + a_buffer(other.a_buffer) + {;} + + /** + Establece el area de memoria asociada a esta variable. + \param buffer Direccion de memoria donde comienza el contenido esta variable. + */ + void setBuffer(void* buffer) throw() { a_buffer = buffer; } + +private: + const Type::_v a_type; + const int a_maxSize; + const bool a_isNulleable; + void* a_buffer; + bool a_isNull; + + virtual void do_clear() throw() = 0; +}; + +} +} + +#endif + diff --git a/include/anna/dbms/Database.hpp b/include/anna/dbms/Database.hpp new file mode 100644 index 0000000..bbe46c5 --- /dev/null +++ b/include/anna/dbms/Database.hpp @@ -0,0 +1,296 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Database_hpp +#define anna_dbms_Database_hpp + +#include + +#include + +#include + +namespace anna { + +namespace comm { +class INetAddress; +class Delivery; +} + +namespace dbms { + +class Statement; +class InputBind; +class OutputBind; +class Data; +class FailRecoveryHandler; +class StatementTranslator; + +/** + Clase que modela la interaccion entre la base y nuestra aplicacion. +*/ +class Database : public app::Component { +public: + /** + Numero maximo de conexiones que podemos crear. + */ + static const int MaxConnection = 32; + + /** + Formas de conexion a la base de datos. + */ + struct Type { + enum _v { Local, Remote } value; + Type() : value(Local) {;} + Type(const _v v) : value(v) {;} + Type(const Type& v) : value(v.value) {;} + operator int () const throw() { return value; } + }; + + typedef std::vector ::const_iterator const_connection_iterator; /**::const_iterator const_statement_iterator; /**::iterator connection_iterator; /** a_connections; + std::vector a_statements; + const Type a_type; + FailRecoveryHandler* a_failRecoveryHandler; + StatementTranslator* a_statementTranslator; + + Database(const Database&); + + void do_cloneChild() throw(RuntimeException); + + virtual Connection* allocateConnection(const std::string& name, const char* user, const char* password) + throw(RuntimeException) = 0; + + virtual Statement* allocateStatement(const char* name, const std::string& expression, const bool isCritical) + throw(RuntimeException) = 0; + + virtual InputBind* allocateInputBind(const char* name, Data& data) + throw(RuntimeException) = 0; + virtual void deallocate(InputBind* inputBind) throw() = 0; + + virtual OutputBind* allocateOutputBind(const char* name, Data& data) + throw(RuntimeException) = 0; + virtual void deallocate(OutputBind* outputBind) throw() = 0; + + friend class Statement; + friend ResultCode Connection::execute(Statement*) throw(RuntimeException, DatabaseException); +}; + +} +} + +#endif diff --git a/include/anna/dbms/DatabaseException.hpp b/include/anna/dbms/DatabaseException.hpp new file mode 100644 index 0000000..66bccee --- /dev/null +++ b/include/anna/dbms/DatabaseException.hpp @@ -0,0 +1,76 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_DatabaseException_hpp +#define anna_dbms_DatabaseException_hpp + +#include + +#include + +namespace anna { + +namespace dbms { + +/** + Exception ocurrida al acceder a algun servicio de la base de datos. +*/ +class DatabaseException : public Exception { +public: + /** + Constructor. + + @param resultCode Clase utilizada para transferir codigo de error entre el gestor de base de datos + y nuestro programa. Entre otra informacion contiene el error ocurrido en la ultima operacion realizada. + @param fromFile Fichero en el que se provoco la situacion de error. + @param fromLine Linea del fichero en la que se provoco la situacion de error. + */ + DatabaseException(const ResultCode& resultCode, const char* fromFile, const int fromLine) : + Exception(resultCode.getErrorText(), "DatabaseException", fromFile, fromLine), + a_resultCode(resultCode) {} + + /** + Constructor. + + @param logicalName Nombre logico del elemento que genera la excepcion. + @param resultCode Clase utilizada para transferir codigo de error entre el gestor de base de datos + y nuestro programa. Entre otra informacion contiene el error ocurrido en la ultima operacion realizada. + @param fromFile Fichero en el que se provoco la situacion de error. + @param fromLine Linea del fichero en la que se provoco la situacion de error. + */ + DatabaseException(const std::string& logicalName, const ResultCode& resultCode, const char* fromFile, const int fromLine) : + Exception("", "DatabaseException", fromFile, fromLine), + a_resultCode(resultCode) { + std::string aux(logicalName); + aux += ": "; + aux += resultCode.getErrorText(); + setText(aux.c_str()); + } + + /** + Destructor. + */ + virtual ~DatabaseException() throw() {;} + + /** + Devuelve el resultado de base de datos asociado a la excepcion + + @return El resultado de base de datos asociado a la excepcion + */ + const ResultCode& getResultCode() const throw() { return a_resultCode; } + +private: + const ResultCode a_resultCode; +}; + +} +} + + +#endif + diff --git a/include/anna/dbms/Date.hpp b/include/anna/dbms/Date.hpp new file mode 100644 index 0000000..92e96cc --- /dev/null +++ b/include/anna/dbms/Date.hpp @@ -0,0 +1,337 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Date_hpp +#define anna_dbms_Date_hpp + +#include + +#include +#include + +#include + +namespace anna { + +namespace dbms { + +/** + Tipo de datos que permite trabajar con el tipo de dato 'Date' de un gestor de base de + datos generico. + + Dependiendo el gestor de base de datos usado el tipo \em date puede contener informacion que incluya + la hora del día, en Oracle (tm) la incluye, mientras que en mysql, por ejemplo, no la incluye. + + Internamente trabaja con una estructura de tipo 'tm' que habitualmente tendrá los campos: + \code + struct tm { + int tm_sec; // Seconds. [0-60] (1 leap second) + int tm_min; // Minutes. [0-59] + int tm_hour; // Hours. [0-23] + int tm_mday; // Day. [1-31] + int tm_mon; // Month. [0-11] + int tm_year; // Year - 1900. + int tm_wday; // Day of week. [0-6] + int tm_yday; // Days in year.[0-365] + int tm_isdst; // DST. [-1/0/1] + }; + \endcode +*/ +class Date : public Data { +public: + /** + * Espacio maximo reservado para representar lo datos de una fecha sobre una cadena. + */ + static const int MaxDateSize = 48; + + /** + Constructor. + \param isNulleable Indica si el dato puede tomar valores nulos. + \param format Formato usado para interpretar los datos de esta fecha, en los metodos Date::getCStringValue y + Date::setValue (const char*) y Date::setValue (const std::string&). Sigue la especificacion: + + \code + %a Replaced by the localeâs abbreviated weekday name. [ tm_wday] + + %A Replaced by the localeâs full weekday name. [ tm_wday] + + %b Replaced by the localeâs abbreviated month name. [ tm_mon] + + %B Replaced by the localeâs full month name. [ tm_mon] + + %c Replaced by the localeâs appropriate date and time representation. (See the Base Definitions volume of + IEEE Std 1003.1-2001, .) + + %C Replaced by the year divided by 100 and truncated to an integer, as a decimal number [00,99]. [ tm_year] + + %d Replaced by the day of the month as a decimal number [01,31]. [ tm_mday] + + %D Equivalent to %m / %d / %y . [ tm_mon, tm_mday, tm_year] + + %e Replaced by the day of the month as a decimal number [1,31]; a single digit is preceded by a space. [ + tm_mday] + + %F Equivalent to %Y - %m - %d (the ISO 8601:2000 standard date format). [ tm_year, tm_mon, tm_mday] + + %g Replaced by the last 2 digits of the week-based year (see below) as a decimal number [00,99]. [ tm_year, + tm_wday, tm_yday] + + %G Replaced by the week-based year (see below) as a decimal number (for example, 1977). [ tm_year, tm_wday, + tm_yday] + + %h Equivalent to %b . [ tm_mon] + + %H Replaced by the hour (24-hour clock) as a decimal number [00,23]. [ tm_hour] + + %I Replaced by the hour (12-hour clock) as a decimal number [01,12]. [ tm_hour] + + %j Replaced by the day of the year as a decimal number [001,366]. [ tm_yday] + + %m Replaced by the month as a decimal number [01,12]. [ tm_mon] + + %M Replaced by the minute as a decimal number [00,59]. [ tm_min] + + %n Replaced by a . + + %p Replaced by the localeâs equivalent of either a.m. or p.m. [ tm_hour] + + %r Replaced by the time in a.m. and p.m. notation; in the POSIX locale this shall be equivalent to %I : %M + : %S %p . [ tm_hour, tm_min, tm_sec] + + %R Replaced by the time in 24-hour notation ( %H : %M ). [ tm_hour, tm_min] + + %S Replaced by the second as a decimal number [00,60]. [ tm_sec] + + %t Replaced by a . + + %T Replaced by the time ( %H : %M : %S ). [ tm_hour, tm_min, tm_sec] + + %u Replaced by the weekday as a decimal number [1,7], with 1 representing Monday. [ tm_wday] + + %U Replaced by the week number of the year as a decimal number [00,53]. The first Sunday of January is the + first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + + %V Replaced by the week number of the year (Monday as the first day of the week) as a decimal number [01,53]. + If the week containing 1 January has four or more days in the new year, then it is considered week 1. Oth- + erwise, it is the last week of the previous year, and the next week is week 1. Both January 4th and the + first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] + + %w Replaced by the weekday as a decimal number [0,6], with 0 representing Sunday. [ tm_wday] + + %W Replaced by the week number of the year as a decimal number [00,53]. The first Monday of January is the + first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + + %x Replaced by the localeâs appropriate date representation. (See the Base Definitions volume of + IEEE Std 1003.1-2001, .) + + %X Replaced by the localeâs appropriate time representation. (See the Base Definitions volume of + IEEE Std 1003.1-2001, .) + + %y Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] + + %Y Replaced by the year as a decimal number (for example, 1997). [ tm_year] + + %z Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), or by no charac- + ters if no timezone is determinable. For example, "-0430" means 4 hours 30 minutes behind UTC (west of + Greenwich). If tm_isdst is zero, the standard time offset is used. If tm_isdst is greater than zero, the + daylight savings time offset is used. If tm_isdst is negative, no characters are returned. [ tm_isdst] + + %Z Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ + tm_isdst] + + %% Replaced by % . + \endcode + + Para obtener más informacion sobre la espeficacion de formato \em man \em strftime (p.e.). + */ + explicit Date(const bool isNulleable = false, const char* format = NULL) ; + + /** + Constructor copia. + \param other Instancia de la que copiar. + */ + Date(const Date& other); + + /** + Destructor. + */ + virtual ~Date(); + + /** + Devuelve el contenido de esta fecha. + \return El contenido de esta fecha. + \warning Si el metodo Data::isNull devolvio \em true el contenido de la estructura no esta definido. + */ + const tm& getValue() const throw() { return a_value; } + + /** + Devuelve el contenido de esta fecha. + \return El contenido de esta fecha. + \warning Si el metodo Data::isNull devolvio \em true el contenido de la estructura no esta definido. + */ + tm& getValue() throw() { return a_value; } + + /** + * Interpreta el contenido de la fecha y lo transfiere al buffer. + * \return El buffer que contiene esta fecha interpretada con el formato indicado en el contructor. + * \warning El resultado sera NULL en caso de no poder interpretar correctamente la fecha. + */ + virtual const char* getCStringValue() const throw(); + + /** + * Interpreta el contenido de esta fecha como el numero de segundos transcurridos desde el 1 de Enero de 1970. + * Si el contenido de la columna sociada es nulo este metodo devolvera 0. Si la conversion a segundos no puede + * ser realizada devolvera -1. + * \return Interpreta el contenido de esta fecha como el numero de segundos transcurridos desde el 1 de Enero de 1970. + * Si el contenido de la columna sociada es nulo este metodo devolvera 0. Si la conversion a + * segundos no puede ser realizada devolvera -1. + */ + Second getSecondValue() const throw() { return Second((Data::isNull() == true) ? 0 : mktime(&const_cast (this)->a_value)); } + + /** + * Devuelve el formato indicado en el constructor de la clase. + * \return El formato indicado en el constructor de la clase. + */ + const char* getFormat() const throw() { return a_format; } + + /** + * Devuelve el año contenido por esta fecha. + * \return El año contenido por esta fecha. + */ + int getYear() const throw() { return a_value.tm_year + 1900; } + + /** + * Devuelve el mes contenido por esta fecha. + * \return El mes contenido por esta fecha. + */ + int getMonth() const throw() { return a_value.tm_mon + 1; } + + /** + * Devuelve el dia del mes contenido por esta fecha. + * \return El dia del mes contenido por esta fecha. + */ + int getDay() const throw() { return a_value.tm_mday; } + + /** + * Devuelve la hora del dia contenida en la fecha. + * \return La hora del dia contenida en la fecha. + * \warning Verifique que el tipo 'Date' de su RDBMS es capaz de contener horas, minutos y segundos. + */ + int getHour() const throw() { return a_value.tm_hour; } + + /** + * Devuelve el minuto de la hora contenida en la fecha. + * \return El minuto de la hora contenida en la fecha. + * \warning Verifique que el tipo 'Date' de su RDBMS es capaz de contener horas, minutos y segundos. + */ + int getMinute() const throw() { return a_value.tm_min; } + + /** + * Devuelve el segundo de la hora contenida en la fecha. + * \return El segundo de la hora contenida en la fecha. + * \warning Verifique que el tipo 'Date' de su RDBMS es capaz de contener horas, minutos y segundos. + */ + int getSecond() const throw() { return a_value.tm_sec; } + + /** + * Establece el año de esta fecha + * \param year Año de la fecha. Debe ser mayor de 1900. + */ + void setYear(const int year) throw(RuntimeException) { set("Year", a_value.tm_year, year - 1900, 0, -1); } + + /** + * Establece mes de esta fecha. + * \param month Mes de la fecha. Debe estar comprendido entre 1 y 12. + */ + void setMonth(const int month) throw(RuntimeException) { set("Month", a_value.tm_mon, month - 1, 0, 11); } + + /** + * Establece el dia del mes de esta fecha. + * \param day Dia del mes. Debe estar comprendido entre 1 y 31. + */ + void setDay(const int day) throw(RuntimeException) { set("Day", a_value.tm_mday, day, 1, 31); } + + /** + * Establece la hora de esta fecha. + * \param hour Hora del dia. Debe estar comprendida entre 0 y 23. + * \warning Verifique que el tipo 'Date' de su RDBMS es capaz de contener horas, minutos y segundos. + */ + void setHour(const int hour) throw(RuntimeException) { set("Hour", a_value.tm_hour, hour, 0, 23); } + + /** + * Establece el minuto de esta fecha. + * \param minute Minuto de la hora del dia. Debe estar comprendida entre 0 y 59. + * \warning Verifique que el tipo 'Date' de su RDBMS es capaz de contener horas, minutos y segundos. + */ + void setMinute(const int minute) throw(RuntimeException) { set("Minute", a_value.tm_min, minute, 0, 59); } + + /** + * Establece el segundo de esta fecha. + * \param second Segungo de la hora del dia. Debe estar comprendida entre 0 y 60. + * \warning Verifique que el tipo 'Date' de su RDBMS es capaz de contener horas, minutos y segundos. + */ + void setSecond(const int second) throw(RuntimeException) { set("Second", a_value.tm_sec, second, 0, 60); } + + /** + Interpreta la cadena recibida segun el formato indicado en el constructor y la asigna a esta instancia, pero requiere que al + invocar al constructor de esta fecha se indique el formato usado para traducir. + \param str Cadena de la que copiar. + */ + void setValue(const char* str) throw(RuntimeException); + + /** + Interpreta la cadena recibida segun el formato indicado en el constructor y la asigna a esta instancia, pero requiere que al + invocar al constructor de esta fecha se indique el formato usado para traducir. + \param str Cadena de la que copiar. + */ + void setValue(const std::string& str) throw(RuntimeException) { setValue(str.c_str()); } + + /** + * Establece esta fecha con los segundos transcurridos desde el 1/1/1970. + * \param second Numeros de segundos transcurridos desde el 1 de Enero de 1970. + * \see anna::functions::second + */ + void setValue(const Second &second) throw(RuntimeException); + + /** + Operador de copia. + \param date Fecha de la que copiar. + \return La instancia de esta fecha. + \warning Solo copia el contenido de la fecha recibida, no cambia el formato de interpretacion de la fecha origen. + */ + Date& operator = (const Date& date) throw(RuntimeException); + + /** + Devuelve una cadena con la informacion referente a esta instancia. + \return Una cadena con la informacion referente a esta instancia. + */ + virtual std::string asString() const throw(); + +protected: + char* a_format; + tm a_value; + char a_buffer [MaxDateSize + 1]; + + /** + * Constructor invocado desde el constructor de TimeStamp. + \param type Sera Data::Type::TimeStamp. + \param isNulleable Indica si el dato puede tomar valores nulos. + \param format Formato usado para representar los datos de esta fecha. + */ + explicit Date(const Type::_v type, const bool isNulleable, const char* format); + +private: + void set(const char* what, int& variable, const int value, const int min, const int max) throw(RuntimeException); + void do_clear() throw() { anna_memset(&a_value, 0, sizeof(a_value)); } +}; + +} +} + +#endif + diff --git a/include/anna/dbms/Delivery.hpp b/include/anna/dbms/Delivery.hpp new file mode 100644 index 0000000..6cd4603 --- /dev/null +++ b/include/anna/dbms/Delivery.hpp @@ -0,0 +1,79 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Delivery_hpp +#define anna_dbms_Delivery_hpp + +#include + +namespace anna { + +namespace dbms { + +class Connection; + +/** + Agrupacion logica de conexiones con la base de datos. Reparte la carga de las transacciones contra + la base de datos entre las distintas conexiones que contenga esta instancia. Ademas en caso de estar + en una ejecucion con soporte para multithread (ver anna::functions::supportMultithread) asegura + que cada uno de los threads siempre utiliza la misma conexion lo cual asegura el mantinimiento de la + integridad de cada una de las transacciones de los threads. +*/ +class Delivery : comm::Delivery { +public: + /** + Constructor. + @param name Nombre logico de este grupo de conexiones. + */ + Delivery(const char *name) : comm::Delivery(name) {;} + + /** + Crea automaticamente las conexiones a la base de datos recibida como parametro con el usuario/password + indicado. + \param database Instancia de la base de datos contra la que realizamos la conexion. + \param prefixName Prefijo del nombre logico de la conexiones que vamos a crear. El resto del nombre vendra + dado por el numero secuencial de la conexion. + \param user Nombre del usuario con el que realizamos la conexion. + \param password Codigo de acceso del usuario. + \param n Numero de conexion a crear. + \warning Recordar que el numero maximo de conexiones a una base de datos esta limitado por Database::maxConnection. + */ + void createConnections(Database& database, const char* prefixName, const char* user, const char* password, const int n) + throw(RuntimeException, DatabaseException); + + /** + Incorpora al conexion recibida como parametro a la agrupacion logica. + \param connection Conexion que vamos a incorporar a la agrupacion logica. + */ + void addConnection(Connection* connection) throw(RuntimeException) { + this->add(connection); + a_iiConnection = this->begin(); + } + + /** + Devuelve la instancia de la conexion a base de datos con la que debemos trabajar. + @return la instancia de la conexion a base de datos con la que debemos trabajar. + \warning La conexion debe ser bloqueada por el Thread que la recibe (ver anna::Guard) para asegurar que + cualquier otro thread que intente acceder a ella queda bloqueado a la espera de que terminemos de + trabajar sobre ella. + */ + Connection& getConnection() throw(RuntimeException); + +private: + iterator a_iiConnection; + + void do_initialize() throw() { a_iiConnection = begin(); } + comm::Resource* do_apply() throw(RuntimeException); + static Connection* connection(iterator& ii) { return (Connection*) comm::Delivery::resource(ii); } + +}; + +} +} + +#endif diff --git a/include/anna/dbms/FailRecoveryHandler.hpp b/include/anna/dbms/FailRecoveryHandler.hpp new file mode 100644 index 0000000..7519854 --- /dev/null +++ b/include/anna/dbms/FailRecoveryHandler.hpp @@ -0,0 +1,44 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_FailRecoveryHandler_hpp +#define anna_dbms_FailRecoveryHandler_hpp + +namespace anna { + +namespace dbms { + +class Database; +class Connection; + +/** + Interfaz que deben cumplir los manejadores que reciben la notificacion de que no ha sido posible + restaurar una determina conexion con la base de datos. +*/ +class FailRecoveryHandler { +protected: + /** + Este metodo debe ser reimplementado para describir las operaciones que vamos a realizar en caso + de no poder recuperar la conexion recibida como parametro. + \param connection Instancia de la conexion en la que hemos detectado el fallo. + \param tryCounter Numero de intentos de recuperacion de la conexion. + + \warning Este metodo se invocara automaticamente desde anna::dbms::Database::recover. + */ + virtual void apply(Connection& connection, const int tryCounter) throw(RuntimeException) = 0; + + friend class Database; +}; + +} +} + +#endif + + + diff --git a/include/anna/dbms/Float.hpp b/include/anna/dbms/Float.hpp new file mode 100644 index 0000000..427668e --- /dev/null +++ b/include/anna/dbms/Float.hpp @@ -0,0 +1,115 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Float_hpp +#define anna_dbms_Float_hpp + +#include + +#include + +namespace anna { + +namespace dbms { + +/** + Numero en coma flotante usado como entrada y/o salida de las sentencias SQL. +*/ +class Float : public Data { +public: + /** + Constructor. + \param isNulleable Indica si el dato puede tomar valores nulos. + \para format Indica el indicador de formato para pasar de cadena a numero. Usa la misma nomenclatura + que printf, scanf, etc. Su uso dependerá del gestor de base de datos usado. + */ + explicit Float(const bool isNulleable = false, const char* format = "%f") : + Data(Type::Float, sizeof(float), isNulleable), + a_format(format), + a_value(0.0) { + Data::setBuffer(&a_value); + } + + /** + Constructor copia. + \param other Instancia de la que copiar. + */ + Float(const Float& other) : Data(other), a_value(other.a_value), a_format(other.a_format) { + Data::setBuffer(&a_value); + } + + /** + * Metodo obsoleto, debería usar #getValue. + Devuelve el contenido del campo de tipo Float. + \return el contenido del campo de tipo Float. + \warning Si el metodo Data::isNull devolvio \em true el resultado de este metodo no esta definido. + */ + //float getFloatValue () const throw () { return getValue (); } + + /** + * Devuelve el valor asociado a este campo. + * \return Devuelve el valor asociado a este campo. + */ + float getValue() const throw() { return a_value; } + + /** + * Devuelve el formato que indica la forma en la que el número será representado sobre + * una cadena, en caso de que fuera necesario. + */ + const char* getFormat() const throw() { return a_format; } + + /** + Operador de copia. + \param other Float del que copiar. + \return La instancia de esta cadena. + */ + Float& operator = (const Float& other) throw(RuntimeException) { + if(this != &other) { + setNull(other.isNull()); + a_value = other.a_value; + } + + return *this; + } + + /** + Operador de asignacion. + \param value Float del que copiar. + \return La instancia de esta cadena. + */ + Float& operator = (const float value) throw(RuntimeException) { + a_value = value; + setNull(false); + return *this; + } + + /** + Operador de conversion. + \warning Si la columna asociada tiene un valor NULL, devolvera 0.0. + \return El contenido de esta cadena. + */ + operator float() const throw() { return getValue(); } + + /** + Devuelve una cadena con la informacion referente a esta instancia. + \return Una cadena con la informacion referente a esta instancia. + */ + std::string asString() const throw(); + +private: + float a_value; + const char* a_format; + + void do_clear() throw() { a_value = 0.0; } +}; + +} +} + +#endif + diff --git a/include/anna/dbms/InputBind.hpp b/include/anna/dbms/InputBind.hpp new file mode 100644 index 0000000..069fb22 --- /dev/null +++ b/include/anna/dbms/InputBind.hpp @@ -0,0 +1,32 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_InputBind_hpp +#define anna_dbms_InputBind_hpp + +#include + +namespace anna { + +namespace dbms { + +class InputBind : public Bind { +protected: + InputBind(const char* name, dbms::Data& value) : Bind(name, value) {;} + +private: + // Éste metodo no sera invocado nunca. A partir de un Input nunca hay que + // convertir de C++ -> RDBMS + void decode() const throw(RuntimeException) {;} +}; + +} +} + +#endif + diff --git a/include/anna/dbms/Integer.hpp b/include/anna/dbms/Integer.hpp new file mode 100644 index 0000000..ce98691 --- /dev/null +++ b/include/anna/dbms/Integer.hpp @@ -0,0 +1,98 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Integer_hpp +#define anna_dbms_Integer_hpp + +#include + +namespace anna { + +namespace dbms { + +/** + Cadena usada como entrada y/o salida de las sentencias SQL. +*/ +class Integer : public Data { +public: + /** + Constructor. + \param isNulleable Indica si el dato puede tomar valores nulos + */ + explicit Integer(const bool isNulleable = false) : + Data(Type::Integer, sizeof(int), isNulleable), + a_value(0) { + Data::setBuffer(&a_value); + } + + /** + Constructor copia. + \param other Instancia de la que copiar. + */ + Integer(const Integer& other) : + Data(other), + a_value(other.a_value) { + Data::setBuffer(&a_value); + } + + /** + Devuelve el valor entero asociado a esta instancia. + \return El valor entero asociado a esta instancia. + */ + int getValue() const throw() { return a_value; } + + /** + Operador de asignacin entero. + \param i Valor entero a asignar. + \return La referencia a esta instancia. + */ + Integer& operator = (const int i) + throw() { + a_value = i; + Data::setNull(false); + return *this; + } + + /** + Operador copia. + \param other Instancia de la que copiar. + \return La referencia a esta instancia. + */ + Integer& operator = (const Integer& other) + throw() { + if(this != &other) { + setNull(other.isNull()); + a_value = other.a_value; + } + + return *this; + } + + /** + Operador de conversion. + \return El valor entero asociado a esta instancia. + */ + operator int () const throw() { return a_value; } + + /** + Devuelve una cadena con la informacion referente a esta instancia. + @return Una cadena con la informacion referente a esta instancia. + */ + std::string asString() const throw(); + +private: + int a_value; + + void do_clear() throw() { a_value = 0; } +}; + +} +} + +#endif + diff --git a/include/anna/dbms/LongBlock.hpp b/include/anna/dbms/LongBlock.hpp new file mode 100644 index 0000000..d0cb9c0 --- /dev/null +++ b/include/anna/dbms/LongBlock.hpp @@ -0,0 +1,122 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_LongBlock_hpp +#define anna_dbms_LongBlock_hpp + +#include + +#include +#include + +#include + +namespace anna { + +namespace dbms { + +/** + Bloque de datos usado como entrada y/o salida de las sentencias SQL. + + A diferencia del tipo de datos ShortBlock, en principio, no tiene ninguna limitacion + en cuanto a la longitud del campo que vamos a tratar. Por contra, dependiendo del motor + de base de datos que vayamos a usar puede tener un tratamiento especial a la hora de + grabarlo en la base de datos. + + \see ShortBlock +*/ +class LongBlock : public Data { +public: + /** + Constructor. + \param isNulleable Indica si el dato puede tomar valores nulos. + */ + explicit LongBlock(const bool isNulleable = false) : + Data(Type::LongBlock, 0, isNulleable), + a_value(true) { + Data::setBuffer((void*) NULL); + } + + /** + Constructor copia. + \param other Instancia de la que copiar. + */ + LongBlock(const LongBlock& other) : + Data(other), + a_value(true) { + a_value = other.a_value; + } + + /** + Destructor. + */ + virtual ~LongBlock() {;} + + /** + Devuelve el tamao actual de este dato. + \return El tamao actual de este dato. + */ + int getSize() const throw() { return a_value.getSize(); } + + /** + Devuelve el contenido de la este bloque de memoria. + \return Devuelve el contenido de la este bloque de memoria. + \warning Si el metodo Data::isNull devolvio \em true el resultado de este metodo no esta definido. + */ + const anna::DataBlock& getValue() const throw() { return a_value; } + + /** + Devuelve el contenido de la este bloque de memoria. + \return Devuelve el contenido de la este bloque de memoria. + \warning Si el metodo Data::isNull devolvio \em true el resultado de este metodo no esta definido. + */ + anna::DataBlock& getValue() throw() { return a_value; } + + /** + Operador de asignacin. + \param other Bloque del que copiar. + \return La instancia de este bloque de memoria. + */ + LongBlock& operator = (const LongBlock& other) throw(RuntimeException); + + /** + Operador de asignacin. + \param value Valor que queremos a asignar. + \return La instancia de esta cadena. + */ + LongBlock& operator = (const anna::DataBlock& value) throw(RuntimeException); + + /** + Operador de conversion. + \return El anna::DataBlock asociado a esta instancia. + */ + operator anna::DataBlock& () throw() { return a_value; } + + /** + Operador de conversion. + \return El anna::DataBlock asociado a esta instancia. + */ + operator const anna::DataBlock& () const throw() { return a_value; } + + /** + Devuelve una cadena con la informacion referente a esta instancia. + \return Una cadena con la informacion referente a esta instancia. + */ + std::string asString() const throw(); + +protected: + anna::DataBlock a_value; + + void do_clear() throw() { a_value.clear(); } +}; + +} +} + +#endif + diff --git a/include/anna/dbms/OutputBind.hpp b/include/anna/dbms/OutputBind.hpp new file mode 100644 index 0000000..e0c7c70 --- /dev/null +++ b/include/anna/dbms/OutputBind.hpp @@ -0,0 +1,53 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_OutputBind_hpp +#define anna_dbms_OutputBind_hpp + +#include + +namespace anna { + +namespace dbms { + +class LongBlock; + +/** + Clase que facilita la interconexion entre las variables del ambito C++ y el ambito RDBMS. + + A continuacion presentamos un ejemplo de uso detallado. + \include db_blob.p/main.cc +*/ +class OutputBind : public Bind { +public: + /** + Graba el valor de la variable anna::dbms::LongBlock asociada a esta OutputBind. Cualquier + modificacion que necesitemos aplicar sobre la columnna de tipo se debera hacer mediante los + metodos ofrecidos por la clase anna::dbms::LongBlock. + + \warning Este metodo solo puede ser usado para variables de tipo dbms::Data::Type::LongBlock y + siempre y cuando hayamos abierto el BLOB con una sentencia SQL de seleccion. + */ + void write() const throw(RuntimeException, dbms::DatabaseException); + +protected: + OutputBind(const char* name, dbms::Data& value) : Bind(name, value) {;} + +private: + // este metodo no sera invocado nunca. A partir de un Output nunca hay que + // convertir de C++ -> RDBMS + void code() const throw(RuntimeException) {;} + + virtual void do_write(const dbms::LongBlock&) const throw(RuntimeException, dbms::DatabaseException) = 0; +}; + +} +} + +#endif + diff --git a/include/anna/dbms/ResultCode.hpp b/include/anna/dbms/ResultCode.hpp new file mode 100644 index 0000000..6489594 --- /dev/null +++ b/include/anna/dbms/ResultCode.hpp @@ -0,0 +1,172 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_ResultCode_hpp +#define anna_dbms_ResultCode_hpp + +#include + +#include + +#include + +#include +#include + +namespace anna { + +namespace dbms { + +/** + Clase para acceder a la informacion devuelta por el gestor de base de datos + referente al ultimo comando realizado. + */ +class ResultCode { +public: + /** + Constructor vacio. + \warning Antes de usarse debe asignarse a algun otro ResultCode obtenido mediante la invocacion + a anna::dbms::Connection::execute. + */ + ResultCode() : a_errorText(NULL), a_errorDecoder(NULL), a_errorCode(0) {;} + + /** + Constructor copia. + @param other Instancia de la que copiar los datos. + */ + ResultCode(const ResultCode& other) : + a_errorText(NULL), + a_errorDecoder(other.a_errorDecoder) { + set(other.a_errorCode, other.a_errorText); + } + + /** + Destructor. + */ + virtual ~ResultCode() { if(a_errorText != NULL) free(a_errorText); } + + /** + Devuelve el codigo de error del ultimo comando ejecutado contra la base de datos. + @return El codigo de error del ultimo comando ejecutado contra la base de datos. + */ + int getErrorCode() const throw() { return a_errorCode; } + + /** + Devuelve el texto del error del ultimo comando ejecutado contra la base de datos. + @return El texto del error del ultimo comando ejecutado contra la base de datos. + */ + const char* getErrorText() const throw() { return (a_errorText != NULL) ? a_errorText : ""; } + + // Operadores + /** + Operador copia. + @param resultCode Instancia a copiar. + @return Una instancia de si mismo. + */ + ResultCode& operator = (const ResultCode& resultCode) + throw() { + if(this != &resultCode) { + a_errorDecoder = resultCode.a_errorDecoder; + set(resultCode.a_errorCode, resultCode.a_errorText); + } + + return *this; + } + + /** + Devuelve \em true si las condiciones de busqueda de la ultimo operacion + no han sido satisfechas por ningun registro o \em false en otro caso. + @return \em true si las condiciones de busqueda de la ultimo operacion + no han sido satisfechas por ningun registro o \em false en otro caso. + */ + bool notFound() const throw(anna::RuntimeException); + + /** + Devuelve \em true si la ultima operacion solicitada fue realizada correctamente + o \em false en otro caso. + @return \em true si la ultima operacion solicitada fue realizada correctamente + o \em false en otro caso. + */ + bool successful() const throw(anna::RuntimeException); + + /** + Devuelve \em true Si el registro obtenenido en una sentencia de seleccion con indicador + de modo exclusivo ha sido bloqueada previamente por otro proceso y/o contexto de base de + datos o \em false en otro caso. + @return \em true Si el registro obtenenido en una sentencia de seleccion con indicador + de modo exclusivo ha sido bloqueada previamente por otro proceso y/o contexto de base de + datos o \em false en otro caso. + */ + bool locked() const throw(anna::RuntimeException); + + /** + Devuelve \em true si se perdio la conexion la base de datos o \em false en otro caso. + @return \em true si se perdio la conexion la base de datos o \em false en otro caso. + */ + bool lostConnection() const throw(anna::RuntimeException); + + /** + Devuelve una cadena con la informacion sobre esta clase. + \return Una cadena con la informacion sobre esta clase. + */ + std::string asString() const throw(); + +protected: + static const int MaxErrorLen = 512; + + /** + Decodificador del error devuelto por el RDBMS concreto que estemos usando. + \warning Exclusivamente uso interno. + */ + class ErrorDecoder { + public: + virtual bool notFound(const int errorCode) const throw() = 0; + virtual bool successful(const int errorCode) const throw() = 0; + virtual bool locked(const int errorCode) const throw() = 0; + virtual bool lostConnection(const int errorCode) const throw() = 0; + }; + + /** + Constructor. + + \param errorCode Codigo de error asociado a la ultima operacion realizada contra la base de datos. + \param errorText Texto asociado al error de ultima operacion realizada contra la base de datos. Puede ser + NULL si no hay ningun texto de error asociado al codigo recibido. + \param errorDecoder Decofidicador de errores. + */ + ResultCode(const int errorCode, const char* errorText, const ErrorDecoder* errorDecoder) : + a_errorText(NULL), + a_errorDecoder(errorDecoder) { + set(errorCode, errorText); + } + + /** + Establece el contenido de esta clase. + + \param errorCode Codigo de error asociado a la ultima operacion realizada contra la base de datos. + \param errorText Texto asociado al error de ultima operacion realizada contra la base de datos. + */ + void set(const int errorCode, const char* errorText) + throw() { + a_errorCode = errorCode; + copy(errorText); + } + +private: + int a_errorCode; + char* a_errorText; + const ErrorDecoder* a_errorDecoder; + + void copy(const char* text) throw(); +}; + +} +} + +#endif + diff --git a/include/anna/dbms/Sentence.hpp b/include/anna/dbms/Sentence.hpp new file mode 100644 index 0000000..39d045e --- /dev/null +++ b/include/anna/dbms/Sentence.hpp @@ -0,0 +1,122 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Sentence_hpp +#define anna_dbms_Sentence_hpp + +#include + +#include + +namespace anna { + +namespace xml { +class Node; +} + +namespace dbms { + +class Database; +class Connection; +class Statement; + +/** + Clase que facilita la ejecucion de sentencias SQL compuestas y la comprobacion de errores ya que + solo devuelve excepciones de tipo anna::RuntimeException. +*/ +class Sentence : public Mutex { +public: + /** + Modos de actuar cuando se detecta un error en la ejecucion de las sentencias SQL. + \see Sentence + */ + struct Mode { enum _v { ExceptionWhenNotFound, SilentWhenNotFound }; }; + + /** + Ejecuta la sentencia SQL asociada a este instancia. Antes de invocar a este metodo debemos + activar una seccion critica sobre esta instancia. + \param connection Conexion usada para ejecutar la sentencia. Debe tener activa una seccion critica. + */ + virtual dbms::ResultCode execute(dbms::Connection& connection) throw(RuntimeException) { + return execute(connection, a_dbStatement); + } + + /** + Devuelve el nombre de la sentencia SQL asociada a esta instancia. + \return El nombre de la sentencia SQL asociada a esta instancia. + \warning Si todavia no tiene nombre asociado devolvera una cadena vacia. + */ + const std::string& getName() const throw(); + + /** + Inicializa el estado de esta instancia + \param database Instancia de la base de datos usada para definir las sentencias SQL que componen esta + instancia. + */ + void initialize(dbms::Database& database) throw(RuntimeException); + + /** + Transfiere un registro desde la base de datos a las variables del entorno C++. + \return \em false si no hay mas registros o \em true en caso contrario. + */ + bool fetch() throw(RuntimeException); + + /** + Transfiere un registro desde la base de datos a las variables del entorno C++. + \param resultCode Variable que contiene el resultado de invocar a anna::dbms::Sentence::execute + \return \em false si no hay mas registros o \em true en caso contrario. + */ + bool fetch(const ResultCode& resultCode) throw(RuntimeException) { + return (resultCode.successful() == true) ? fetch() : false; + } + + /** + Devuelve una cadena con la informacion referente a esta instancia. + \return una cadena con la informacion referente a esta instancia. + */ + virtual std::string asString() const throw(); + + /** + Devuelve un documento XML con la informacion referente a esta instancia. + \param parent Nodo XML del que dependerá la información referente a esta instancia. + \return un documento XML con la informacion referente a esta instancia. + */ + virtual xml::Node* asXML(xml::Node* parent) const throw(); + +protected: + /** + Constructor. + \param mode Modo de actuacion en caso de detectar errores. + */ + Sentence(const Mode::_v mode = Mode::ExceptionWhenNotFound) : + a_mode(mode), a_dbStatement(NULL) + {;} + + /** + Ejecuta la sentencia SQL asociada a este instancia. + \param connection Conexion usada para ejecutar la sentencia. Debe tener activa una seccion critica. + \param statement Sentencia a ejecutar. + */ + dbms::ResultCode execute(dbms::Connection& connection, dbms::Statement* statement) throw(RuntimeException); + + /** + Metodo que debe inicializar las sentencias asociadas a esta instancia (valores de entrada y salida). + \return Retorna la instancia de la sentencia asociada a esta instancia debidamente inicializada. + */ + virtual dbms::Statement* do_initialize(dbms::Database&) throw(RuntimeException) = 0; + +private: + const Mode::_v a_mode; + dbms::Statement* a_dbStatement; +}; + +} +} + +#endif + diff --git a/include/anna/dbms/ShortBlock.hpp b/include/anna/dbms/ShortBlock.hpp new file mode 100644 index 0000000..cf22c7b --- /dev/null +++ b/include/anna/dbms/ShortBlock.hpp @@ -0,0 +1,115 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_ShortBlock_hpp +#define anna_dbms_ShortBlock_hpp + +#include + +#include +#include + +#include + +namespace anna { + +namespace dbms { + +/** + Bloque de datos usado como entrada y/o salida de las sentencias SQL. + + La longitud del dato a tratar estara en 2048 y 4096 bytes, dependiendo + del RDBMS concreto con el que estemos trabajando. + + \see LongBlock +*/ +class ShortBlock : public Data { +public: + /** + + Constructor. + \param maxSize Tamao maximo que puede tener este bloque. + \param isNulleable Indica si el dato puede tomar valores nulos. + */ + explicit ShortBlock(const int maxSize, const bool isNulleable = false) : + Data(Type::ShortBlock, maxSize, isNulleable), + a_value(true) { + a_value.allocate(maxSize); + Data::setBuffer((void*) a_value.getData()); + } + + /** + Constructor copia. + \param other Instancia de la que copiar. + */ + ShortBlock(const ShortBlock& other) : + Data(other), + a_value(true) { + a_value = other.a_value; + Data::setBuffer((void*) a_value.getData()); + } + + /** + Destructor. + */ + virtual ~ShortBlock() {;} + + /** + Devuelve el tamao actual de este dato. + \return El tamao actual de este dato. + */ + int getSize() const throw() { return a_value.getSize(); } + + /** + Devuelve el contenido de la este bloque de memoria. + \return Devuelve el contenido de la este bloque de memoria. + \warning Si el metodo Data::isNull devolvio \em true el resultado de este metodo no esta definido. + */ + const anna::DataBlock& getValue() const throw() { return a_value; } + + /** + Operador de asignacin. + \param other Bloque del que copiar. + \return La instancia de este bloque de memoria. + */ + ShortBlock& operator = (const ShortBlock& other) throw(RuntimeException); + + /** + Operador de asignacin. + \param value Valor que queremos a asignar. + \return La instancia de esta cadena. + */ + ShortBlock& operator = (const anna::DataBlock& value) throw(RuntimeException); + + /** + Operador de conversion. + \return El anna::DataBlock asociado a esta instancia. + */ + operator anna::DataBlock& () throw() { return a_value; } + + /** + Operador de conversion. + \return El anna::DataBlock asociado a esta instancia. + */ + operator const anna::DataBlock& () const throw() { return a_value; } + + /** + Devuelve una cadena con la informacion referente a esta instancia. + \return Una cadena con la informacion referente a esta instancia. + */ + std::string asString() const throw(); + +protected: + anna::DataBlock a_value; +}; + +} +} + +#endif + diff --git a/include/anna/dbms/Statement.hpp b/include/anna/dbms/Statement.hpp new file mode 100644 index 0000000..df20293 --- /dev/null +++ b/include/anna/dbms/Statement.hpp @@ -0,0 +1,291 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_Statement_hpp +#define anna_dbms_Statement_hpp + +#include + +#include +#include +#include +#include + +#include +#include + +namespace anna { + +namespace xml { +class Node; +} + +namespace dbms { + +class Database; +class Connection; +class InputBind; +class OutputBind; +class Data; + +/** + Clase que facilita la ejecucion de sentencias SQL. + + Para obtener la instancia de un comando para una determinada base de datos habra que instanciar + dicha base de datos e invocar al metodo Database::createStatement. Independientemente del tipo de comando + particular que la base de datos retorne, debemos asignarlo a un puntero de tipo anna::dbms::Statement. +*/ +class Statement : public Mutex { +public: + typedef std::vector ::iterator input_iterator; + typedef std::vector ::iterator output_iterator; + + /** + Destructor. + */ + virtual ~Statement(); + + /** + Devuelve el nombre logico de esta sentencia. + \return El nombre logico de esta sentencia. + */ + const std::string& getName() const throw() { return a_name; } + + /** + Devuelve la expresion SQL recibida en el constructor. + \return La expresion SQL recibida en el constructor. + */ + const std::string& getExpression() const throw() { return a_expression; } + + /** + Devuelve el indicador de criticidad, si vale \em true indica que si la ejecucion de esta sentencia + falla al desbloquear la conexion con la que ejecutamos esta sentencia se invocara a + Connection::rollback, en otro caso aunque falle se invocara a Connection::commit. + Solo aplicara en sentencias que no sean de seleccion. + \return El indicador de criticidad de esta sentencia. + */ + bool isCritical() const throw() { return a_isCritical; } + + /** + Devuelve la instancia de la base de datos asociada a esta sentencia. + \return La instancia de la base de datos asociada a la sentencia. + */ + Database& getDatabase() const throw() { return a_database; } + + /** + Establece el parametro de entrada de la sentencia SQL.Cada una de las variables de entrada indicadas + en esta sentencia SQL deberia tener un parametro de entrada asociado. La correspondencia entre esta + variable y la sentencia SQL vendra dada por el orden de aparacion en la sentencia SQL y por el orden + de definicion del parametro. + + Por ejemplo la sentencia: + + \code + update tabla1 set xx = :unavariable where yy = :otravariable; + \endcode + + Cada una de las variables \em unavariable y \em otravariable debera tener asociado una variable de entrada. + Primero debemos indicaremos la correspondiente a \em unavariable y luego la correspondiente a \em otravariable. + + \param name Nombre logico de esta variable. No tiene porque tener ninguna correspondencia con el nombre + de la columna. + \param data Variable que deseamos asociar como variable de entrada. La correspondencia entre esta + y la sentencia SQL vendra dada por el orden de declaracion. + */ + void bindInput(const char* name, Data& data) throw(); + + /** + Establece el parametro de salida de la sentencia SQL.Cada una de las variables de salida indicadas + en esta sentencia SQL deberia tener un parametro de salida asociado. La correspondencia entre esta + variable y la sentencia SQL vendra dada por el orden de aparacion en la sentencia SQL y por el orden + de definicion del parametro. + + Por ejemplo la sentencia: + + \code + select xx, yy from tabla1 where zz = :unavariable; + \endcode + + Cada una de las variables \em xx e \em yy debera tener asociado una variable de salida. + Ademas la variable \em unavaraible debera tener asociada una variable de entrada. + + \param name Nombre logico de esta variable. No tiene porque tener ninguna correspondencia con el nombre + de la columna. + \param data Variable que deseamos asociar como variable de salida. La correspondencia entre esta + y la sentencia SQL vendra dada por el orden de declaracion. + + \return La instancia del dbms::OutputBind asociado al dato. nos permite + grabar el contenido de los tipos de datos BLOB. + + \warning Solo las sentencias SQL del tipo \em select usan las variables de salida. + */ + const dbms::OutputBind* bindOutput(const char* name, Data& data) throw(); + + /** + Establece el valor del indicador que activa/desactiva la necesidad de invocar al + \em commit y/o \em rollback despues de ejecutar esta sentencia. + */ + void setRequiresCommit(const bool requiresCommit) throw() { a_requiresCommit = requiresCommit; } + + /** + Devuelve \em true si la sentencia requiere la invocacion a \em commit o \em rollback + tras su ejecucion. Puede devolver \em true por tratarse de una sentencia que no tiene variables + de salida (insert, update o delete) o bien porque se haya activado el indicador correspondiente + mediante la llamada #setRequiresCommit + */ + bool requiresCommit() const throw() { return (a_requiresCommit == true) || (a_outputBinds.empty() == true); } + + /** + Devuelve el iterador que apunta a la primera variable de entrada. + \return el iterador que apunta a la primera variable de entrada. + */ + input_iterator input_begin() throw() { return a_inputBinds.begin(); } + + /** + Devuelve el iterador que apunta a la primera variable de entrada. + \return el iterador que apunta a la primera variable de entrada. + */ + input_iterator input_end() throw() { return a_inputBinds.end(); } + + /** + Devuelve el numero de variables de entrada asociado a esta sentencia SQL. + \return el numero de variables de entrada asociado a esta sentencia SQL. + */ + int input_size() const throw() { return a_inputBinds.size(); } + + /** + Devuelve el iterador que apunta a la primera variable de salida. + \return el iterador que apunta a la primera variable de salida. + */ + output_iterator output_begin() throw() { return a_outputBinds.begin(); } + + /** + Devuelve el iterador que apunta a la primera variable de salida. + \return el iterador que apunta a la primera variable de salida. + */ + output_iterator output_end() throw() { return a_outputBinds.end(); } + + /** + Devuelve el numero de variables de entrada asociado a esta sentencia SQL. + \return el numero de variables de entrada asociado a esta sentencia SQL. + */ + int output_size() const throw() { return a_outputBinds.size(); } + + /** + Devuelve un documento XML con la informacion referente a esta instancia. + \param parent Nodo XML del que debe colgar la informacion. + @return un documento XML con la informacion referente a esta instancia. + */ + virtual xml::Node* asXML(xml::Node* parent) const throw(); + + /** + Devuelve una cadena con la informacion referente a esta instancia. + @return Una cadena con la informacion referente a esta instancia. + */ + virtual std::string asString() const throw(); + + /** + Transfiere la informacion de una fila de la sentencia SQL de seleccion a las + variables de salida asociadas a la sentencia. + + \return \em true si el registro ha sido transferido a las variables de salida o \em false si habiamos llegado + al ultimo registro de la seleccion. + */ + virtual bool fetch() throw(RuntimeException, DatabaseException) = 0; + + /** + Devuelve la variable de entrada apuntada por el iterador recibido como parametro. + \return la variable de entrada apuntada por el iterador recibido como parametro. + */ + static Data& input(input_iterator& ii) throw(); + + /** + Devuelve la variable de salida apuntada por el iterador recibido como parametro. + \return la variable de salida apuntada por el iterador recibido como parametro. + */ + static Data& output(output_iterator& ii) throw(); + +protected: + /** + Contructor. + + \param database Base de datos sobre la que se define la sentencia. + \param name Nombre logico de la sentencia. + \param expression Sentencia SQL asociada a esta clase. + \param isCritical Si vale \em true indica que si la ejecucion de esta sentencia falla al desbloquear + la conexion con la que ejecutamos esta sentencia se invocara a Connection::rollback, en otro caso + aunque falle se invocara a Connection::commit. Solo aplicara en sentencias que no sean de seleccion. + */ + Statement(Database& database, const char* name, const char* expression, const bool isCritical) : + a_database(database), + a_name(name), + a_expression(expression), + a_prepared(false), + a_isCritical(isCritical), + a_measureTiming("Timing", "us"), + a_requiresCommit(false) { + } + + /** + Contructor. + + \param database Base de datos sobre la que se define la sentencia. + \param name Nombre logico de la sentencia. + \param expression Sentencia SQL asociada a esta clase. + \param isCritical Si vale \em true indica que si la ejecucion de esta sentencia falla al desbloquear + la conexion con la que ejecutamos esta sentencia se invocara a Connection::rollback, en otro caso + aunque falle se invocara a Connection::commit. Solo aplicara en cuenta en sentencias que no + sean de seleccion. + */ + Statement(Database& database, const char* name, const std::string& expression, const bool isCritical) : + a_database(database), + a_name(name), + a_expression(expression), + a_prepared(false), + a_isCritical(isCritical), + a_measureTiming("Timing", "us"), + a_requiresCommit(false) { + } + + /** + Devuelve la referencia de entrada apuntada por el iterador recibido como parametro. + \return la referencia de entrada apuntada por el iterador recibido como parametro. + */ + static InputBind* inputBind(input_iterator& ii) throw() { return *ii; } + + /** + Devuelve la referencia de salida apuntada por el iterador recibido como parametro. + \return la referencia de salida apuntada por el iterador recibido como parametro. + */ + static OutputBind* outputBind(output_iterator& ii) throw() { return *ii; } + +private: + Database& a_database; + const std::string a_name; + std::string a_expression; + std::vector a_inputBinds; /**< Lista de variables de entrada */ + std::vector a_outputBinds; /**< Lista de variables de salida */ + bool a_prepared; + const bool a_isCritical; + Average a_measureTiming; + bool a_requiresCommit; + + Statement(const Statement&); + void measureTiming(const Microsecond & init, const Microsecond & end) throw() { a_measureTiming += (end - init); } + + virtual void prepare(Connection* connection) throw(RuntimeException, DatabaseException) = 0; + virtual ResultCode execute(Connection* connection) throw(RuntimeException, DatabaseException) = 0; + + friend class Connection; + friend class Database; +}; + +} +} + +#endif diff --git a/include/anna/dbms/StatementTranslator.hpp b/include/anna/dbms/StatementTranslator.hpp new file mode 100644 index 0000000..8610aaa --- /dev/null +++ b/include/anna/dbms/StatementTranslator.hpp @@ -0,0 +1,86 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_StatementTranslator_hpp +#define anna_dbms_StatementTranslator_hpp + +#include + +namespace anna { + +namespace dbms { + +class Database; + +/** + * Clase que ajustar de forma transparente las diferencias de tratamiento que tiene las + * sentencias SQL en los distintos motores de base de datos. De esta forma una aplicación + * originariamente escrita para un determinado RDBMS no tendrá que hacer ningún cambio + * en las sentencias SQL al cambiar a otro RDBMS. + * + * Por ejemplo para indicar los parámetros de entrada en Oracle se indica con un + * literal precedido de ':' o '&'. Con lo que la sentencia podría quedar como: + * \code + * insert into foo (field1, field2) values (:f1, :f2) + * \endcode + * + * En PosgreSQL (tambien sorpotado en Oracle) quedaría algo así: + * \code + * insert into foo (field1, field2) values (&f1, &f2) + * \endcode + * + * Mientras que en MySQL la expresión debería ser como: + * \code + * insert into foo (field1, field2) values (?, ?); + * \endcode + * + * \see anna::dbms::Database::setStatementTranslator + */ +class StatementTranslator { + /** + * Devuelve el nombre lógico de este traductor. + * \return el nombre lógico de este traductor. + */ + const char* getName() const throw() { return a_name; } + +protected: + /** + * Constructor. + * \param name Nombre lógico del traductor. + */ + explicit StatementTranslator(const char* name) : a_name(name) {;} + + /** + * Se invoca automáticamente desde anna::dbms::Database::createStatement si la + * instancia de la base de datos tiene asociada alguna instancia heredada de esta clase. + * + * Éste metodo sólo se invoca una vez para cada una de las sentencias definidas sobre + * la base de datos, por lo que la traducción de sentencias SQL tiene un consumo despreciable + * con respecto al tiempo total del proceso. + * + * \param statement Sentencia SQL original. + * \return La sentencia SQL correspondiente con la original, pero tratada para que + * pueda ser interpretada correctamente por el motor de base de datos sobre el que + * se va a ejecutar. + */ + virtual const char* apply(const char* statement) throw(RuntimeException) = 0; + +private: + const char* a_name; + + StatementTranslator(const StatementTranslator&); + + friend class Database; +}; + +} +} + +#endif + + diff --git a/include/anna/dbms/String.hpp b/include/anna/dbms/String.hpp new file mode 100644 index 0000000..a591eed --- /dev/null +++ b/include/anna/dbms/String.hpp @@ -0,0 +1,114 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_String_hpp +#define anna_dbms_String_hpp + +#include + +#include + +namespace anna { + +namespace dbms { + +/** + Cadena usada como entrada y/o salida de las sentencias SQL. +*/ +class String : public Data { +public: + /** + Constructor. + \param maxSize Tamao maximo que puede tener esta cadena. Deberia coincidir con el indicado + por la columna con la que vaya a corresponder en la sentencia. + \param isNulleable Indica si el dato puede tomar valores nulos. + */ + explicit String(const int maxSize, const bool isNulleable = false) : + Data(Type::String, maxSize, isNulleable) { + Data::setBuffer(a_value = new char [maxSize + 1]); + anna_memset(a_value, 0, maxSize + 1); + } + + /** + Constructor copia. + \param other Instancia de la que copiar. + */ + String(const String& other) : + Data(other), + a_value(other.a_value) { + const int maxSize = getMaxSize(); + Data::setBuffer(a_value = new char [maxSize + 1]); + anna_memset(a_value, 0, maxSize + 1); + } + + /** + Destructor. + */ + virtual ~String() { delete [] a_value; } + + /** + Devuelve el contenido de la cadena. + \return El contenido de la cadena. + \warning Si el metodo Data::isNull devolvio \em true el resultado de este metodo no esta definido. + */ + const char* getValue() const throw() { return a_value; } + + /** + Operador de copia. + \param str Cadena de la que copiar. Si la longitud de la cadena sobrepasa el tamao maximo + indicado en el constructor obtendremos una excepcin. + \return La instancia de esta cadena. + */ + String& operator = (const String& str) throw(RuntimeException); + + /** + Operador de asignacin. + \param str Cadena de la que copiar. Si la longitud de la cadena sobrepasa el tamao maximo + indicado en el constructor obtendremos una excepcin. + \return La instancia de esta cadena. + */ + String& operator = (const char* str) throw(RuntimeException); + + /** + Operador de asignacin. + \param str Cadena de la que copiar. Si la longitud de la cadena sobrepasa el tamao maximo + indicado en el constructor obtendremos una excepcin. + \return La instancia de esta cadena. + */ + String& operator = (const std::string& str) throw(RuntimeException) { return operator= (str.c_str()); } + + /** + Operador de conversion. si el contenido de la columna sociada + fue nulo este metodo devolvera NULL. + \return El contenido de esta cadena. + */ + operator const char*() const throw() { return (Data::isNull() == true) ? NULL : a_value; } + + /** + Elimina los espacios a la derecha de la cadena recibida. + \return La misma cadena recibida pero con los espacios eliminados. + */ + static char* strip(char *str) throw(); + + /** + Devuelve una cadena con la informacion referente a esta instancia. + \return Una cadena con la informacion referente a esta instancia. + */ + virtual std::string asString() const throw(); + +private: + char* a_value; + + void do_clear() throw() { a_value [0] = 0; } +}; + +} +} + +#endif + diff --git a/include/anna/dbms/TimeStamp.hpp b/include/anna/dbms/TimeStamp.hpp new file mode 100644 index 0000000..9f53aea --- /dev/null +++ b/include/anna/dbms/TimeStamp.hpp @@ -0,0 +1,188 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_TimeStamp_hpp +#define anna_dbms_TimeStamp_hpp + +#include + +namespace anna { + +namespace dbms { + +/** + Tipo de datos que permite trabajar con el tipo de dato 'TimeStamp' de un gestor de base de + datos generico. + + El tipo de dato TimeStamp contiene la información suficiente para representar una fecha + incluyendo la hora del día. +*/ +class TimeStamp : public Date { +public: + /** + Constructor. + \param isNulleable Indica si el dato puede tomar valores nulos. + \param format Formato usado para interpretar los datos de esta fecha, en los metodos Date::getCStringValue y + Date::setValue (const char*) y Date::setValue (const std::string&). Sigue la especificacion: + + \code + %a Replaced by the localeâs abbreviated weekday name. [ tm_wday] + + %A Replaced by the localeâs full weekday name. [ tm_wday] + + %b Replaced by the localeâs abbreviated month name. [ tm_mon] + + %B Replaced by the localeâs full month name. [ tm_mon] + + %c Replaced by the locale's appropriate date and time representation. (See the Base Definitions volume of + IEEE Std 1003.1-2001, .) + + %C Replaced by the year divided by 100 and truncated to an integer, as a decimal number [00,99]. [ tm_year] + + %d Replaced by the day of the month as a decimal number [01,31]. [ tm_mday] + + %D Equivalent to %m / %d / %y . [ tm_mon, tm_mday, tm_year] + + %e Replaced by the day of the month as a decimal number [1,31]; a single digit is preceded by a space. [ + tm_mday] + + %F Equivalent to %Y - %m - %d (the ISO 8601:2000 standard date format). [ tm_year, tm_mon, tm_mday] + + %g Replaced by the last 2 digits of the week-based year (see below) as a decimal number [00,99]. [ tm_year, + tm_wday, tm_yday] + + %G Replaced by the week-based year (see below) as a decimal number (for example, 1977). [ tm_year, tm_wday, + tm_yday] + + %h Equivalent to %b . [ tm_mon] + + %H Replaced by the hour (24-hour clock) as a decimal number [00,23]. [ tm_hour] + + %I Replaced by the hour (12-hour clock) as a decimal number [01,12]. [ tm_hour] + + %j Replaced by the day of the year as a decimal number [001,366]. [ tm_yday] + + %m Replaced by the month as a decimal number [01,12]. [ tm_mon] + + %M Replaced by the minute as a decimal number [00,59]. [ tm_min] + + %n Replaced by a . + + %p Replaced by the locale's equivalent of either a.m. or p.m. [ tm_hour] + + %r Replaced by the time in a.m. and p.m. notation; in the POSIX locale this shall be equivalent to %I : %M + : %S %p . [ tm_hour, tm_min, tm_sec] + + %R Replaced by the time in 24-hour notation ( %H : %M ). [ tm_hour, tm_min] + + %S Replaced by the second as a decimal number [00,60]. [ tm_sec] + + %t Replaced by a . + + %T Replaced by the time ( %H : %M : %S ). [ tm_hour, tm_min, tm_sec] + + %u Replaced by the weekday as a decimal number [1,7], with 1 representing Monday. [ tm_wday] + + %U Replaced by the week number of the year as a decimal number [00,53]. The first Sunday of January is the + first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + + %V Replaced by the week number of the year (Monday as the first day of the week) as a decimal number [01,53]. + If the week containing 1 January has four or more days in the new year, then it is considered week 1. Oth- + erwise, it is the last week of the previous year, and the next week is week 1. Both January 4th and the + first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] + + %w Replaced by the weekday as a decimal number [0,6], with 0 representing Sunday. [ tm_wday] + + %W Replaced by the week number of the year as a decimal number [00,53]. The first Monday of January is the + first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + + %x Replaced by the locale's appropriate date representation. (See the Base Definitions volume of + IEEE Std 1003.1-2001, .) + + %X Replaced by the locale's appropriate time representation. (See the Base Definitions volume of + IEEE Std 1003.1-2001, .) + + %y Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] + + %Y Replaced by the year as a decimal number (for example, 1997). [ tm_year] + + %z Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), or by no charac- + ters if no timezone is determinable. For example, "-0430" means 4 hours 30 minutes behind UTC (west of + Greenwich). If tm_isdst is zero, the standard time offset is used. If tm_isdst is greater than zero, the + daylight savings time offset is used. If tm_isdst is negative, no characters are returned. [ tm_isdst] + + %Z Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ + tm_isdst] + + %% Replaced by % . + \endcode + + Para obtener más informacion sobre la espeficacion de formato \em man \em strftime (p.e.). + + Para poder obtener la parte fraccionaria en la salida del metodo #getCStringValue hay que indicar el literal \em %%d. Por ejemplo: + + \code + TimeStamp oneTime (false, "%T.%%d") + \endcode + */ + explicit TimeStamp(const bool isNulleable = false, const char* format = NULL) : + Date(Data::Type::TimeStamp, isNulleable, format), + a_fractionalSecond(0) + {;} + + /** + * Devuelve la parte fraccionaria de los segundos asociados a este objeto. + * \return La parte fraccionaria de los segundos asociados a este objeto. + */ + int getFractionalSecond() const throw() { return a_fractionalSecond; } + + /** + * Establece la parte fraccionaria de los segundos de este objeto. + * \param fsec Parte fraccionaria de los segundos. + */ + void setFractionalSecond(const int fsec) throw() { a_fractionalSecond = fsec; } + + /** + * Interpreta el contenido de la fecha y lo transfiere al buffer. + * \return El buffer que contiene esta fecha interpretada con el formato indicado en el contructor. + * \warning El resultado sera NULL en caso de no poder interpretar correctamente la fecha. + */ + virtual const char* getCStringValue() const throw(); + + /** + Operador de copia. + \param timeStamp Fecha de la que copiar. + \return La instancia de esta fecha. + \warning Solo copia el contenido de la fecha recibida, no cambia el formato de interpretacion de la fecha origen. + */ + TimeStamp& operator = (const TimeStamp& timeStamp) throw(RuntimeException) { + Date::operator= (timeStamp); + a_fractionalSecond = timeStamp.a_fractionalSecond; + return *this; + } + + /** + Operador de copia. + \param date Fecha de la que copiar. + \return La instancia de esta fecha. + \warning Solo copia el contenido de la fecha recibida, no cambia el formato de interpretacion de la fecha origen. + */ + TimeStamp& operator = (const Date& date) throw(RuntimeException) { Date::operator= (date); a_fractionalSecond = 0; return *this; } + +private: + char a_anotherBuffer [MaxDateSize + 1]; + int a_fractionalSecond; + + void do_clear() throw() { a_fractionalSecond = 0; a_anotherBuffer [0] = 0; } +}; + +} +} + +#endif + diff --git a/include/anna/dbms/dbms.hpp b/include/anna/dbms/dbms.hpp new file mode 100644 index 0000000..cccdad3 --- /dev/null +++ b/include/anna/dbms/dbms.hpp @@ -0,0 +1,51 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_dbms_hpp +#define anna_dbms_dbms_hpp + +namespace anna { +/** +Define de forma generica el protocolo de acceso a los datos guardados en un DBMS. + +El ejecutable debera enlazarse con las librerias: + \li anna.core.a + \li anna.xml.a + \li anna.app.a + \li anna.comm.a + \li anna.dbms.a + +El Packet Header es anna.dbms.h +*/ +namespace dbms { +} +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace anna::dbms; + +#endif + diff --git a/include/anna/dbms/functions.hpp b/include/anna/dbms/functions.hpp new file mode 100644 index 0000000..e782d34 --- /dev/null +++ b/include/anna/dbms/functions.hpp @@ -0,0 +1,96 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_functions_hpp +#define anna_dbms_functions_hpp + +#include + +namespace anna { +namespace dbms { +class Connection; +} +} + +namespace anna { +namespace dbms { + +using namespace anna; + + +/** + Métodos usados habitualmente para trabajar contra la base de datos. +*/ +struct functions { +public: + /** + * Este metodo asegura la integridad entre el esquema de tablas instalado y el esperado por los procesos de un proyecto. + * Para ello cada uno de los proyectos debe invocar a este método indicado los parámetro requeridos. Es imprescindible + * que la versión esperada del esquema de base de datos esté definida en una única variable centralizada. + * + * La idea es que cada vez que hagamos un cambio en la estructura de tablas de una cierta entidad cambiemos la versión + * del esquema, y ese cambio deberá reflejarse en la invocación a este nuevo método. + * + * Este método lanzará una excepción si el último parche instalado en el esquema de base de datos no coincide + * con el valor de la variable \em requiredPatch. + * + * Cada proceso debe invocar la comprobación de esquema en cuanto la base de datos tenga una conexión disponible, de forma + * que si el esquema de base de datos no coincide con el esperado el proceso mostrará un error indicado el esquema de base + * de datos esperado y el esquema de base datos instalado. + * + * Para usar este método el esquema de base de nuestro proyecto debe disponer de una tabla que debe contener, al menos, + * la siguiente estructura: + * + * \code + * -- Ejemplo de creación para Oracle(tm) + * create table axe_dataScheme ( + * id varchar(8) not null, + * installation_date date default sysdate not null + * ); + * + * alter table axe_dataScheme add constraint axe_dataScheme_pk primary key (id); + * + * \endcode + * + * \code + * -- Ejemplo de creación para PostgreSQL + * create table data_scheme ( + * id varchar(8) not null, + * installation_date timestamp default now (), + * primary key (id) + * ); + * \endcode + * + * El nombre de la tabla y los campos de las columnas podrían ser distintos ya que este método los recibe como parámetros. + * + * Un ejemplo de implementación podría ser: + * + * \code + * //static + * void axe::storage::functions::verifyDataScheme (dbms::Connection& connection) + * throw (RuntimeException) + * { + * anna::dbms::functions::verifyDataScheme (connection, "axe_dataScheme", "v1.3"); + * } + * \endcode + * + * \param connection Conexión usada para acceder a los datos. + * \param tableName Nombre de la tabla que contiene las versiones instaladas. + * \param requiredPatch Identificador de parche de base de base de datos requerido por los procesos de nuestro proyecto. + * Este indicador debería estar definido en un único punto para todo nuestro proyecto. + * \param columnID Nombre de la columna que contiene el identificador de cada uno de los parches instalados. + * \param columnDate Nombre de la columna que contiene la fecha de instalación de cada parche. + */ + static void verifyDataScheme(dbms::Connection& connection, const char* tableName, const char* requiredPatch, const char* columnID = "id", const char* columnDate = "installation_date") + throw(RuntimeException); +}; + +} +} + +#endif diff --git a/include/anna/dbms/internal/sccs.hpp b/include/anna/dbms/internal/sccs.hpp new file mode 100644 index 0000000..d518c6a --- /dev/null +++ b/include/anna/dbms/internal/sccs.hpp @@ -0,0 +1,25 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbms_internal_sccs_hpp +#define anna_dbms_internal_sccs_hpp + +namespace anna { + +namespace dbms { + +class sccs { +public: + static void activate() throw(); +}; + +} +} + +#endif + diff --git a/include/anna/dbos/Accesor.hpp b/include/anna/dbos/Accesor.hpp new file mode 100644 index 0000000..a719c51 --- /dev/null +++ b/include/anna/dbos/Accesor.hpp @@ -0,0 +1,175 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Accesor_hpp +#define anna_dbos_Accesor_hpp + +#include +#include + +#include + +#include + +namespace anna { + +namespace dbms { +class Database; +class Connection; +class Statement; +} + +namespace dbos { + +class StorageArea; + +/** + Interfaz que deben cumplir los objetos encargados de acceder al objeto del medio fisico, + que normalmente sera alguna base de datos. +*/ +class Accesor : public Mutex { +public: + typedef short Id; /**< Permite identificar el tipo de accesor. */ + + /** + Destructor. + */ + virtual ~Accesor(); + + /** + Devuelve el identificador de este accesor. + \return El identificador de este accesor. + */ + Id getId() const throw() { return a_id; } + + /** + Devuelve la instancia de la sentencia \em statement asociada a este cargador. + \return La instancia de la sentencia \em statement asociada a este cargador. Puede ser NULL. + */ + dbms::Statement* getStatement() + throw(RuntimeException) { + return (a_statement == NULL && a_database != NULL) ? (a_statement = initialize(*a_database)) : a_statement; + } + + /** + * Devuelve \em true si el accesor fue inicializado con base de datos o \em false en otro caso. + * \return \em true si el accesor fue inicializado con base de datos o \em false en otro caso. + */ + bool hasDataBase() const throw() { return a_database != NULL; } + + /** + Devuelve la instancia de la base de datos asociada a este cargador. + \return La instancia de la base de datos asociada a este cargador. + + \warning Si el accesor fue inicializado sin base de datos lo resultados no están definidos. + */ + dbms::Database& getDatabase() throw() { return *a_database; } + + /** + Devuelve la conexion que esta usando actualmente este cargador. + \return la conexion que esta usando actualmente este cargador. + */ + dbms::Connection& getConnection() throw(RuntimeException) { + if(a_connection == NULL) { + std::string msg(asString()); + msg += " | No available database connection"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return *a_connection; + } + + /** + Devuelve la representacion en forma de cadena de la clave primaria establecida. + @return La representacion en forma de cadena de la clave primaria establecida. + */ + virtual std::string asString() const throw() = 0; + + /** + Metodo de debemos re-escribir para devolver el nombre completo del selector de recursos. + Para evitar ambigüedades este nombre deberia incluir la lista completa de \em namespaces + a la que pertenece la clase. + \return Una cadena con el nombre de este selector. + */ + virtual const char* getClassName() const throw() = 0; + +protected: + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + */ + Accesor(dbms::Database& database, const Id id) : + a_database(&database), + a_id(id), + a_statement(NULL), + a_connection(NULL), + a_emodeIsNull(true) + {;} + + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + \param emode Modo de actuar en caso de no encontrar el dato buscado. + */ + Accesor(dbms::Database& database, const Id id, const Exception::Mode::_v emode) : + a_database(&database), + a_id(id), + a_statement(NULL), + a_connection(NULL), + a_emodeIsNull(false), + a_exceptionMode(emode) + {;} + + /** + Constructor. + \param id Identificador de este accesor. + */ + Accesor(const Id id) : + a_database(NULL), + a_id(id), + a_statement(NULL), + a_connection(NULL), + a_emodeIsNull(true) + {;} + + /** + Metodo que deben implementar todos los accesores para definir la sentencia + SQL que los definira. + Se invocara automaticamente desde el nucleo de anna.dbos la primera vez que + se use este accesor, de forma que el programador solo debe preocuparse por + definir este metodo. + \param database Instancia de la base de datos indicada en el constructor. + */ + virtual dbms::Statement* initialize(dbms::Database& database) throw(RuntimeException) = 0; + +private: + dbms::Database* a_database; + const Id a_id; + dbms::Statement* a_statement; + dbms::Connection* a_connection; + bool a_emodeIsNull; + Exception::Mode::_v a_exceptionMode; + + void setStatement(dbms::Statement* statement) throw() { a_statement = statement; } + + virtual bool load(dbms::Connection*, const StorageArea*) throw(RuntimeException, dbms::DatabaseException); + + friend class StorageArea; +}; + +} +} + +#endif + + + diff --git a/include/anna/dbos/AutoObject.hpp b/include/anna/dbos/AutoObject.hpp new file mode 100644 index 0000000..6c3992e --- /dev/null +++ b/include/anna/dbos/AutoObject.hpp @@ -0,0 +1,152 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_AutoObject_hpp +#define anna_dbos_AutoObject_hpp + +#include + +namespace anna { + +namespace dbos { + + +/** + Facilita el uso de los punteros a objectos obtenidos a partir de los datos guardados en un medio fisico. + + La idea de esta clase es que el constructor y el destructor de esta clase cooperan para reservar y/o + liberar correctamente la instancia de T asociada a esta instancia. + + \param T Clase que vamos a gestionar. + + En el siguiente ejemplo podemos ver la forma habitual de trabajar con un objeto persistente tiene + el incoveniente de que tenemos que tener en cuenta cada una de las situaciones en las que la + referencia obtenida mediante el metodo \em instantiate debe ser liberada. + + \code + void Application::getServerSocketsData (vector & serverSocketsData) const + throw (RuntimeException) + { + LOGMETHOD (TraceMethod ("anna::Application", "getServerSocketsData", ANNA_FILE_LOCATION)); + + Facility* facility (NULL); + FacilityLoader facilityLoader; + + try { + facility = Facility::instantiate (facilityLoader.setKey (a_thisFacility)); // (1) + LOGDEBUG (Logger::write (Logger::Debug, facility->asString (), ANNA_FILE_LOCATION)); + + getSocketsData (getThisHostName (), facility->getName (), a_thisCell, a_thisInstance, serverSocketsData); + + Facility::release (facility); + } + catch (dbos::DatabaseException& edbos) { + Facility::release (facility); + throw RuntimeException (edbos.getText (), ANNA_FILE_LOCATION); + } + catch (RuntimeException&) { // Tenemos que capturar esta excepcion para liberar el recurso. + Facility::release (facility); + throw; + } + } + \endcode + + Como podemos ver a continuacion el siguiente metodo es mucho mas sencillo y aporta la gran ventaja de que + el sistema trabaja por nosotros para liberar correctamente los recursos. + + \code + void Application::getServerSocketsData (vector & serverSocketsData) const + throw (RuntimeException) + { + LOGMETHOD (TraceMethod ("anna::Application", "getServerSocketsData", ANNA_FILE_LOCATION)); + + AutoObject facility; + FacilityLoader facilityLoader; + + try { + facility = Facility::instantiate (facilityLoader.setKey (a_thisFacility)); // (1) + + LOGDEBUG (Logger::write (Logger::Debug, facility->asString (), ANNA_FILE_LOCATION)); + + getSocketsData (getThisHostName (), facility->getName (), a_thisCell, a_thisInstance, serverSocketsData); + } + catch (dbos::DatabaseException& edbos) { + throw RuntimeException (edbos.getText (), ANNA_FILE_LOCATION); + } + } + \endcode +*/ +template class AutoObject { +public: + /** + Constructor. + \param t Instancia del objeto asociado a esta instancia. + \warning La instancia deberia haber sido obtenida mediate la invocacion a \em T::instantiate de la + clase persistente. + */ + explicit AutoObject(T* t) : a_t(t) {;} + + /** + Constructor. + */ + AutoObject() : a_t(NULL) {;} + + /** + Destructor. Invoca al metodo \em T::release + */ + ~AutoObject() { if(a_t != NULL) T::release(a_t); } + + /** + Operador -> + Permite invocar a metodos de la clase T. + \return La instancia de la clase T asociada a esta instancia. + */ + T* operator -> () const throw() { return a_t; } + + /** + Operador copia. + \param t Referencia al objeto que vamos a tratar. + \return La instancia de la clase T asociada a esta instancia. + */ + T* operator = (T* t) throw() { + if(a_t != t) { + T::release(a_t); + a_t = t; + } + + return a_t; + } + + /** + Operador copia. + \param other Referencia al objeto que vamos a tratar. + \return La instancia de la clase T asociada a esta instancia. + */ + T* operator = (const AutoObject & other) throw(RuntimeException) { + return (this != &other) ? (*this = T::duplicate(other.a_t)) : a_t; + } + + /** + Operador de conversion. + \return La instancia de la clase T asociada a esta instancia. + */ + operator T*() const throw() { return a_t; } + +private: + T* a_t; +}; + + +} +} + + + +#endif + + diff --git a/include/anna/dbos/AutoSet.hpp b/include/anna/dbos/AutoSet.hpp new file mode 100644 index 0000000..5882bb1 --- /dev/null +++ b/include/anna/dbos/AutoSet.hpp @@ -0,0 +1,158 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_AutoSet_hpp +#define anna_dbos_AutoSet_hpp + +#include + +namespace anna { + +namespace dbos { + + +/** + Facilita el uso de los punteros a objectos obtenidos a partir de los datos guardados en un medio fisico. + + La idea de esta clase es que el constructor y el destructor de esta clase cooperan para reservar y/o + liberar correctamente la instancia de T asociada a esta instancia. + + \param T Clase que vamos a gestionar. + + En el siguiente ejemplo podemos ver la forma habitual de trabajar con un objeto persistente tiene + el incoveniente de que tenemos que tener en cuenta cada una de las situaciones en las que la + referencia obtenida mediante el metodo \em instantiate debe ser liberada. + + \code + Server::Set * servers (NULL); + ServerLoader serverLoader; // ServerLoader hereda de anna::dbos::Loader. + Server* server; + + try { + serverLoader.setKey (..........); // Establece los parametros de busqueda + + servers = Server::instantiate (serverLoader); + + if (servers->size () == 0) { + .... + .... Si fuera necesario trataria la condicion de no encontrar ningun registro + .... + } + + Server::iterator ii, maxii; + + for (ii = servers->begin (), maxii = servers->end (); ii != maxii; ii ++) { + server = *ii; + + .... Trataria cada uno de los Server encontrados .... + } + + Server::release (servers); + } + catch (Exception& ex) { + Server::release (servers); + + ... + ... Si fuera necesario trataria la condificion de error. + } + \endcode + + Como podemos ver a continuacion el siguiente metodo es mucho mas sencillo y aporta la gran ventaja de que + el sistema trabaja por nosotros para liberar correctamente los recursos. + + \code + AutoSet servers; + ServerLoader serverLoader; // ServerLoader hereda de anna::dbos::Loader. + Server* server; + + try { + serverLoader.setKey (..........); // Establece los parametros de busqueda + + servers = Server::instantiate (serverLoader); + + if (servers->size () == 0) { + .... + .... Si fuera necesario trataria la condicion de no encontrar ningun registro + .... + } + + Server::iterator ii, maxii; + + for (ii = servers->begin (), maxii = servers->end (); ii != maxii; ii ++) { + server = *ii; + + .... Trataria cada uno de los Server encontrados .... + } + } + catch (Exception& ex) { + ... + ... Si fuera necesario trataria la condificion de error. + } + \endcode +*/ +template class AutoSet { +public: + /** + Constructor. + \param t Instancia del objeto asociado a esta instancia. + \warning La instancia deberia haber sido obtenida mediate la invocacion a \em T::instantiate de la + clase persistente. + */ + explicit AutoSet(Set * t) : a_t(t) {;} + + /** + Constructor. + */ + AutoSet() : a_t(NULL) {;} + + /** + Destructor. Invoca al metodo \em T::release + */ + ~AutoSet() { if(a_t != NULL) T::release(a_t); } + + /** + Operador -> + Permite invocar a metodos de la clase T. + \return La instancia de la clase T asociada a esta instancia. + */ + Set * operator -> () const throw() { return a_t; } + + /** + Operador copia. + \return La instancia de la clase T asociada a esta instancia. + */ + Set * operator = (Set* t) + throw() { + if(a_t == t) + return t; + + if(a_t != NULL) + T::release(a_t); + + return a_t = t; + } + + /** + Operador de conversion. + \return La instancia de la clase T asociada a esta instancia. + */ + operator Set *() const throw() { return a_t; } + +private: + Set * a_t; +}; + + +} +} + + + +#endif + + diff --git a/include/anna/dbos/Creator.hpp b/include/anna/dbos/Creator.hpp new file mode 100644 index 0000000..2f806cd --- /dev/null +++ b/include/anna/dbos/Creator.hpp @@ -0,0 +1,65 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Creator_hpp +#define anna_dbos_Creator_hpp + +#include + +namespace anna { + +namespace dbos { + +/** + Interfaz que deben cumplir los objetos encargados de crear un nuevo objeto que sera ubicado + en el area de almacenamiento asociado a un medio fisico. +*/ +class Creator : public Accesor { +public: + /** + Metodo de debemos re-escribir para devolver el nombre completo del selector de recursos. + Para evitar ambigüedades este nombre deberia incluir la lista completa de \em namespaces + a la que pertenece la clase. + \return Una cadena con el nombre de este selector. + */ + virtual const char* getClassName() const throw() { return "anna::dbos::Creator"; } + +protected: + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + */ + Creator(dbms::Database& database, const Id id = 0) : Accesor(database, id) {;} + + /** + Constructor. + \param id Identificador de este accesor. + */ + Creator(const Id id = 0) : Accesor(id) {;} + + /** + Devuelve el indice usado para ubicar en memoria el objeto que vamos a cargar. + @return El indice usado para ubicar en memoria el objeto que vamos a cargar. + */ + virtual Index getIndex() const throw(RuntimeException) = 0; + +private: + dbms::Statement* initialize(dbms::Database&) throw(RuntimeException) { return NULL;} + + friend class StorageArea; +}; + +} +} + +#endif + + + diff --git a/include/anna/dbos/CrossedLoader.hpp b/include/anna/dbos/CrossedLoader.hpp new file mode 100644 index 0000000..c1516ec --- /dev/null +++ b/include/anna/dbos/CrossedLoader.hpp @@ -0,0 +1,97 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_CrossedLoader_hpp +#define anna_dbos_CrossedLoader_hpp + +#include + +namespace anna { + +namespace dbms { +class Database; +} + +namespace dbos { + +class Loader; + +/** + * Interfaz que deben cumplir los cargadores cruzados. Un cargador cruzado facilita que una + * misma clase pueda ser cargada usando varios criterios de búsqueda. + * + * El criterio de búsqueda más usado y que debería ser más óptimo será desarrollado + * mediante la definición del anna::dbos::Loader específico. El resto de criterios de búsqueda, + * los cargadores cruzados, deberían ser capaces obtener los datos usados como clave en el criterio + * principal, para a partir de ahí poder aplicar el criterio de búsqueda principal. + * + * Cada cargador cruzado podría tener una lista de pares (clave_alternativa, clave_principal) que permitirá + * acelerar las búsquedas de la clave principal, en base a la clave alternativa usada en este cargador. + * + * Para obtener los datos de la clave principal en base a los datos de la clave alternativa habrá que + * acceder al medio físico. + * + * Para optimizar el acceso a los pares (Clave alternativa, Clave Principal) se podría usar una + * instancia del tipo anna::LRUMap. +*/ +class CrossedLoader : public Accesor { +public: + /** + Metodo de debemos re-escribir para devolver el nombre completo del selector de recursos. + Para evitar ambigüedades este nombre deberia incluir la lista completa de \em namespaces + a la que pertenece la clase. + \return Una cadena con el nombre de este selector. + */ + virtual const char* getClassName() const throw() { return "anna::dbos::CrossedLoader"; } + +protected: + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + */ + CrossedLoader(dbms::Database& database, const Id id = 0) : Accesor(database, id) {;} + + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + \param emode Modo de actuar en caso de no encontrar el dato buscado. + */ + CrossedLoader(dbms::Database& database, const Id id, const Exception::Mode::_v emode) : Accesor(database, id, emode) {;} + + /** + Este método debe ser reescrito para que permita localizar la información del objeto, que posiblemente + esté ubicado en la lista de objetos de este cargador cruzado. + + @return \em true si se ha localizado la información la clave primaria del objeto en su lista de pares + o \em false en otro caso. + */ + virtual bool seek() const throw() { return false; } + + /** + * Este método debe ser reescrito para que se pueda actualizar la lista de pares (clave_alternativa, clave_principal) + * que permitirá acelear las posteriores búsquedas. + * + * \param loader Instancia del cargador principal que habrá compuesto la clave principal con los suministrados por + * esta instancia. + */ + virtual void download(Loader& loader) throw() {;} + + friend class StorageArea; +}; + +} +} + +#endif + + + diff --git a/include/anna/dbos/Eraser.hpp b/include/anna/dbos/Eraser.hpp new file mode 100644 index 0000000..3994714 --- /dev/null +++ b/include/anna/dbos/Eraser.hpp @@ -0,0 +1,70 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Eraser_hpp +#define anna_dbos_Eraser_hpp + +#include + +namespace anna { + +namespace dbos { + +class Object; + +/** + Interfaz que deben cumplir los objetos encargados de borrar el objeto del medio fisico, + que normalmente sera alguna base de datos. +*/ +class Eraser : public Accesor { +public: + /** + Devuelve la instancia establecida por #setObject. + \return La instancia establecida por #setObject. Puede ser NULL. + */ + Object* getObject() throw() { return a_object; } + + /** + Establece la instancia del objeto sobre el que vamos a actuar. + \param object Instancia del objeto sobre el que vamos a actuar. + */ + void setObject(Object* object) throw() { a_object = object; } + + /** + Metodo de debemos re-escribir para devolver el nombre completo del selector de recursos. + Para evitar ambigüedades este nombre deberia incluir la lista completa de \em namespaces + a la que pertenece la clase. + \return Una cadena con el nombre de este selector. + */ + virtual const char* getClassName() const throw() { return "anna::dbos::Eraser"; } + +protected: + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + */ + Eraser(dbms::Database& database, const Id id = 0) : + Accesor(database, id), + a_object(NULL) + {;} + +private: + Object* a_object; + + Index getIndex() const throw() { return 0; } // No se usa +}; + +} +} + +#endif + + + diff --git a/include/anna/dbos/Loader.hpp b/include/anna/dbos/Loader.hpp new file mode 100644 index 0000000..ec71709 --- /dev/null +++ b/include/anna/dbos/Loader.hpp @@ -0,0 +1,77 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Loader_hpp +#define anna_dbos_Loader_hpp + +#include + +namespace anna { + +namespace dbms { +class Database; +} + +namespace dbos { + +class CrossedLoader; + +/** + Interfaz que deben cumplir los objetos encargados de cargar el objeto desde el medio fisico, + que normalmente sera alguna base de datos, y pasarlo un ambito de objetos en C++. +*/ +class Loader : public Accesor { +public: + /** + Metodo de debemos re-escribir para devolver el nombre completo del selector de recursos. + Para evitar ambigüedades este nombre deberia incluir la lista completa de \em namespaces + a la que pertenece la clase. + \return Una cadena con el nombre de este selector. + */ + virtual const char* getClassName() const throw() { return "anna::dbos::Loader"; } + +protected: + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + */ + Loader(dbms::Database& database, const Id id = 0) : Accesor(database, id) {;} + + /** + Constructor. + \param id Identificador de este accesor. + */ + Loader(const Id id = 0) : Accesor(id) {;} + + /** + Devuelve el indice usado para ubicar en memoria el objeto que vamos a cargar. + @return El indice usado para ubicar en memoria el objeto que vamos a cargar. + */ + virtual Index getIndex() const throw(RuntimeException) = 0; + + /** + * Este método debe ser reescrito para poder obtener los datos de la clave principal usada para localizar + * los objetos en un área del almacenamiento. + * + * \param crossedLoader Instancia del cargador alternativo que habrá calculado la clave principal en + * a lo clave alternativa contenida en él. + */ + virtual void upload(CrossedLoader& crossedLoader) throw(RuntimeException) {;} + + friend class StorageArea; +}; + +} +} + +#endif + + + diff --git a/include/anna/dbos/Object.hpp b/include/anna/dbos/Object.hpp new file mode 100644 index 0000000..fc4e38c --- /dev/null +++ b/include/anna/dbos/Object.hpp @@ -0,0 +1,112 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Object_hpp +#define anna_dbos_Object_hpp + +#include + +#include + +#include + +namespace anna { + +namespace dbos { + +class Creator; + +/** + Interfaz que deben cumplir los objetos persistentes. + + Ejemplo de definicion de una clase usando esta interfaz: + + \include dbos_demo.p/oodb.d/hdrs/dbos_demo.oodb.Table01.h + + Ejemplo de implementacion de la clase correspondiente a la definicion: + + \include dbos_demo.p/oodb.d/oodb.Table01.cc +*/ +class Object { +public: + /** + Devuelve el indice asociado a este objeto + \return el indice asociado a este objeto + */ + Index getIndex() const throw() { return a_index; } + + /** + * Devuelve \em true si este objeto ya existe en el medio físico (fué cargado desde allí o fue creado y grabado posteriormente) o + * \em false si este objeto sólo existe en la memoria intermedia. + * \return \em true si este objeto ya existe en el medio físico (fué cargado desde allí o fue creado y grabado posteriormente) o + * \em false si este objeto sólo existe en la memoria intermedia. + */ + bool isStored() const throw() { return a_isStored; } + +protected: + /** + Constructor. + */ + Object() : a_index(0), a_isStored(false) {;} + + /** + Inicializa este objeto con la informacion obtenida desde el medio fisico donde + esta grabado el objeto. Normalmente este medio fisico correspondera con una base de datos. + + \param loader Cargador que contiene la informacion con la que debemos inicializar este objeto. + + */ + virtual void initialize(Loader& loader) throw(RuntimeException, dbms::DatabaseException) = 0; + + /** + Actualiza la informacion de este objeto con la nueva informacion obtenida del medio + fisico. + \param creator Creador que contiene la informacion con la que debemos inicializar este objeto. + */ + virtual void create(Creator& creator) throw(RuntimeException, dbms::DatabaseException) {;} + + /** + Libera los recursos reservados por este objeto. Este metodo solo se invocara cuando el objeto + vaya a ser sacado definitivamente del area de almacenamiento. + */ + virtual void destroy() throw() {;} + + /** + Devuelve \em true si el registro del medio fisico ha cambiado respecto al registro + cargado en memoria o \em false en otro caso. + + \param loader Cargador que contiene la informacion con la que deberiamos re-inicializar + este objeto. + + \return \em true si el registro del medio fisico ha cambiado respecto al registro + cargado en memoria o \em false en otro caso. + */ + virtual bool hasChanges(Loader& loader) throw(RuntimeException, dbms::DatabaseException) { + return true; + } + + /** + Devuelve \em true si el objeto requiere comenzar el proceso de comprobacion de recarga + de datos \em false en otro caso. + */ + virtual bool enableUpdate() const throw() { return true; } + +private: + Index a_index; + bool a_isStored; + + void setIndex(const Index index) throw() { a_index = index; } + + friend class StorageArea; +}; + + +} +} + +#endif diff --git a/include/anna/dbos/ObjectAllocator.hpp b/include/anna/dbos/ObjectAllocator.hpp new file mode 100644 index 0000000..df9f190 --- /dev/null +++ b/include/anna/dbos/ObjectAllocator.hpp @@ -0,0 +1,25 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_ObjectAllocator_hpp +#define anna_dbos_ObjectAllocator_hpp + +namespace anna { + +namespace dbos { + +class Object; + +typedef Object*(*ObjectAllocator)(); + +} + +} + +#endif + diff --git a/include/anna/dbos/ObjectFacade.hpp b/include/anna/dbos/ObjectFacade.hpp new file mode 100644 index 0000000..18577f0 --- /dev/null +++ b/include/anna/dbos/ObjectFacade.hpp @@ -0,0 +1,406 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_ObjectFacade_hpp +#define anna_dbos_ObjectFacade_hpp + +#include + +#include + +#include +#include +#include +#include +#include + +namespace anna { + +namespace dbms { +class Connection; +} + +namespace dbos { + +class Object; + +/** + Clase que facilita el acceso y uso de las clases encargadas de la instanciacion de objetos a partir de los datos + contenidos en un medio fisico, que normalmente seria la tabla de una base de datos. + + \param T clase debe ser heredada de anna::dbos::Object. + + Ejemplo de definicion de una clase usando esta interfaz: + + \include dbos_demo.p/oodb.d/hdrs/dbos_demo.oodb.Table01.h + + Ejemplo de implementacion de la clase correspondiente a la definicion: + + \include dbos_demo.p/oodb.d/oodb.Table01.cc + + \see dbos_declare_object + \see dbos_prepare_object +*/ +template class ObjectFacade { +public: + /** + Devuelve un numerico que puede ser usado en la definicion del area de almacenamiento. + + \return Un numerico que puede ser usado en la definicion del area de almacenamiento. + \see Database::createStorageArea + */ + static StorageId getStorageAreaId() throw() { return (StorageId) anna_ptrnumber_cast(&st_storageArea); } + + /** + Devuelve el area de almacenamiento asociado a esta clase. + \return Devuelve el area de almacenamiento asociado a esta clase. + */ + static StorageArea* getStorageArea() throw() { return st_storageArea; } + + /** + Establece el area de almacenamiento asociado a esta clase, que deberiaser creado mediante la invocacin al metodo + Database::createStorageArea. + + \param storageArea area de almacenamiento asociada esta clase. + + \warning El area de almacenamiento debe establecerse antes de invocar a cualquier otro metodo de esta clase. + */ + static void setStorageArea(StorageArea* storageArea) throw() { + (st_storageArea = storageArea)->setSizeof(sizeof(T)); + } + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar el area de almacenamiento asociado a esta clase se halla indicado un \em errorCode + igual a -1 en otro caso si no encuentra el objeto que cumpla el patron devolveria una + excepcion. + + \warning Cada llamada a este metodo deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + static T* instance(dbms::Connection& connection, Loader& loader) + throw(RuntimeException, dbms::DatabaseException) { + if(st_storageArea == NULL) { + std::string msg(loader.asString()); + msg += " | ObjectFacade uninitialized "; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return static_cast (st_storageArea->instance(connection, loader)); + } + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. + + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar el area de almacenamiento asociado a esta clase se halla indicado un \em errorCode + igual a -1 en otro caso si no encuentra el objeto que cumpla el patron devolveria una + excepcion. + + \warning Cada llamada a este metodo deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + static T* instance(Loader& loader) + throw(RuntimeException, dbms::DatabaseException) { + if(st_storageArea == NULL) { + std::string msg(loader.asString()); + msg += " | ObjectFacade uninitialized "; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return static_cast (st_storageArea->instance(loader)); + } + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param crossedLoader Cargador encargado de encontrar la clave principal a aplicar con el #Loader + recibido como parámetro en base a una clave alternativa contenida en el mismo. + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar esta clase \em errorCode se halla indicado un #NoExceptionWhenNotFound en otro + caso si no encuentra el objeto que cumpla el patron devolveria una excepcion de ejecucion. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + static T* instance(dbms::Connection& connection, CrossedLoader& crossedLoader, Loader& loader) + throw(RuntimeException, dbms::DatabaseException) { + if(st_storageArea == NULL) { + std::string msg(loader.asString()); + msg += " | ObjectFacade uninitialized "; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return static_cast (st_storageArea->instance(connection, crossedLoader, loader)); + } + + /** + Crea un objeto en el area de almacenamiento un y lo prepara para ser transferido al medio fisico + si fuera necesario. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario acceder al medio fisico. + \param creator Creador encargado de generar el objeto de forma que sea facil de localizar posteriormente + en el area de almacenamiento. + + \return La nueva instancia que cumple el patron establecido por el creador. + + \warning Cada llamada a este metodo deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + static T* create(dbms::Connection& connection, Creator& creator) + throw(RuntimeException, dbms::DatabaseException) { + if(st_storageArea == NULL) { + std::string msg(creator.asString()); + msg += " | ObjectFacade uninitialized "; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return static_cast (st_storageArea->create(connection, creator)); + } + + /** + Devuelve la informacion de un objeto cargado desde el medio fisico. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe buscar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param loader Cargador de clase encargado de localizar la informacion referente al objeto buscado. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL si el + objeto no fue cargado en el area de almacenamiento. + + \warning Cada llamada a este metodo deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + static T* find(Loader& loader) + throw(RuntimeException) { + if(st_storageArea == NULL) { + std::string msg(loader.asString()); + msg += " | ObjectFacade uninitialized "; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return static_cast (st_storageArea->find(loader)); + } + + /** + Habilita la reutilizacion del espacio de memoria ocupado por un objeto alojado en el area de almacenamiento. + + Este metodo no saca al objeto de la memoria de almacenamiento, sino que marca su espacio de memoria + como subceptible de ser reutilizado. De esta forma, si el numero de objetos cargados en memoria se + acerca al tamao maximo indicado en la inicializacion, se intentara reusar espacios libres en vez + de continuar ampliando la memoria reservada. + + \param t Instancia del tipo T que vamos a liberar. + + \warning Si el objeto recibido como parametro no fue reservado mediate alguno de los metodos de reserva de + objetos ofrecidos por esta clase no tendra ningun efecto. + */ + static void release(T*& t) + throw() { + if(st_storageArea == NULL) + return; + + try { + st_storageArea->release(reinterpret_cast (&t)); + } catch(RuntimeException& ex) { + ex.trace(); + } + } + + /** + Descarga todos los objetos contenidos en el area de almacenamiento. + */ + static void clear() + throw(RuntimeException) { + if(st_storageArea == NULL) + throw RuntimeException("ObjectFacade uninitialized ", ANNA_FILE_LOCATION); + + st_storageArea->clear(); + } + + /** + Devuelve de una copia del objeto recibido como parametro e incrementa la cuenta de utilizacion + asociada a la instancia. + + \param t Instancia del tipo T obtenida mediate el metodo #instance. + + \return Una copia del objeto recibido como parametro. Si el parametro recibido es NULL devolveria NULL. + + \warning Cada llamada a este metodo deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + static T* duplicate(const T* t) + throw(RuntimeException) { + if(st_storageArea == NULL) + throw RuntimeException("ObjectFacade uninitialized ", ANNA_FILE_LOCATION); + + return static_cast (st_storageArea->duplicate(t)); + } + + /** + Permite conocer si un determinado objeto esta alojado en el area de almacenamiento. + + \param loader Cargador de clase encargado de localizar la informacion referente al objeto buscado. + + \return \em true Si el objeto identificado por el Loader esta en el area de almacenamiento o + \em false en otro caso. + */ + static bool isLoaded(const Loader& loader) + throw(RuntimeException) { + if(st_storageArea == NULL) + throw RuntimeException("ObjectFacade uninitialized ", ANNA_FILE_LOCATION); + + return st_storageArea->isLoaded(loader); + } + + /** + Transfiere la informacion del objeto recibido como parametro al medio fisico usando el Recorder + recibido como parametro. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param recorder Grabador usado para transferir los datos al medio fisico. + */ + static void apply(dbms::Connection& connection, Recorder& recorder) + throw(RuntimeException, dbms::DatabaseException) { + if(st_storageArea == NULL) { + std::string msg(recorder.asString()); + msg += " | ObjectFacade uninitialized"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + st_storageArea->apply(connection, recorder); + } + + /** + Elimina la informacion del objeto recibido como parametro del medio fisico usando el Eraser + recibido como parametro. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param eraser Objecto usado para eliminar los datos al medio fisico. + + \warning Si la cuanta de utilizacion de T es 1 se liberaria en otro caso se devolveria una excepcion. + */ + static void apply(dbms::Connection& connection, Eraser& eraser) + throw(RuntimeException, dbms::DatabaseException) { + if(st_storageArea == NULL) { + std::string msg(eraser.asString()); + msg += " | ObjectFacade uninitialized"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + st_storageArea->apply(connection, eraser); + } + + /** + Elimina toda la informacion referente al objeto recibido como parametro, siempre y cuando + solo tenga un unica referencia activa. Descarga el objeto de la memoria de almacenamiento, + + \param t Instancia del tipo T que vamos a descargar de la memoria de almacenamiento. + + \warning \li Si el objeto recibido como parametro no fue reservado mediate #instance no tiene + ningun efecto. \li La instancia a liberar solo puede tener 1 en su cuenta de utilizacion. + \li La memoria asignada a la instancia recibida es liberada, por lo que podemos evitar la invocacion + al metodo #release para esta instancia. + */ + static void erase(T*& t) + throw(RuntimeException) { + if(st_storageArea == NULL) + return; + + st_storageArea->erase(reinterpret_cast (&t)); + } + + /** + Devuelve el puntero sobre el que estaria posicionado el iterador recibido como parametro. + \return El puntero sobre el que estaria posicionado el iterador recibido como parametro. + */ + static T* data(StorageArea::iterator& ii) throw() { return static_cast (StorageArea::data(ii)); } + + /** + Devuelve el puntero sobre el que estaria posicionado el iterador recibido como parametro. + \return El puntero sobre el que estaria posicionado el iterador recibido como parametro. + */ + static const T* data(StorageArea::const_iterator& ii) throw() { return static_cast (StorageArea::data(ii)); } + + /** + Metodo creador de nuevas instancias de la clase T. + \return Una nueva instancia del tipo de objeto T. + \warning Solo deberia ser llamado desde anna::comm::StorageArea cuando sea preciso crear + nuevas instancias de objetos. + */ + static Object* allocator() throw() { return new T; } + +protected: + static StorageArea* st_storageArea; + + /** + Constructor. + */ + ObjectFacade() {} +}; + +} +} + +/** + Definicion a la que hay que invocar en la implementacion de la clase que hereda + de anna::dbos::ObjectFacade. + + \param T Nombre de la clase que vamos a tratar en el ambito de C++. +*/ +#define dbos_prepare_object(T) \ + template <> anna::dbos::StorageArea* anna::dbos::ObjectFacade< T >::st_storageArea = NULL + +/** + Definicion a la que hay que invocar dentro de la definicion de la clase que hereda + de anna::dbos::ObjectFacade. + + \param T Nombre de la clase que vamos a tratar en el ambito de C++. +*/ +#define dbos_declare_object(T) \ + friend class anna::dbos::ObjectFacade + + +#endif diff --git a/include/anna/dbos/Recorder.hpp b/include/anna/dbos/Recorder.hpp new file mode 100644 index 0000000..696e665 --- /dev/null +++ b/include/anna/dbos/Recorder.hpp @@ -0,0 +1,51 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Recorder_hpp +#define anna_dbos_Recorder_hpp + +#include + +namespace anna { + +namespace dbos { + +/** + Interfaz que deben cumplir los objetos encargados de grabar el objeto en el medio fisico, + que normalmente sera alguna base de datos. +*/ +class Recorder : public Accesor { +public: + /** + Metodo de debemos re-escribir para devolver el nombre completo del selector de recursos. + Para evitar ambigüedades este nombre deberia incluir la lista completa de \em namespaces + a la que pertenece la clase. + \return Una cadena con el nombre de este selector. + */ + virtual const char* getClassName() const throw() { return "anna::dbos::Recorder"; } + +protected: + /** + Constructor. + \param database Base de datos asociada a este cargador y que deberia servir para + obtener los datos de un objeto. Debe tener la misma disponibilidad que este cargador. + \param id Identificador de este accesor. + */ + Recorder(dbms::Database& database, const Id id = 0) : Accesor(database, id) {;} + +private: + Index getIndex() const throw() { return 0; } // No se usa +}; + +} +} + +#endif + + + diff --git a/include/anna/dbos/Repository.hpp b/include/anna/dbos/Repository.hpp new file mode 100644 index 0000000..57a90ea --- /dev/null +++ b/include/anna/dbos/Repository.hpp @@ -0,0 +1,154 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Repository_hpp +#define anna_dbos_Repository_hpp + +#include + +#include + +#include +#include + +namespace anna { + +namespace xml { +class Node; +} + +namespace comm { +class INetAddress; +class Delivery; +} + +namespace dbms { +class Connection; +} + +namespace dbos { + +/** + Clase que modela la interaccion entre la base y nuestra aplicacion. +*/ +class Repository : public Mutex { +public: + typedef std::map container; + typedef container::const_iterator const_storage_iterator; /**second; } + + /** + Devuelve un documento XML con la informacion referente a esta instancia. + \param parent Nodo XML del que dependende la informacion. + @return un documento XML con la informacion referente a esta instancia. + */ + xml::Node* asXML(xml::Node* parent) const throw(); + +protected: + /** + Devuelve un iterator al comienzo de la lista de areas de almacenamiento de esta base de datos. + \return Un iterator al comienzo de la lista de areas de almacenamiento de esta base de datos. + */ + storage_iterator storage_begin() throw() { return a_storageAreas.begin(); } + + /** + Devuelve un iterator al final de la lista de areas de almacenamiento de esta base de datos. + \return Un iterator al final de la lista de areas de almacenamiento de esta base de datos. + */ + storage_iterator storage_end() throw() { return a_storageAreas.end(); } + + /** + Devuelve el objeto sobre el que esta posicionado el iterator recibido como parametro. + \param ii Iterator que deberia estar comprendido entre #const_storage_begin y #const_storage_end. + \return El objeto sobre el que esta posicionado el iterator recibido como parametro. + */ + static StorageArea* storageArea(storage_iterator& ii) throw() { return ii->second; } + +private: + std::string a_name; + container a_storageAreas; + + Repository(const Repository&); +}; + +} +} + +#endif diff --git a/include/anna/dbos/Set.hpp b/include/anna/dbos/Set.hpp new file mode 100644 index 0000000..a4325b9 --- /dev/null +++ b/include/anna/dbos/Set.hpp @@ -0,0 +1,173 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_Set_hpp +#define anna_dbos_Set_hpp + +#include +#include +#include + +#include +#include + +#include + +namespace anna { + +namespace dbos { + +/** + Template para acceder a los elementos de un conjunto de objetos inicializados a partir + de los datos contenidos en un medio fisico. + + A continuacion presentamos un ejemplo de uso: + + \code + Server::Set * servers (NULL); // Server hereda de esta clase. + ServerLoader serverLoader; // ServerLoader hereda de dbos::Loader. + Server* server; + + try { + serverLoader.setKey (..........); // Establece los parametros de busqueda + + servers = Server::instantiate (serverLoader); + + if (servers->size () == 0) { + .... + .... Si fuera necesario Trataria la condicion de no encontrar ningun registro + .... + } + + Server::iterator ii, maxii; + + for (ii = servers->begin (), maxii = servers->end (); ii != maxii; ii ++) { + server = *ii; + + .... Trataria cada uno de los Server encontrados .... + } + + Server::release (servers); + } + catch (Exception& ex) { + Server::release (servers); + + ... + ... Si fuera necesario Trataria la condicion de error. + } + \endcode + */ +template class Set : public Object { +public: + /** + Sinonimo usado para definir la clase contenedora de objetos del conjunto. + */ + typedef typename anna::SafeRecycler > Container; + + /** + Sinonimo usado para acceder a los elementos del conjunto atraves de un iterador + de objetos no modificables. + */ + typedef typename Container::const_iterator const_iterator; + + /** + Sinonimo usado para acceder a los elementos del conjunto atraves de un iterador + de objetos modificables. + */ + typedef typename Container::iterator iterator; + + /** + Devuelve el inicio del vector de objetos contenidos en el conjunto. + \return El inicio del vector de objetos contenidos en el conjunto. + */ + const_iterator begin() const throw() { return a_objects.begin(); } + + /** + Devuelve el inicio del vector de objetos contenidos en el conjunto. + \return El inicio del vector de objetos contenidos en el conjunto. + */ + iterator begin() throw() { return a_objects.begin(); } + + /** + Devuelve el final del vector de objetos contenidos en el conjunto. + \return El final del vector de objetos contenidos en el conjunto. + */ + const_iterator end() const throw() { return a_objects.end(); } + + /** + Devuelve el final del vector de objetos contenidos en el conjunto. + \return El final del vector de objetos contenidos en el conjunto. + */ + iterator end() throw() { return a_objects.end(); } + + /** + Crea un nuevo puntero de la clase T dentro de este conjunto. + */ + T* append() throw(RuntimeException) { return a_objects.create(); } + + /** + Saca de este conjunto la instancia recibida como parametro y libera su memoria asociada. + La operacion se ignoraria si el puntero recibido como parametro es nulo o no pertenece al conjunto. + \param t Instancia que del objeto a eliminar. + */ + void remove(T*& t) throw(RuntimeException) { a_objects.release(t); t = NULL; } + + /** + Devuelve el nmero de elementos contenidos en el conjunto. + \return El nmero de elementos contenidos en el conjunto. + */ + int size() const throw() { return a_objects.size(); } + + /** + Devuelve el puntero sobre el que esta posicionado el iterador recibido como parametro. + \return El puntero sobre el que esta posicionado el iterador recibido como parametro. + */ + static T* data(iterator& ii) throw() { return Container::data(ii); } + + /** + Devuelve el puntero sobre el que esta posicionado el iterador recibido como parametro. + \return El puntero sobre el que esta posicionado el iterador recibido como parametro. + */ + static const T* data(const_iterator& ii) throw() { return Container::data(ii); } + +private: + Container a_objects; + + void initialize(Loader& loader) + throw(RuntimeException, dbms::DatabaseException) { + T* object; + dbms::Statement* statement = loader.getStatement(); + + try { + do { + a_objects.create()->initialize(loader); + } while(statement->fetch() == true); + } catch(RuntimeException&) { + destroy(); + throw; + } catch(dbms::DatabaseException&) { + destroy(); + throw; + } + } + + void destroy() + throw() { + for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) + data(ii)->destroy(); + + a_objects.clear(); + } +}; + +} +} + +#endif + + diff --git a/include/anna/dbos/SetFacade.hpp b/include/anna/dbos/SetFacade.hpp new file mode 100644 index 0000000..babadc9 --- /dev/null +++ b/include/anna/dbos/SetFacade.hpp @@ -0,0 +1,126 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_SetFacade_hpp +#define anna_dbos_SetFacade_hpp + +#include + +#include +#include + +namespace anna { + +namespace dbos { + +/** + Clase que facilita el acceso y uso de las clases encargadas de la instanciacion de multiples objetos a partir de los + datos contenidos en un medio fisico, que normalmente seria la tabla de una base de datos. + + Esta nos facilita el manejo de instancias multiples, es decir, para una condicion de carga dada hay varios registros + o elementos del medio fisico que la cumplen. + + La clase \em T debe tener definidos los siquientes metodos: + \li anna::dbos::Object::instantiate: Interpreta la informacin la del medio fisico para adecuarla a + la clase C++. + \li void destroy () throw (): Libera los recursos reservados por este objeto + + \see dbos_declare_set + \see dbos_prepare_set +*/ +template class SetFacade : public ObjectFacade < Set > { +public: + typedef typename Set::iterator iterator; + typedef typename Set::const_iterator const_iterator; + + /** + Devuelve el numero de elementos contenidos en el conjunto recibido como parametro. + Se puede usar sin tener que preocuparse por + el valor de la instancia del conjunto, ya que si este es NULL devolveria 0. + \param t Instancia del conjunto. + \return El numero de elementos que contiene el conjunto. + */ + static int size(Set* t) throw() { return (t == NULL) ? 0 : t->size(); } + + /** + Iterator al primer elemento del conjunto. Se puede usar sin tener que preocuparse por + el valor de la instancia del conjunto, ya que si este es NULL devolveria 0. + \return Un iterador del primer elemento del conjunto. + */ + static iterator begin(Set* t) throw() { + return (t == NULL) ? iterator(0) : t->begin(); + } + + /** + Iterator al ultimo elemento del conjunto. Se puede usar sin tener que preocuparse por + el valor de la instancia del conjunto, ya que si este es NULL devolveria 0. + \return Un iterador del primer elemento del conjunto. + */ + static iterator end(Set* t) throw() { + return (t == NULL) ? iterator(0) : t->end(); + } + + /** + Iterator al primer elemento del conjunto. Se puede usar sin tener que preocuparse por + el valor de la instancia del conjunto, ya que si este es NULL devolveria 0. + \return Un iterador del primer elemento del conjunto. + */ + static const_iterator begin(const Set* t) throw() { + return (t == NULL) ? const_iterator(0) : t->begin(); + } + + /** + Iterator al ultimo elemento del conjunto. Se puede usar sin tener que preocuparse por + el valor de la instancia del conjunto, ya que si este es NULL devolveria 0. + \return Un iterador del primer elemento del conjunto. + */ + static const_iterator end(const Set* t) throw() { + return (t == NULL) ? const_iterator(0) : t->end(); + } + /** + Devuelve el objeto referenciado por el iterator recibido como parametro. + \return El objeto referenciado por el iterator recibido como parametro. + */ + static T* data(iterator ii) throw() { return Set::data(ii); } + + /** + Devuelve el objeto referenciado por el iterator recibido como parametro. + \return El objeto referenciado por el iterator recibido como parametro. + */ + static const T* data(const_iterator ii) throw() { return Set::data(ii); } + +protected: + /** + Contructor. + */ + SetFacade() {} +}; + +/** + Definicion a la que hay que invocar en la implementacion de la clase que hereda + de anna::dbos::SetFacade. + + \param T Nombre de la clase que vamos a tratar en el ambito de C++. +*/ +#define dbos_prepare_set(T) \ + template <> anna::dbos::StorageArea* anna::dbos::ObjectFacade < anna::dbos::Set >::st_storageArea = NULL + +/** + Definicion a la que hay que invocar dentro de la definicion de la clase que hereda + de anna::dbos::SetFacade. + + \param T Nombre de la clase que vamos a tratar en el ambito de C++. +*/ +#define dbos_declare_set(T) \ + friend class anna::Allocator; \ + friend class anna::dbos::Set + +} +} + +#endif diff --git a/include/anna/dbos/StorageArea.hpp b/include/anna/dbos/StorageArea.hpp new file mode 100644 index 0000000..e9bda03 --- /dev/null +++ b/include/anna/dbos/StorageArea.hpp @@ -0,0 +1,625 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_StorageArea_hpp +#define anna_dbos_StorageArea_hpp + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +namespace anna { + +namespace xml { +class Node; +} + +namespace dbms { +class Connection; +} + +namespace dbos { + +class Accesor; +class Object; +class Creator; +class Loader; +class Recorder; +class Eraser; +class Repository; +class CrossedLoader; + +/** + Area de almacenamiento de los objetos obtenidos a partir de los datos guardados en un medio + fisico, que normalmente seria una base de datos. + + La creacion de cualquier área de almacenamiento debe hacerse mediante el método + anna::dbos::Repository::createStorageArea. + + \warning Exclusivamente para uso interno de anna.dbos +*/ +class StorageArea : public Mutex { + struct Instance; + class Block; + class Holes; + +public: + /** + Modo de acceso al área de almacenamiento. + */ + struct AccessMode { + enum _v { + /** + Carga el objeto una vez y no lo vuelve a comprobar mientras tena un estado valido. + No permite la creacion de nuevos objetos. + */ + ReadOnly, + /** + Carga el objeto una vez e intenta recargarlo cada vez que su cuenta de utilizacion + sea cero. Se invocara al método dbos::Object::needUpdate () que se ejecuta antes de + realizar ninguna operacion sobre el medio fisico, si devuelve 'true' cargaria la + informacion mediante el dbos::Loader correspondiente y se invocaria al + método dbos::Object::hasChange (dbos::Loader&). + */ + ReadWrite, + /** + Comprueba la recarga del objeto cada vez que se accede a el. Se invocara al método dbos::Object::needUpdate () que se ejecuta antes de + realizar ninguna operacion sobre el medio fisico, si devuelve 'true' cargaria la + informacion mediante el dbos::Loader correspondiente y se invocaria al + método dbos::Object::hasChange (dbos::Loader&). + */ + ReadEver + }; + + /* + Devuelve el literal correspondiente al codigo recibido. + \param v Codigo a traducir a literal. + \return el literal correspondiente al codigo recibido. + */ + static const char* asString(const _v v) throw(); + }; + + /** + Flags usados para marcar los registros cargados en memoria. + \warning Exclusivamente uso interno. + */ + struct Flag { + enum _v { + None = 0, Dirty = 1, Incoherent = 2, Empty = 4, HasHole = 8, Ready = 16, InProgress = 32, + NoDirty = ~Dirty, NoIncoherent = ~Incoherent, NoEmpty = ~Empty, NoHasHole = ~HasHole, NoReady = ~Ready, Done = ~InProgress + }; + }; + + /** + Tamaños normalizados del área de almacenamiento. + + @see StorageArea + */ + struct StandardSize { + enum Value { + Tiny = 16, /**< Intenta que el número de registros cargados no supere los 16 registros. */ + Little = 64, /**< Intenta que el número de registros cargados no supere los 64 registros. */ + Small = 256, /** Blocks; /**< Estructura para mantener los bloques de objetos */ + typedef std::map ::iterator iterator; /**< Definicion para recorrer los objetos del área de almacenamiento */ + typedef std::map ::const_iterator const_iterator; /**< Definicion para recorrer los objetos del área de almacenamiento */ + + /** + Destructor. + */ + virtual ~StorageArea(); + + /** + * Devuelve el código de error asociado a la excepción cuando no se encuentra un registro buscado. + * \return el código de error asociado a la excepción cuando no se encuentra un registro buscado. + */ + int getErrorCode() const throw() { return a_errorCode; } + + /* + * Devuelve el nombre de la este área de almacenamiento. + * \return el nombre de la este área de almacenamiento. + */ + const std::string& getName() const throw() { return a_name; } + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar esta clase \em errorCode se halla indicado un #NoExceptionWhenNotFound en otro + caso si no encuentra el objeto que cumpla el patron devolveria una excepcion de ejecucion. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + Object* instance(dbms::Connection& connection, Loader& loader) throw(RuntimeException, dbms::DatabaseException) { + return instance(&connection, loader); + } + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar esta clase \em errorCode se halla indicado un #NoExceptionWhenNotFound en otro + caso si no encuentra el objeto que cumpla el patron devolveria una excepcion de ejecucion. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + Object* instance(dbms::Connection* connection, Loader& loader) throw(RuntimeException, dbms::DatabaseException); + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param crossedLoader Cargador encargado de encontrar la clave principal a aplicar con el #Loader + recibido como parámetro en base a una clave alternativa contenida en el mismo. + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar esta clase \em errorCode se halla indicado un #NoExceptionWhenNotFound en otro + caso si no encuentra el objeto que cumpla el patron devolveria una excepcion de ejecucion. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + Object* instance(dbms::Connection& connection, CrossedLoader& crossedLoader, Loader& loader) + throw(RuntimeException, dbms::DatabaseException) { + return instance(&connection, crossedLoader, loader); + } + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario extraer los datos del medio fisico. + \param crossedLoader Cargador encargado de encontrar la clave principal a aplicar con el #Loader + recibido como parámetro en base a una clave alternativa contenida en el mismo. + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar esta clase \em errorCode se halla indicado un #NoExceptionWhenNotFound en otro + caso si no encuentra el objeto que cumpla el patron devolveria una excepcion de ejecucion. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + Object* instance(dbms::Connection* connection, CrossedLoader& crossedLoader, Loader& loader) + throw(RuntimeException, dbms::DatabaseException); + + /** + Carga la informacion de un objeto contenida en un medio fisico y la interpreta para adecuarla a + una clase C++. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. + + \param loader Cargador de clase encargado de localizar y obtener la informacion referente al objeto + que deseamos cargar en memoria. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL en caso de + que al inicializar esta clase \em errorCode se halla indicado un #NoExceptionWhenNotFound en otro + caso si no encuentra el objeto que cumpla el patron devolveria una excepcion de ejecucion. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + \warning Para usar este método se requiere haber re-escrito el método virtual Loader::load para que no intente + obtener los datos desde la base de datos. + */ + Object* instance(Loader& loader) throw(RuntimeException, dbms::DatabaseException) { + return instance(NULL, loader); + } + + /** + Crea un objeto en el área de almacenamiento un y lo prepara para ser transferido al medio fisico + si fuera necesario. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario acceder al medio fisico. + \param creator Creador encargado de generar el objeto en el área de almacenamiento. + + \return La nueva instancia que cumple el patron establecido por el creador. + + \warning \li Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + \li No puede usarse en las áreas de almacenamiento definidas como AccessMode::ReadOnly. + */ + Object* create(dbms::Connection& connection, Creator& creator) throw(RuntimeException, dbms::DatabaseException) { + return create(&connection, creator); + } + + /** + Crea un objeto en el área de almacenamiento un y lo prepara para ser transferido al medio fisico + si fuera necesario. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param connection Conexion usada si fuera necesario acceder al medio fisico. + \param creator Creador encargado de generar el objeto en el área de almacenamiento. + + \return La nueva instancia que cumple el patron establecido por el creador. + + \warning \li Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + \li No puede usarse en las áreas de almacenamiento definidas como AccessMode::ReadOnly. + */ + Object* create(dbms::Connection* connection, Creator& creator) throw(RuntimeException, dbms::DatabaseException); + + /** + Crea un objeto en el área de almacenamiento y lo prepara para ser transferido al medio fisico + si fuera necesario. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe cargar. + + \param creator Creador encargado de generar el objeto en el área de almacenamiento. + + \return La nueva instancia que cumple el patron establecido por el creador. + + \warning \li Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + \li No puede usarse en las áreas de almacenamiento definidas como AccessMode::ReadOnly. + */ + Object* create(Creator& creator) throw(RuntimeException, dbms::DatabaseException) { return create(NULL, creator); } + + /** + Devuelve la informacion de un objeto cargado desde el medio fisico. + + Este cargador deberia tener todos los datos necesarios para localizar la informacion del objeto que + debe buscar. Por ejemplo, en caso de tener que obtener el objeto a partir de los datos contenidos + en una tabla de una base de datos deberia conocer la clave primaria del objeto a cargar, o alguna + otra combinacion de columnas que lo identifiquen univocamente. + + \param loader Cargador de clase encargado de localizar la informacion referente al objeto buscado. + + \return Una instancia que cumple el patron establecido por el cargador. Puede ser NULL si el + objeto no fue cargado en el área de almacenamiento. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + Object* find(Loader& loader) throw(RuntimeException); + + /** + Devuelve de una copia del objeto recibido como parámetro e incrementa la cuenta de utilizacion + asociada a la instancia. + + \param object Instancia obtenida mediate el método #instance. + + \return Una copia del objeto recibido como parámetro. Si el parámetro recibido es NULL devolveria NULL. + + \warning Cada llamada a este método deberia tener su correspondiente liberacion invocando a #release + cuando dejemos de usar la instancia. + */ + Object* duplicate(const Object* object) throw(RuntimeException); + + /** + Permite conocer si un determinado objeto esta alojado en el área de almacenamiento. No + cambia la cuenta de utilizacion de los objetos ni provoca cambios en las estadisticas + de aciertos. + + \param loader Cargador de clase encargado de localizar la informacion referente al objeto buscado. + + \return \em true Si el objeto identificado por el Loader esta en el área de almacenamiento o + \em false en otro caso. + */ + bool isLoaded(const Loader& loader) throw(RuntimeException); + + /** + Transfiere la informacion del objeto recibido como parámetro al medio fisico usando el Recorder + recibido como parámetro. + + \param connection Conexion usada si fuera necesario acceder al medio fisico. + \param recorder Grabador usado para transferir los datos al medio fisico. + */ + void apply(dbms::Connection& connection, Recorder& recorder) throw(RuntimeException, dbms::DatabaseException); + + /** + Elimina la informacion del objeto recibido como parámetro del medio fisico usando el Eraser + recibido como parámetro. + + \param connection Conexion usada si fuera necesario acceder al medio fisico. + \param eraser Objecto usado para eliminar los datos al medio fisico. + + \warning Si la cuenta de utilizacion del objeto es 1 se liberaría en otro caso se devolvería una excepción. + */ + void apply(dbms::Connection& connection, Eraser& eraser) throw(RuntimeException, dbms::DatabaseException); + + /** + Habilita la reutilizacion del espacio de memoria ocupado por un objeto instanciado mediate #instance. + + Este método no saca al objeto de la memoria de almacenamiento, sino que marca su espacio de memoria + como subceptible de ser reutilizado. De esta forma, si el número de objetos cargados en memoria se + acerca al tamaño maximo indicado en la inicializacin, cuando halla que cargar un nuevo registro se + reusaria el espacio libre en vez de seguir aumentando el tamaño de la memoria de almacenamiento. + + \param object Instancia del objeto que vamos a liberar. + + \warning Si el objeto recibido como parámetro no fue reservado mediate #instance no tiene + ningun efecto. + */ + void release(Object** object) throw(RuntimeException); + + /** + Elimina toda la informacion referente al objeto recibido como parámetro, siempre y cuando + solo tenga un unica referencia activa. Descarga el objeto de la memoria de almacenamiento, + + \param object Instancia que vamos a descargar de la memoria de almacenamiento. + + \warning \li Si el objeto recibido como parámetro no fue reservado mediate #instance no tiene + ningun efecto. + \li La instancia a liberar solo puede tener 1 en su cuenta de utilizacion. + */ + void erase(Object** object) throw(RuntimeException); + + /** + Marca el objeto recibido como pendiente de recarga de datos. + + \param object Instancia que vamos a marcar como pendiente de recarga. + + \warning \li Si el objeto recibido como parámetro no fue reservado mediate #instance no tiene + ningun efecto. + \li La instancia a marcar solo deberia tener una unica instancia en uso. + */ + void dirty(Object* object) throw(RuntimeException); + + /** + Establece el tamanho de las instancias de los objetos contenidos en este área de almacenamiento. + \param _sizeof Numero de bytes ocupado por cada una de las instancias. + */ + void setSizeof(const Size _sizeof) throw() { a_sizeof = _sizeof + sizeof(Instance); } + + /** + Devuelve el numero maximo de bytes teorico que puede llegar a reservar este área de almacenamiento. + \return el numero maximo de bytes teorico que puede llegar a reservar este área de almacenamiento. + */ + Size getMaxSizeOf() const throw() { return a_maxSize * a_sizeof; } + + /** + Devuelve el numero de bytes teorico que puede llegar a reservar este área de almacenamiento. + \return el numero de bytes teorico que puede llegar a reservar este área de almacenamiento. + */ + Size getSizeOf() const throw() { return a_directory.size() * a_sizeof; } + + /** + Devuelve un iterator al primero de los objetos contenido en el área del almacenamiento. + \return Un iterator al primero de los objetos contenido en el área del almacenamiento. + */ + iterator begin() throw() { return a_directory.begin(); } + + /** + Devuelve un iterator al primero de los objetos contenido en el área del almacenamiento. + \return Un iterator al primero de los objetos contenido en el área del almacenamiento. + */ + const_iterator begin() const throw() { return a_directory.begin(); } + + /** + Devuelve un iterator al fin de los objetos contenidos en el área del almacenamiento. + \return Un iterator al fin de los objetos contenidos en el área del almacenamiento. + */ + iterator end() throw() { return a_directory.end(); } + + /** + Devuelve un iterator al fin de los objetos contenidos en el área del almacenamiento. + \return Un iterator al fin de los objetos contenidos en el área del almacenamiento. + */ + const_iterator end() const throw() { return a_directory.end(); } + + /** + Devuelve el puntero sobre el que esta posicionado el iterador recibido como parámetro. + \return El puntero sobre el que esta posicionado el iterador recibido como parámetro. + */ + static Object* data(iterator ii) throw() { return ii->second->object; } + + /** + Devuelve el puntero sobre el que esta posicionado el iterador recibido como parámetro. + \return El puntero sobre el que esta posicionado el iterador recibido como parámetro. + */ + static const Object* data(const_iterator ii) throw() { return ii->second->object; } + + /** + Devuelve una cadena con la informacion referente a este área de almacenamiento. + \return Una cadena con la informacion referente a este área de almacenamiento. + */ + std::string asString() const throw(); + + /** + Devuelve un documento XML con la informacion referente a esta instancia. + \param parent Nodo XML del que dependende la informacion. + @return un documento XML con la informacion referente a esta instancia. + */ + xml::Node* asXML(xml::Node* parent) const throw(); + +protected: + /** + Descarga del área de almacenamiento todos los objetos que estuviera cargados. + Este método se invoca desde anna::dbos::Repository::clear + */ + void clear() throw(RuntimeException); + + /** + Devuelve un literal con el entero recibido tratado como una cantidad en bytes. + \return un literal con el entero recibido tratado como un cantidad en bytes + */ + static std::string asMemorySize(const Size size) throw(); + +private: + typedef std::map ::value_type value_type; + + friend class Holes; + + class Holes { + public: + struct Mode { enum _v { ReadyToReuse, TimeWait }; }; + + typedef std::list hole_container; + typedef hole_container::iterator hole_iterator; + + Holes() : a_size(0) {;} + + bool insert(Instance* instance, const Mode::_v mode) throw(); + void erase(Instance* instance) throw(); + Instance* front() throw() { return a_holes.front(); } + int size() const throw() { return a_size; } + int empty() const throw() { return a_holes.empty(); } + void pop_front() throw() { a_holes.pop_front(); a_size --; } + void clear() throw() { a_holes.clear(); a_size = 0; } + + private: + hole_container a_holes; + int a_size; + }; + + //------------------------------------------------------------------------ + // - object: Puntero a la instancia con el objeto cacheado. + // - msHoleTime: Millisecond en el que entra en la lista de huecos. + // - copyCounter: Numero de copias del objeto. + // - flags: Flags (Dirty, Incoherent, Empty). + //------------------------------------------------------------------------ + friend struct Instance; + + struct Instance { + Object* object; + Counter copyCounter; + int flags; + Holes::hole_iterator holeIterator; + + Instance() : copyCounter(0), object(NULL), flags(Flag::None) {;} + }; + + friend class Block; + + class Block { + public: + Block(ObjectAllocator objectAllocator, const Size maxSize); + Instance* getInstance() throw() { return (a_size < a_maxSize) ? &a_instances [a_size ++] : NULL; } + void reset() throw() { a_size = 0; } + + private: + Instance* a_instances; + Size a_maxSize; + Size a_size; + }; + + const std::string a_name; + const Size a_maxSize; + ObjectAllocator a_objectAllocator; + const AccessMode::_v a_accessMode; + int a_errorCode; + Size a_sizeof; + + Blocks a_blocks; + std::map a_directory; + Holes a_holes; + Block* a_currentBlock; + int a_indexBlock; + Counter a_hit; + Counter a_fault; + Counter a_doneReuse; + + /* + typedef std::deque inprogress_container; + typedef inprogress_container::iterator inprogress_iterator; + inprogress_container a_inprogress; + + inprogress_iterator inprogress_begin () const throw () { return a_inprogress.begin (); } + inprogress_iterator inprogress_end () const throw () { return a_inprogress.end (); } + static DBIndex index (inprogress_iterator ii) throw () { return *ii; } + bool isInProgress (const DBIndex index) const throw (); + */ + + /* + * Constructor. + * \warning Este método sólo debería usarse desde el método dbos::Repository::allocateStorageArea. + */ + StorageArea(const char* name, const Size maxSize, ObjectAllocator, const AccessMode::_v, const int errorCode); + StorageArea(const StorageArea&); + + Object* reload(dbms::Connection*, Loader&, Instance*) throw(RuntimeException, dbms::DatabaseException); + void checkIncoherence(Instance*) throw(); + bool quickReusing(Instance*) throw(); + void verifyStatus(StorageArea::Instance*, const bool ignoreDirty = false) throw(RuntimeException); + Instance* allocate() throw(); + Instance* reuse() throw(); + + static Instance* instance(iterator& ii) throw() { return ii->second; } + static std::string asString(const Instance*) throw(); + + friend class Repository; +}; + +} +} + +#endif diff --git a/include/anna/dbos/dbos.hpp b/include/anna/dbos/dbos.hpp new file mode 100644 index 0000000..1e7609e --- /dev/null +++ b/include/anna/dbos/dbos.hpp @@ -0,0 +1,50 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_dbos_hpp +#define anna_dbos_dbos_hpp + +namespace anna { +/** +Define las clases y templates necesarias para convertir datos guardados en un medio fisico en +clases totalmente funcionalines en C++. + +El ejecutable debera enlazarse con las librerias: + \li anna.core.a + \li anna.xml.a + \li anna.app.a + \li anna.comm.a + \li anna.dbms.a + \li anna.dbos.a + +El Packet Header es anna.dbos.h +*/ +namespace dbos { +} +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace anna::dbos; + +#endif + diff --git a/include/anna/dbos/defines.hpp b/include/anna/dbos/defines.hpp new file mode 100644 index 0000000..7e9a25f --- /dev/null +++ b/include/anna/dbos/defines.hpp @@ -0,0 +1,26 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_defines_hpp +#define anna_dbos_defines_hpp + +#include + +namespace anna { + +namespace dbos { +typedef unsigned int Size; +typedef U64 Index; +typedef ptrnumber StorageId; +typedef unsigned int Counter; +} + +} + +#endif + diff --git a/include/anna/dbos/internal/sccs.hpp b/include/anna/dbos/internal/sccs.hpp new file mode 100644 index 0000000..e4699c5 --- /dev/null +++ b/include/anna/dbos/internal/sccs.hpp @@ -0,0 +1,25 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_dbos_internal_sccs_hpp +#define anna_dbos_internal_sccs_hpp + +namespace anna { + +namespace dbos { + +class sccs { +public: + static void activate() throw(); +}; + +} +} + +#endif + diff --git a/source/dbms.mysql/BaseBind.cpp b/source/dbms.mysql/BaseBind.cpp new file mode 100644 index 0000000..b91ca85 --- /dev/null +++ b/source/dbms.mysql/BaseBind.cpp @@ -0,0 +1,107 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include + +#include + +using namespace std; +using namespace anna; + +//---------------------------------------------------------------------------- +// (1) Reserva 2 Kbytes para trabajar con el LOB. +//---------------------------------------------------------------------------- +dbms::mysql::BaseBind::BaseBind(const dbms::Data& data) : + a_type(data.getType()), + a_time(NULL) { + switch(a_type) { + case Data::Type::ShortBlock: + case Data::Type::LongBlock: + break; + case Data::Type::Date: // (1) + case Data::Type::TimeStamp: + a_time = new MYSQL_TIME; + break; + default: break; + } +} + +dbms::mysql::BaseBind::~BaseBind() { + delete a_time; +} + +/** + * Según http://dev.mysql.com/doc/refman/4.1/en/c-api-prepared-statement-datatypes.html + * y el truco para recoger BLOB's http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html. + */ +void dbms::mysql::BaseBind::setupBind(st_mysql_bind& bind, dbms::Data& data) +throw(RuntimeException) { + anna_memset(&bind, 0, sizeof(bind)); + bind.is_null = &a_nullIndicator; + a_length = 0; + + switch(a_type) { + case Data::Type::Integer: + bind.buffer_type = MYSQL_TYPE_LONG; + bind.buffer_length = data.getMaxSize(); + bind.length = &a_length; + bind.buffer = const_cast (data).getBuffer(); + bind.is_unsigned = false; + break; + case Data::Type::String: + bind.buffer_type = MYSQL_TYPE_STRING; + bind.buffer_length = data.getMaxSize(); + bind.length = &a_length; + bind.buffer = const_cast (data).getBuffer(); + break; + case Data::Type::Float: + bind.buffer_type = MYSQL_TYPE_FLOAT; + bind.buffer_length = data.getMaxSize(); + bind.length = &a_length; + bind.buffer = const_cast (data).getBuffer(); + break; + case Data::Type::Date: // (1) + bind.buffer_type = MYSQL_TYPE_DATE; + bind.buffer_length = sizeof(MYSQL_TIME); + bind.length = &a_length; + bind.buffer = (char*) a_time; + break; + case Data::Type::TimeStamp: + bind.buffer_type = MYSQL_TYPE_DATETIME; + bind.buffer_length = sizeof(MYSQL_TIME); + bind.length = &a_length; + bind.buffer = (char*) a_time; + break; + case Data::Type::ShortBlock: + + if((bind.buffer_length = data.getMaxSize()) < MaxBlobSize) { + bind.buffer_type = MYSQL_TYPE_BLOB; + bind.length = &a_length; + bind.buffer = const_cast (data).getBuffer(); + } else { + string msg(data.asString()); + msg += functions::asString(" | Over maximum size: %d/%d", data.getMaxSize(), MaxBlobSize); + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + break; + case Data::Type::LongBlock: + /** + * El tratamiento particular se realiza en las clases InputBind y OutputBind. + */ + break; + default: + throw RuntimeException(functions::asString("Unsupported data type %d", (int) a_type), ANNA_FILE_LOCATION); + } +} + diff --git a/source/dbms.mysql/Connection.cpp b/source/dbms.mysql/Connection.cpp new file mode 100644 index 0000000..aae802c --- /dev/null +++ b/source/dbms.mysql/Connection.cpp @@ -0,0 +1,103 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +mysql::Connection::Connection(Database& database, const std::string& name, const char* user, const char* password) : + dbms::Connection(database, name, user, password), + a_mysqlDatabase(database), + a_mysql(NULL) { +} + +void mysql::Connection::open() +throw(dbms::DatabaseException) { + if(a_mysql != NULL) { + LOGWARNING( + string msg = asString(); + msg += " | Has already been established"; + Logger::warning(msg, ANNA_FILE_LOCATION); + ); + return; + } + + if((a_mysql = mysql_init(NULL)) == NULL) + RuntimeException("Cannot initiate MySQL", ANNA_FILE_LOCATION); + + const char* dbmsName = (a_mysqlDatabase.getType() == Database::Type::Remote) ? a_mysqlDatabase.getName().c_str() : NULL; + + try { + if(mysql_real_connect(a_mysql, a_mysqlDatabase.getHost(), a_user.c_str(), a_password.c_str(), dbmsName, 0, NULL, 0L) == NULL) { + ResultCode resultCode(a_mysql); + throw DatabaseException(resultCode, ANNA_FILE_LOCATION); + } + + LOGINFORMATION( + string msg("anna::dbms::mysql::Connection::open | "); + msg += asString(); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + } catch(DatabaseException& edbms) { + close(); + throw; + } +} + +void mysql::Connection::close() +throw() { + LOGINFORMATION( + string msg("anna::dbms::mysql::Connection::close | "); + msg += asString(); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + + if(a_mysql != NULL) { + mysql_close(a_mysql); + a_mysql = NULL; + LOGINFORMATION( + string msg("anna::dbms::mysql::Connection::close | "); + msg += asString(); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + } +} + +void mysql::Connection::do_commit() +throw(RuntimeException, dbms::DatabaseException) { + anna_dbms_mysql_check(mysql_commit(a_mysql), a_mysql); +} + +void mysql::Connection::do_rollback() +throw() { + try { + anna_dbms_mysql_check(mysql_rollback(a_mysql), a_mysql); + } catch(Exception& ex) { + ex.trace(); + } +} + +string mysql::Connection::asString() const +throw() { + string result("dbms::mysql::Connection { "); + result += dbms::Connection::asString(); + result += " | Context: "; + result += (a_mysql == NULL) ? "(null)" : functions::asHexString(anna_ptrnumber_cast(a_mysql)); + return result += " }"; +} + diff --git a/source/dbms.mysql/Database.cpp b/source/dbms.mysql/Database.cpp new file mode 100644 index 0000000..9508158 --- /dev/null +++ b/source/dbms.mysql/Database.cpp @@ -0,0 +1,77 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +mysql::Database::Database(const char* dbmsName, const char* host) : + dbms::Database(getClassName(), dbmsName) { + a_host = (host == NULL) ? NULL : strdup(host); + mysql::sccs::activate(); +} + +mysql::Database::Database(const char* componentName, const char* dbmsName, const char* host) : + dbms::Database(componentName, dbmsName) { + a_host = (host == NULL) ? NULL : strdup(host); + mysql::sccs::activate(); +} + +void mysql::Database::do_initialize() +throw(RuntimeException) { + LOGMETHOD(TraceMethod tm("anna::dbms::mysql::Database", "do_initialize", ANNA_FILE_LOCATION)); + dbms::Database::do_initialize(); +} + +//---------------------------------------------------------------------------------- +// Libera todos los manejadores asociados a este entorno. +//---------------------------------------------------------------------------------- +mysql::Database::~Database() { + LOGMETHOD(TraceMethod tm("anna::dbms::mysql::Database", "~Database", ANNA_FILE_LOCATION)); + + if(a_host != NULL) + free(a_host); +} + +dbms::Connection* mysql::Database::allocateConnection(const std::string& name, const char* user, const char* password) +throw(RuntimeException) { + return new Connection(*this, name, user, password); +} + +dbms::Statement* mysql::Database::allocateStatement(const char* name, const std::string& expression, const bool isCritical) +throw(RuntimeException) { + return new Statement(*this, name, expression, isCritical); +} + +dbms::InputBind* mysql::Database::allocateInputBind(const char* name, Data& data) +throw(RuntimeException) { + return new InputBind(name, data); +} + +void mysql::Database::deallocate(dbms::InputBind* inputBind) +throw() { + delete(InputBind*) inputBind; +} + +dbms::OutputBind* mysql::Database::allocateOutputBind(const char* name, Data& data) +throw(RuntimeException) { + return new OutputBind(name, data); +} + +void mysql::Database::deallocate(dbms::OutputBind* outputBind) +throw() { + delete(OutputBind*) outputBind; +} diff --git a/source/dbms.mysql/InputBind.cpp b/source/dbms.mysql/InputBind.cpp new file mode 100644 index 0000000..edfb2d3 --- /dev/null +++ b/source/dbms.mysql/InputBind.cpp @@ -0,0 +1,110 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace anna; + +InputBind::InputBind(const char* name, dbms::Data& data) : + dbms::InputBind(name, data), + BaseBind(data) { +} + +InputBind::~InputBind() { +} + +/* + * Completa la informacion establececida por el setupBind. + */ +void InputBind::prepare(anna::dbms::Statement* dbmsStmt, anna::dbms::Connection*, const int pos) +throw(RuntimeException) { + st_mysql_bind* bind = static_cast (dbmsStmt)->getBindParams() + pos; + Data& data = anna::dbms::Bind::getData(); + BaseBind::setupBind(*bind, data); + + if(data.getType() == Data::Type::LongBlock) { + DataBlock& dataBlock = static_cast (data).getValue(); + bind->buffer_type = MYSQL_TYPE_BLOB; + bind->buffer_length = dataBlock.getSize(); + bind->buffer = (char*) dataBlock.getData(); + bind->length = &a_length; + } +} + +/* + * Se invoca desde anna::dbms::mysql::Statement::execute. + * Codificar� la informaci�n C++ de forma que encaje en las estructuras requeridas por el API de MySQL. + */ +void InputBind::code() const +throw(RuntimeException) { + InputBind* _this = const_cast (this); + Data& data = _this->getData(); + + if((_this->a_nullIndicator = data.isNull() ? true : false) == true) + return; + + switch(data.getType()) { + case Data::Type::String: + _this->a_length = anna_strlen((char*)(static_cast (data).getBuffer())); + break; + case Data::Type::Date: + case Data::Type::TimeStamp: + _this->codeDate(data); + break; + case Data::Type::Integer: + throw RuntimeException("anna::dbms::mysql::InputBind::code not implemented for Data::Type::Integer", ANNA_FILE_LOCATION); + break; + case Data::Type::Float: + throw RuntimeException("anna::dbms::mysql::InputBind::code not implemented for Data::Type::Float", ANNA_FILE_LOCATION); + break; + case Data::Type::ShortBlock: + throw RuntimeException("anna::dbms::mysql::InputBind::code not implemented for Data::Type::ShortBlock", ANNA_FILE_LOCATION); + break; + case Data::Type::LongBlock: + throw RuntimeException("anna::dbms::mysql::InputBind::code not implemented for Data::Type::LongBlock", ANNA_FILE_LOCATION); + break; + + } +} + +/** + * El bind.buffer ha sido asociado a una estructura de tipo MYSQL_TIME (a_time), cuyos valores vamos + * a establecer en �ste m�todo. + */ +void InputBind::codeDate(dbms::Data& data) +throw() { + dbms::Date& date = static_cast (data); + + if(data.getType() == Data::Type::TimeStamp) { + a_time->second_part = static_cast (data).getFractionalSecond(); + } + + a_time->year = date.getYear(); + a_time->month = date.getMonth(); + a_time->day = date.getDay(); + a_time->hour = date.getHour(); + a_time->minute = date.getMinute(); + a_time->second = date.getSecond(); +} + diff --git a/source/dbms.mysql/OracleTranslator.cpp b/source/dbms.mysql/OracleTranslator.cpp new file mode 100644 index 0000000..4db0d52 --- /dev/null +++ b/source/dbms.mysql/OracleTranslator.cpp @@ -0,0 +1,94 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +mysql::OracleTranslator mysql::OracleTranslator::st_this; + +/* + * Pone las sentencias SQL escritas para Oracle en el formato que necesita el + * MySQL. + * + * La sentencia Oracle podría ser algo así como: + * insert into foo (a, b, c) values (:x, :y, :zzzz) + * update goo set xx=&value where yy=:zzz + * + * Y Debería quedar algo así: + * insert into foo (a, b, c) values (?,?,?) + * update goo set xx=? where yy=? + */ +const char* mysql::OracleTranslator::apply(const char* statement) +throw(RuntimeException) { + bool makeit = false; + + if(anna_strchr(statement, ':') != NULL) + makeit = true; + else if(anna_strchr(statement, '&') != NULL) + makeit = true; + + if(makeit == false) + return statement; + + allocate(statement); + enum { Copying, Filtering }; + int mode(Copying); + char* w = a_buffer; + char character; + + while((character = *statement) != 0) { + switch(mode) { + case Copying: + + if(character == ':' || character == '&') { + *w ++ = '?'; + mode = Filtering; + } else + *w ++ = character; + + break; + case Filtering: + + if(character == ',' || character == ')' || isspace(character) || iscntrl(character)) { + *w ++ = character; + mode = Copying; + } + + break; + } + + statement ++; + } + + *w = 0; + return a_buffer; +} + +void mysql::OracleTranslator::allocate(const char* statement) +throw() { + const int size = anna_strlen(statement); + + if(size > a_size) { + if(a_size > 0) { + delete a_buffer; + a_buffer = NULL; + } + + a_buffer = new char [a_size = size + 1]; + } + + a_buffer [0] = 0; +} + diff --git a/source/dbms.mysql/OutputBind.cpp b/source/dbms.mysql/OutputBind.cpp new file mode 100644 index 0000000..016b9e9 --- /dev/null +++ b/source/dbms.mysql/OutputBind.cpp @@ -0,0 +1,174 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace anna; +using namespace std; + +OutputBind::OutputBind(const char* name, dbms::Data& data) : + dbms::OutputBind(name, data), + BaseBind(data) { + a_blob = (data.getType() == Data::Type::LongBlock) ? new Blob : NULL; +} + +OutputBind::~OutputBind() { + delete a_blob; +} + +/* + * El dato de LONG BLOB se recoge según cuenta: + * http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html. + */ +void OutputBind::prepare(anna::dbms::Statement* dbmsStmt, anna::dbms::Connection*, const int pos) +throw(RuntimeException) { + st_mysql_bind* bind = static_cast (dbmsStmt)->getBindResults() + pos; + Data& data = anna::dbms::Bind::getData(); + BaseBind::setupBind(*bind, data); + + if(data.getType() == Data::Type::LongBlock) { + dbms::mysql::Statement* myStmt = static_cast (dbmsStmt); + a_blob->stmt = *myStmt; + a_blob->binds = myStmt->getBindResults(); + a_blob->pos = pos; + bind->buffer_type = MYSQL_TYPE_BLOB; + bind->buffer = NULL; + bind->buffer_length = 0; + bind->length = &a_length; + } +} + +/* + * Transfiere la información del los MYSQL_BIND del API C de MySQL a las + * estructuras dbms::Data de nuestro programa C++. + */ +void OutputBind::decode() const +throw(RuntimeException) { + OutputBind* _this = const_cast (this); + char* str; + Data& data = _this->getData(); + data.setNull(a_nullIndicator == true); + + switch(data.getType()) { + case Data::Type::String: + str = (char*) data.getBuffer(); + + if(data.isNull() == true) + *str = 0; + else + dbms::String::strip(str); + + break; + case Data::Type::Float: + + if(data.isNull() == true) + static_cast (data) = 0.0; + + break; + case Data::Type::Date: + case Data::Type::TimeStamp: + _this->decodeDate(data); + break; + case Data::Type::Integer: + throw RuntimeException("anna::dbms::mysql::OutputBind::decode not implemented for Data::Type::Integer", ANNA_FILE_LOCATION); + break; + case Data::Type::ShortBlock: + throw RuntimeException("anna::dbms::mysql::OutputBind::decode not implemented for Data::Type::ShortBlock", ANNA_FILE_LOCATION); + break; + case Data::Type::LongBlock: + + try { + _this->decodeLongBlob(data); + } catch(dbms::DatabaseException& edb) { + throw RuntimeException(edb); + } + + break; + } +} + +void OutputBind::do_write(const dbms::LongBlock&) const +throw(RuntimeException, dbms::DatabaseException) { +} + +/* + * El m�todo BaseBind::setupBind asocia� el contenido de la variable + * a_time al buffer de salida de la sentencia SQL, as� que el contenido + * de la columna est� contenido ah�. S�lo tendremos que copiar dichos + * contenidos en la variable C++ de nuestro entorno. + */ +void OutputBind::decodeDate(dbms::Data& data) +throw() { + if(data.isNull() == true) + return; + + Date& date = static_cast (data); + date.setYear(a_time->year); + date.setMonth(a_time->month); + date.setDay(a_time->day); + date.setHour(a_time->hour); + date.setMinute(a_time->minute); + date.setSecond(a_time->second); + + if(data.getType() == Data::Type::TimeStamp) + static_cast (data).setFractionalSecond(a_time->second_part); +} + +/* + * Según http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html + * + * Recoge el contenido total de BLOB en partes que va componiendo sobre el + * DataBlock final asociado al anna::dbms::LongBlock sobre el que trabaja el + * programador final. + */ +void OutputBind::decodeLongBlob(dbms::Data& data) const +throw(RuntimeException, dbms::DatabaseException) { + const int bufferSize = a_blob->buffer.getMaxSize(); + st_mysql_bind& bind = a_blob->binds [a_blob->pos]; + DataBlock& target = static_cast (data).getValue(); + const int maxloop = *bind.length / bufferSize; + const int remainder = *bind.length % bufferSize; + int offset = 0; + target.clear(); + bind.buffer = (void*) a_blob->buffer.getData(); + + for(int iloop = 0; iloop < maxloop; iloop ++) { + bind.buffer_length = bufferSize; + anna_dbms_mysql_check( + mysql_stmt_fetch_column(a_blob->stmt, a_blob->binds, a_blob->pos, offset), a_blob->stmt + ); + target += DataBlock((const char*) bind.buffer, bind.buffer_length, false); + offset += bufferSize; + } + + if(remainder) { + bind.buffer_length = remainder; + anna_dbms_mysql_check( + mysql_stmt_fetch_column(a_blob->stmt, a_blob->binds, a_blob->pos, offset), a_blob->stmt + ); + target += DataBlock((const char*) bind.buffer, remainder, false); + } +} + +OutputBind::Blob::Blob() : + buffer(true) { + buffer.allocate(64 * 1024); +} diff --git a/source/dbms.mysql/ResultCode.cpp b/source/dbms.mysql/ResultCode.cpp new file mode 100644 index 0000000..c4e8f2d --- /dev/null +++ b/source/dbms.mysql/ResultCode.cpp @@ -0,0 +1,63 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include +#include + +#include +#include + +#include + +using namespace anna; +using namespace anna::dbms; + +mysql::ResultCode::ErrorDecoder mysql::ResultCode::st_errorDecoder; + +mysql::ResultCode::ResultCode(st_mysql* _mysql) : + dbms::ResultCode(0, NULL, &st_errorDecoder) { + int errorCode = mysql_errno(_mysql); + + if(errorCode != 0) + dbms::ResultCode::set(errorCode, mysql_error(_mysql)); +} + +mysql::ResultCode::ResultCode(st_mysql_stmt* stmt) : + dbms::ResultCode(0, NULL, &st_errorDecoder) { + int errorCode = mysql_stmt_errno(stmt); + + if(errorCode != 0) + dbms::ResultCode::set(errorCode, mysql_stmt_error(stmt)); +} + +/* + * Códigos de error obtenidos de: + * http://dev.mysql.com/doc/refman/4.1/en/error-messages-client.html + */ + +bool mysql::ResultCode::ErrorDecoder::notFound(const int errorCode) const +throw() { + return errorCode == CR_NO_DATA; +} + +bool mysql::ResultCode::ErrorDecoder::successful(const int errorCode) const +throw() { + return errorCode == 0; +} + +bool mysql::ResultCode::ErrorDecoder::locked(const int errorCode) const +throw() { + return false; // No parece que haya un código de error en MySQL para identificar esta situación ¿?¿? +} + +bool mysql::ResultCode::ErrorDecoder::lostConnection(const int errorCode) const +throw() { + return errorCode == CR_INVALID_CONN_HANDLE || errorCode == CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR || errorCode == CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR || + errorCode == CR_SHARED_MEMORY_CONNECT_MAP_ERROR || errorCode == CR_SHARED_MEMORY_CONNECT_SET_ERROR; +} diff --git a/source/dbms.mysql/Statement.cpp b/source/dbms.mysql/Statement.cpp new file mode 100644 index 0000000..df18acc --- /dev/null +++ b/source/dbms.mysql/Statement.cpp @@ -0,0 +1,192 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include + +#include + +using namespace std; +using namespace anna; + +dbms::mysql::Statement::~Statement() { + if(a_mysqlStmt) { + try { + anna_dbms_mysql_check(mysql_stmt_close(a_mysqlStmt), a_mysqlStmt); + } catch(DatabaseException& edb) { + edb.trace(); + } + } + + delete [] a_params; + delete [] a_results; +} + +/** + * Según el ejemplo de: http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html + * + */ +void dbms::mysql::Statement::prepare(dbms::Connection* dbmsConnection) +throw(RuntimeException, dbms::DatabaseException) { + LOGMETHOD(TraceMethod tm("anna::dbms::mysql::Statement", "prepare", ANNA_FILE_LOCATION)); + Connection* connection(static_cast (dbmsConnection)); + //Database& dbms(static_cast (connection->getDatabase())); + LOGDEBUG( + string msg("anna::dbms::mysql::Statement::prepare | "); + msg += asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + + /* + * Libera la información establecida anteriormente. Las sentencias se pueden reusar. + */ + if(a_mysqlStmt != NULL) { + anna_dbms_mysql_check(mysql_stmt_reset(a_mysqlStmt), a_mysqlStmt); + delete [] a_params; + delete [] a_results; + a_params = a_results = NULL; + } else if((a_mysqlStmt = mysql_stmt_init(*connection)) == NULL) { + string msg(asString()); + msg += " | Insufficient memory"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + const char* expression = dbms::Statement::getExpression().c_str(); + + anna_dbms_mysql_check(mysql_stmt_prepare(a_mysqlStmt, expression, anna_strlen(expression)), a_mysqlStmt); + + const int paramCount = mysql_stmt_param_count(a_mysqlStmt); + + const int inputSize = dbms::Statement::input_size(); + + const int outputSize = dbms::Statement::output_size(); + + /* + * Verifica que el número de parámetros de entrada de la sentencia coincida con el número de parámetros + * indicados por el programador. + */ + if(paramCount != inputSize) { + string msg(asString()); + msg += " | Wrong input parameters"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + /* + * Verifica que el número de parámetros de salida de la sentencia coincida con el número de parámetros + * indicados por el programador. Si no tiene parámetros de salida (INSERT) => no debe tener parámetros + * indicados por el programador. + */ + MYSQL_RES* metaResult = mysql_stmt_result_metadata(a_mysqlStmt); + + try { + if(metaResult == NULL) { + if(outputSize != 0) { + string msg(asString()); + msg += " | Wrong output parameters"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + } else if(mysql_num_fields(metaResult) != outputSize) { + string msg(asString()); + msg += " | Wrong output parameters"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + } catch(RuntimeException&) { + mysql_free_result(metaResult); + throw; + } + + /* + * Define las estructuras requeridas para asociar las columasn MySQL con las áreas de memoria C++ + */ + int pos; + + if((a_params = create(inputSize, "input")) != NULL) { + pos = 0; + + for(input_iterator ii = input_begin(), maxii = input_end(); ii != maxii; ii ++) + inputBind(ii)->prepare(this, dbmsConnection, pos ++); + + anna_dbms_mysql_check(mysql_stmt_bind_param(a_mysqlStmt, a_params), a_mysqlStmt); + } + + if((a_results = create(outputSize, "output")) != NULL) { + pos = 0; + + for(output_iterator oo = output_begin(), maxoo = output_end(); oo != maxoo; oo ++) + outputBind(oo)->prepare(this, dbmsConnection, pos ++); + + anna_dbms_mysql_check(mysql_stmt_bind_result(a_mysqlStmt, a_results), a_mysqlStmt); + } +} + +dbms::ResultCode dbms::mysql::Statement::execute(dbms::Connection* dbmsConnection) +throw(RuntimeException, dbms::DatabaseException) { + //Connection* connection(static_cast (dbmsConnection)); + + for(input_iterator ii = input_begin(), maxii = input_end(); ii != maxii; ii ++) { + inputBind(ii)->code(); + LOGDEBUG( + string msg("anna::dbms::mysql::Statement::InputBind: "); + msg += inputBind(ii)->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + } + + anna_dbms_mysql_check(mysql_stmt_execute(a_mysqlStmt), a_mysqlStmt) + ResultCode result(a_mysqlStmt); + return result; +} + +/* + * Según la información de http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html + */ +bool dbms::mysql::Statement::fetch() +throw(RuntimeException, dbms::DatabaseException) { + bool result = false; + + switch(mysql_stmt_fetch(a_mysqlStmt)) { + case 0: + result = true; + + for(output_iterator oo = output_begin(), maxoo = output_end(); oo != maxoo; oo ++) { + outputBind(oo)->decode(); + LOGDEBUG(Logger::write(Logger::Debug, outputBind(oo)->asString(), ANNA_FILE_LOCATION)); + } + + break; + case 1: + throw DatabaseException(ResultCode(a_mysqlStmt), ANNA_FILE_LOCATION); + default: + result = false; + break; + } + + LOGDEBUG( + string msg("anna::dbms::mysql::Statement::fetch | Result: "); + msg += functions::asString(result); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + return result; +} + +st_mysql_bind* dbms::mysql::Statement::create(const int size, const char* whatis) +throw(RuntimeException) { + st_mysql_bind* result = NULL; + + if(size > 0) { + if((result = new st_mysql_bind [size]) == NULL) { + string msg(asString()); + msg += anna::functions::asString("Insufficient memory to create %d parameters of %s", size, whatis); + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + } + + return result; +} diff --git a/source/dbms.mysql/internal/sccs.cpp b/source/dbms.mysql/internal/sccs.cpp new file mode 100644 index 0000000..5317de4 --- /dev/null +++ b/source/dbms.mysql/internal/sccs.cpp @@ -0,0 +1,22 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include +#include + +#include + +anna_define_sccs_tag_ex(dbms_mysql, dbms.mysql, 0); + +void anna::dbms::mysql::sccs::activate() +throw() { + dbms::sccs::activate(); + ModuleManager::instantiate().insert(anna_use_sccs_tag(dbms_mysql), "00"); +} + diff --git a/source/dbms.oracle/BaseBind.cpp b/source/dbms.oracle/BaseBind.cpp new file mode 100644 index 0000000..4eb7b75 --- /dev/null +++ b/source/dbms.oracle/BaseBind.cpp @@ -0,0 +1,107 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace anna; + +//---------------------------------------------------------------------------- +// (1) Reserva 2 Kbytes para trabajar con el LOB. +//---------------------------------------------------------------------------- +dbms::oracle::BaseBind::BaseBind(const dbms::Data& data) : + a_type(data.getType()), + a_ofb(NULL) { + switch(a_type) { + case Data::Type::Float: + a_ofb = new anna::DataBlock(true); + a_ofb->allocate(FloatSize + 1); + break; + case Data::Type::ShortBlock: + a_ofb = new anna::DataBlock(true); + a_ofb->allocate(data.getMaxSize() * 2 + 1); + break; + case Data::Type::LongBlock: + a_ofb = new anna::DataBlock(true); + a_ofb->allocate(2048); // (1) + break; + default: break; + } +} + +dbms::oracle::BaseBind::~BaseBind() { + delete a_ofb; +} + +// +// (1) Aunque sea un objeto de tipo Date lo define/trata como un tipo TimeStamp, porque de otro modo no se grabaria la +// informacion referente a la hora. +// (2) El Float hasta ahora se trataba como un tipo especial de cadena, pero no es un tratamiento indicado +// para todos los gestores de base de datos, as� que vamos a resumir en estas clases todos los detalles de +// tratamiento. +// +dbms::oracle::BaseBind::oci_param dbms::oracle::BaseBind::getOCIParam(dbms::oracle::Database& database, dbms::oracle::Connection* connection, dbms::Data& data) +throw(RuntimeException) { + oci_param ociparam; + + switch(a_type) { + case Data::Type::Integer: + ociparam.type = SQLT_INT; + ociparam.maxLength = data.getMaxSize(); + ociparam.length = NULL; + ociparam.buffer = const_cast (data).getBuffer(); + break; + case Data::Type::String: + ociparam.type = SQLT_STR; + ociparam.maxLength = data.getMaxSize() + 1; + ociparam.length = NULL; + ociparam.buffer = const_cast (data).getBuffer(); + break; + case Data::Type::Float: // (2) + ociparam.type = SQLT_STR; + ociparam.maxLength = FloatSize + 1; + ociparam.length = NULL; + ociparam.buffer = const_cast (a_ofb->getData()); + break; + case Data::Type::ShortBlock: + ociparam.type = SQLT_STR; + ociparam.maxLength = data.getMaxSize() * 2 + 1; + ociparam.length = &a_length; + ociparam.buffer = const_cast (a_ofb->getData()); + break; + case Data::Type::LongBlock: + a_blob.allocate(database, connection, OCI_DTYPE_LOB); + ociparam.type = SQLT_BLOB; + ociparam.buffer = &a_blob.handle; + ociparam.length = NULL; + ociparam.maxLength = 0; + break; + case Data::Type::Date: // (1) + case Data::Type::TimeStamp: + a_datetime.allocate(database, connection, OCI_DTYPE_TIMESTAMP); + ociparam.type = SQLT_TIMESTAMP; + ociparam.buffer = &a_datetime.handle; + ociparam.length = NULL; + ociparam.maxLength = 0; + break; + default: + throw RuntimeException(functions::asString("Unsupported data type %d", (int) a_type), ANNA_FILE_LOCATION); + } + + return ociparam; +} + diff --git a/source/dbms.oracle/Connection.cpp b/source/dbms.oracle/Connection.cpp new file mode 100644 index 0000000..b3cea7a --- /dev/null +++ b/source/dbms.oracle/Connection.cpp @@ -0,0 +1,147 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +oracle::Connection::Connection(Database& database, const std::string& name, const char* user, const char* password) : + dbms::Connection(database, name, user, password), + a_context(NULL), + a_session(NULL), + a_server(NULL), + a_oracleDatabase(database) { +} + +void oracle::Connection::open() +throw(dbms::DatabaseException) { + OCIError* error = a_oracleDatabase.getErrorHandler(); + + if(a_context != NULL) { + LOGWARNING( + string msg = asString(); + msg += " | Already established"; + Logger::warning(msg, ANNA_FILE_LOCATION); + ); + return; + } + + try { + anna_dbms_oracle_check(OCIHandleAlloc(a_oracleDatabase, (void**) &a_context, OCI_HTYPE_SVCCTX, 0, 0), error); + anna_dbms_oracle_check(OCIHandleAlloc(a_oracleDatabase, (void**) &a_session, OCI_HTYPE_SESSION, 0, 0), error); + anna_dbms_oracle_check(OCIHandleAlloc(a_oracleDatabase, (void**) &a_server, OCI_HTYPE_SERVER, 0, 0), error); + const char* dbmsName = NULL; + int lenDBName = 0; + + if(a_oracleDatabase.getType() == Database::Type::Remote) { + dbmsName = a_oracleDatabase.getName().c_str(); + lenDBName = anna_strlen(dbmsName); + } + + anna_dbms_oracle_check( + OCIServerAttach(a_server, error, (unsigned char*) dbmsName, lenDBName, OCI_DEFAULT), + error + ); + anna_dbms_oracle_check(OCIAttrSet(a_context, OCI_HTYPE_SVCCTX, a_server, 0, OCI_ATTR_SERVER, error), error); + anna_dbms_oracle_check( + OCIAttrSet(a_session, OCI_HTYPE_SESSION, (void*) a_user.c_str(), a_user.size(), OCI_ATTR_USERNAME, error), + error + ); + anna_dbms_oracle_check( + OCIAttrSet(a_session, OCI_HTYPE_SESSION, (void*) a_password.c_str(), a_password.size(), OCI_ATTR_PASSWORD, error), + error + ); + anna_dbms_oracle_check(OCISessionBegin(a_context, error, a_session, OCI_CRED_RDBMS, OCI_DEFAULT), error); + anna_dbms_oracle_check(OCIAttrSet(a_context, OCI_HTYPE_SVCCTX, a_session, 0, OCI_ATTR_SESSION, error), error); + LOGINFORMATION( + string msg("anna::dbms::oracle::Connection::open | "); + msg += asString(); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + } catch(DatabaseException& edbms) { + close(); + throw; + } +} + +void oracle::Connection::close() +throw() { + OCIError* error = a_oracleDatabase.getErrorHandler(); + + try { + LOGINFORMATION( + string msg("anna::dbms::oracle::Connection::close | "); + msg += asString(); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + + if(a_session != NULL && a_context != NULL) { + OCISessionEnd(a_context, error, a_session, OCI_DEFAULT); + a_session = NULL; + } + + if(a_server != NULL) { + OCIServerDetach(a_server, error, OCI_DEFAULT); + OCIHandleFree(a_server, OCI_HTYPE_SERVER); + a_server = NULL; + } + + if(a_context != NULL) { + OCIHandleFree(a_context, OCI_HTYPE_SVCCTX); + a_context = NULL; + } + + LOGINFORMATION( + string msg("anna::dbms::oracle::Connection::close | "); + msg += asString(); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + } catch(DatabaseException& ex) { + ex.trace(); + } +} + +void oracle::Connection::do_commit() +throw(RuntimeException, dbms::DatabaseException) { + OCIError* error = a_oracleDatabase.getErrorHandler(); + anna_dbms_oracle_check(OCITransCommit(a_context, error, 0), error); +} + +void oracle::Connection::do_rollback() +throw() { + try { + OCIError* error = a_oracleDatabase.getErrorHandler(); + anna_dbms_oracle_check(OCITransRollback(a_context, error, 0), error); + } catch(Exception& ex) { + ex.trace(); + } +} + +string oracle::Connection::asString() const +throw() { + string result("dbms::oracle::Connection { "); + result += dbms::Connection::asString(); + result += " | Context: "; + result += (a_context == NULL) ? "(null)" : functions::asHexString(anna_ptrnumber_cast(a_context)); + result += " | Session: "; + result += (a_session == NULL) ? "(null)" : functions::asHexString(anna_ptrnumber_cast(a_session)); + result += " | Server: "; + result += (a_server == NULL) ? "(null)" : functions::asHexString(anna_ptrnumber_cast(a_server)); + return result += " }"; +} + diff --git a/source/dbms.oracle/Database.cpp b/source/dbms.oracle/Database.cpp new file mode 100644 index 0000000..a90785e --- /dev/null +++ b/source/dbms.oracle/Database.cpp @@ -0,0 +1,139 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +char oracle::Database::st_decimalPoint = 0; + +oracle::Database::Database(const char* dbmsName) : + dbms::Database(getClassName(), dbmsName), + a_env(NULL), + a_error(NULL) { + oracle::sccs::activate(); +} + +oracle::Database::Database(const char* componentName, const char* dbmsName) : + dbms::Database(componentName, dbmsName), + a_env(NULL), + a_error(NULL) { + oracle::sccs::activate(); +} + +void oracle::Database::do_initialize() +throw(RuntimeException) { + LOGMETHOD(TraceMethod tm("anna::dbms::oracle::Database", "do_initialize", ANNA_FILE_LOCATION)); + + if(a_env != NULL) { + Logger::write(Logger::Warning, asString(), "Already initialized", ANNA_FILE_LOCATION); + return; + } + + if(OCIInitialize(OCI_DEFAULT, 0, 0, 0, 0) != OCI_SUCCESS) { + string msg(asString()); + msg += " | Cannot initialize Oracle access system"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(OCIEnvInit(&a_env, OCI_DEFAULT, 0, 0) != OCI_SUCCESS) { + string msg(asString()); + msg += " | Cannot create database environment"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(OCIHandleAlloc(a_env, (void**) &a_error, OCI_HTYPE_ERROR, 0, 0) != OCI_SUCCESS) { + string msg(asString()); + msg += " | Cannot create database error handler"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + WHEN_MULTITHREAD( + OCIThreadProcessInit(); + anna_dbms_oracle_check(OCIThreadInit(a_env, a_error), a_error); + ); + initializeDecimalPoint(); + dbms::Database::do_initialize(); +} + +//---------------------------------------------------------------------------------- +// Libera todos los manejadores asociados a �te entorno. +//---------------------------------------------------------------------------------- +oracle::Database::~Database() { + LOGMETHOD(TraceMethod tm("anna::dbms::oracle::Database", "~Database", ANNA_FILE_LOCATION)); + + if(a_error) { + OCIHandleFree(a_env, OCI_HTYPE_ERROR); + a_env = NULL; + } + + if(a_env) { + OCIHandleFree(a_env, OCI_HTYPE_ENV); + a_env = NULL; + } +} + +dbms::Connection* oracle::Database::allocateConnection(const std::string& name, const char* user, const char* password) +throw(RuntimeException) { + return new Connection(*this, name, user, password); +} + +dbms::Statement* oracle::Database::allocateStatement(const char* name, const std::string& expression, const bool isCritical) +throw(RuntimeException) { + return new Statement(*this, name, expression, isCritical); +} + +dbms::InputBind* oracle::Database::allocateInputBind(const char* name, Data& data) +throw(RuntimeException) { + return new InputBind(name, data); +} + +void oracle::Database::deallocate(dbms::InputBind* inputBind) +throw() { + delete(InputBind*) inputBind; +} + +dbms::OutputBind* oracle::Database::allocateOutputBind(const char* name, Data& data) +throw(RuntimeException) { + return new OutputBind(name, data); +} + +void oracle::Database::deallocate(dbms::OutputBind* outputBind) +throw() { + delete(OutputBind*) outputBind; +} + +/** + * Ojo no se activa el uso de forma definitiva porque afectaría a todo el programa, cualquier + * conversion texto -> float/double que se hiciera se vería afectado por este cambio, que sólo debería + * aplicar a temas relaciones con la base de datos. + * + * Carga el valor del LC_NUMERIC de la correspondiente variable del entorno y luego repone el + * usado por defecto en las librerias del core de C. + */ +/*static*/ +void oracle::Database::initializeDecimalPoint() +throw(RuntimeException) { + setlocale(LC_NUMERIC, ""); + struct lconv *locale = localeconv(); + + if(*locale->decimal_point != '.') + st_decimalPoint = *locale->decimal_point; + + setlocale(LC_NUMERIC, "C"); +} diff --git a/source/dbms.oracle/Descriptor.cpp b/source/dbms.oracle/Descriptor.cpp new file mode 100644 index 0000000..b7e5011 --- /dev/null +++ b/source/dbms.oracle/Descriptor.cpp @@ -0,0 +1,39 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include +#include + +using namespace anna; + +dbms::oracle::Descriptor::~Descriptor() { + if(*reference != NULL) { + OCIDescriptorFree(*reference, type); + *reference = NULL; + } +} + +void dbms::oracle::Descriptor::allocate(dbms::oracle::Database& database, dbms::oracle::Connection* connection, const int _type) +throw(RuntimeException) { + if(*reference != NULL) + return; + + env = database; + error = database.getErrorHandler(); + context = *connection; // Invoca al operador de conversion + + try { + anna_dbms_oracle_check(OCIDescriptorAlloc(env, reference, type = _type, 0, 0), error); + } catch(DatabaseException& edb) { + throw RuntimeException(edb); + } +} diff --git a/source/dbms.oracle/InputBind.cpp b/source/dbms.oracle/InputBind.cpp new file mode 100644 index 0000000..c554843 --- /dev/null +++ b/source/dbms.oracle/InputBind.cpp @@ -0,0 +1,208 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace anna; + +InputBind::InputBind(const char* name, dbms::Data& data) : + dbms::InputBind(name, data), + BaseBind(data), + a_ociBind(NULL) { +} + +InputBind::~InputBind() { +} + +// Slo se invoca una vez. Establece las variables atrav� de las que nos vamos a poder +// comunicar con Oracle, para indicar la longitud de una variable, o su estado de nulo o +// no nulo. +void InputBind::prepare(dbms::Statement* dbmsStatement, dbms::Connection* connection, const int pos) +throw(RuntimeException, dbms::DatabaseException) { + if(a_ociBind != NULL) + return; + + Data& data = anna::dbms::Bind::getData(); + + if(data.getType() == Data::Type::LongBlock) { + string msg("anna::dbms::oracle::InputBind::prepare | "); + msg += data.asString(); + msg += " | This RDBMS doesn't support BLOB type as BindInput (see anna::dbms::OutputBind::write)"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + Database& database = static_cast (dbmsStatement->getDatabase()); + OCIError* error = database.getErrorHandler(); + Statement* statement(static_cast (dbmsStatement)); + oci_param ociparam = getOCIParam(database, static_cast (connection), data); + + if(data.isNulleable() == false) { + anna_dbms_oracle_check( + OCIBindByPos( + *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type, + 0, ociparam.length, 0, 0, 0, OCI_DEFAULT + ), + error + ); + } else { + anna_dbms_oracle_check( + OCIBindByPos( + *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type, + &a_nullIndicator, ociparam.length, 0, 0, 0, OCI_DEFAULT + ), + error + ); + } + + LOGDEBUG( + std::string msg("anna::dbms::oracle::InputBind::prepare | "); + msg += asString(); + msg += " | Sentence: "; + msg += statement->getName(); + msg += " | Position: "; + msg += functions::asString(pos); + Logger::debug(msg, ANNA_FILE_LOCATION) + ); +} + +//------------------------------------------------------------------------------- +// Establece la informacin mediante la que conectamos con Oracle. Todos los +// par�etros que modificamos en �te m�odo tienen efecto en la llamada a Oracle +// debido cmo hemos invocamo al m�odo OCIBindByPos. +// +// Todo esto se podr� haber hecho en la anna::dbms::DataBlock pero exigir� +// definir una clase distinta para cada RDBMS. Creo que los Binds particulares de +// cada base de datos se ocupen toda la complejidad de convertir los datos. +//------------------------------------------------------------------------------- +void InputBind::code() const +throw(RuntimeException) { + InputBind* _this = const_cast (this); + Data& data = _this->getData(); + + if((_this->a_nullIndicator = data.isNull() ? -1 : 0) == -1) + return; + + switch(data.getType()) { + case Data::Type::String: + throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::String", ANNA_FILE_LOCATION); + break; + case Data::Type::Integer: + throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::Integer", ANNA_FILE_LOCATION); + break; + case Data::Type::Float: + codeFloat(data); + break; + case Data::Type::ShortBlock: + codeShortBlock(data); + break; + case Data::Type::LongBlock: + throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::LongBlock", ANNA_FILE_LOCATION); + break; + case Data::Type::Date: + case Data::Type::TimeStamp: + + try { + codeDate(data); + } catch(DatabaseException& edb) { + throw RuntimeException(edb); + } + + break; + } +} + +/** + * Transfiere el valor numerico del float, al buffer reservado para + * ubiucarlo como una cadena. Éste buffer es el que está "conectado" con + * Oracle (tm). + */ +void InputBind::codeFloat(dbms::Data& data) const +throw() { + dbms::Float& _float = static_cast (data); + InputBind* _this = const_cast (this); + char* buffer = (char*) _this->a_ofb->getData(); + snprintf(buffer, FloatSize, _float.getFormat(), _float.getValue()); + const char decimalPoint = oracle::Database::getDecimalPoint(); + + if(decimalPoint != 0) { + char* point = anna_strchr(buffer, '.'); + + if(point != NULL) + *point = decimalPoint; + } +} + +void InputBind::codeShortBlock(dbms::Data& data) const +throw() { + const int length = static_cast (data).getSize(); + InputBind* _this = const_cast (this); + + if(length == 0) { + _this->a_ofb->clear(); + _this->a_length = 0; + return; + } + + const char* src = (const char*) data.getBuffer(); + + char* dest = const_cast (a_ofb->getData()); + + int j = 0; + + for(int i = 0; i < length; i ++) { + dest [j ++] = asCharacter((src [i] & 0xf0) >> 4); + dest [j ++] = asCharacter(src [i] & 0x0f); + } + + dest [j ++] = 0; + _this->a_length = j; +} + +void InputBind::codeDate(dbms::Data& data) const +throw(RuntimeException, dbms::DatabaseException) { + dbms::Date& date = static_cast (data); + ub4 fsec(0); + + if(data.getType() == Data::Type::TimeStamp) + fsec = static_cast (data).getFractionalSecond() * 1000; + + anna_dbms_oracle_check( + OCIDateTimeConstruct( + a_datetime.env, a_datetime.error, a_datetime.handle, + date.getYear(), date.getMonth(), date.getDay(), date.getHour(), date.getMinute(), date.getSecond(), fsec, NULL, 0 + ), + a_datetime.error + ); + ub4 errorMask(0); + anna_dbms_oracle_check( + OCIDateTimeCheck(a_datetime.env, a_datetime.error, a_datetime.handle, &errorMask), + a_datetime.error + ); + + if(errorMask != 0) { + string msg(data.asString()); + msg += anna::functions::asHexText(" | Invalid date | ErrorCode: ", (int) errorMask); + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } +} diff --git a/source/dbms.oracle/OutputBind.cpp b/source/dbms.oracle/OutputBind.cpp new file mode 100644 index 0000000..9b18d68 --- /dev/null +++ b/source/dbms.oracle/OutputBind.cpp @@ -0,0 +1,278 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace anna; +using namespace std; + +OutputBind::OutputBind(const char* name, dbms::Data& data) : + dbms::OutputBind(name, data), + BaseBind(data), + a_ociDefine(NULL) { +} + +OutputBind::~OutputBind() { +} + +void OutputBind::prepare(dbms::Statement* dbmsStatement, dbms::Connection* connection, const int pos) +throw(dbms::DatabaseException) { + if(a_ociDefine != NULL) + return; + + Database& database(static_cast (dbmsStatement->getDatabase())); + OCIError* error = database.getErrorHandler(); + Statement* statement(static_cast (dbmsStatement)); + dbms::Data& data = anna::dbms::Bind::getData(); + oci_param ociparam = getOCIParam(database, static_cast (connection), data); + + if(data.isNulleable() == false) { + anna_dbms_oracle_check( + OCIDefineByPos( + *statement, &a_ociDefine, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type, + 0, ociparam.length, 0, OCI_DEFAULT + ), + error + ); + } else { + anna_dbms_oracle_check( + OCIDefineByPos( + *statement, &a_ociDefine, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type, + &a_nullIndicator, ociparam.length, 0, OCI_DEFAULT + ), + error + ); + } + + LOGDEBUG( + std::string msg("anna::dbms::oracle::OutputBind::prepare | "); + msg += asString(); + msg += " | Sentence: "; + msg += statement->getName(); + msg += " | Position: "; + msg += functions::asString(pos); + Logger::debug(msg, ANNA_FILE_LOCATION) + ); +} + +//------------------------------------------------------------------------------- +// Transfiere la informacin obtenida desde Oracle. Todos los comprobados en �te +// m�odo son parametros que modificados al ejecuta la sentencia Oracle debido cmo +// hemos invocamo al m�odo OCIDefineByPos +// +// Todo esto se podr� haber hecho en la anna::dbms::DataBlock pero exigir� +// definir una clase distinta para cada RDBMS. Creo que los Binds particulares de +// cada base de datos se ocupen toda la complejidad de convertir los datos. +// +// (1) Truco para fijar el contenido y la longitud actual. +//------------------------------------------------------------------------------- +void OutputBind::decode() const +throw(RuntimeException) { + OutputBind* _this = const_cast (this); + char* str; + Data& data = _this->getData(); + data.setNull(a_nullIndicator < 0); + + switch(data.getType()) { + case Data::Type::String: + str = (char*) data.getBuffer(); + + if(data.isNull() == true) + *str = 0; + else + dbms::String::strip(str); + + break; + case Data::Type::Integer: + throw RuntimeException("anna::dbms::oracle::OutputBind::decode not implemented for Data::Type::Integer", ANNA_FILE_LOCATION); + break; + case Data::Type::Float: + decodeFloat(data); + break; + case Data::Type::ShortBlock: + decodeShortBlock(data); + break; + case Data::Type::LongBlock: + decodeLongBlock(data); + break; + case Data::Type::Date: + case Data::Type::TimeStamp: + + try { + decodeDate(data); + } catch(DatabaseException& edb) { + throw RuntimeException(edb); + } + + break; + } +} + +void OutputBind::decodeFloat(dbms::Data& data) const +throw(RuntimeException) { + dbms::Float& _float = static_cast (data); + + if(data.isNull() == true) { + _float = 0.0; + return; + } + + char* _data = (char*) a_ofb->getData(); + const char decimalPoint = oracle::Database::getDecimalPoint(); + + if(decimalPoint != 0) { + char* point = anna_strchr(_data, decimalPoint); + + if(point != NULL) + *point = '.'; + } + + sscanf(_data, _float.getFormat(), (float*) _float.getBuffer()); +} + +void OutputBind::decodeShortBlock(dbms::Data& data) const +throw(RuntimeException) { + const anna::DataBlock& constdbms(static_cast (data).getValue()); + anna::DataBlock& dataBlock(const_cast (constdbms)); + + if(data.isNull() == true) { + dataBlock.clear(); + return; + } + + const char* src = a_ofb->getData(); + + char* dest = (char*) dataBlock.getData(); + + unsigned char hex; + + int j = 0; + + for(int i = 1; i < a_length; i += 2, j ++) { + hex = asByte(src [i - 1]) << 4; + hex |= asByte(src [i]); + dest [j] = hex; + } + + dataBlock.clear(); + dataBlock.allocate(j); // (1) +} + +//-------------------------------------------------------------------------------------------- +// (1) Offset = 1 => primer caracter. +//-------------------------------------------------------------------------------------------- +void OutputBind::decodeLongBlock(dbms::Data& data) const +throw(RuntimeException) { + const anna::DataBlock& constdbms(static_cast (data).getValue()); + anna::DataBlock& dataBlock(const_cast (constdbms)); + dataBlock.clear(); + + if(data.isNull() == true) + return; + + ub1* buffer; + ub4 maxLength; + ub4 length; + sword ret; + bool stop = false; + ub4 offset = 1; // (1) + + try { + buffer = (ub1*) a_ofb->getData(); + maxLength = a_ofb->getSize(); + length = 0; + + do { + ret = OCILobRead(a_blob.context, a_blob.error, a_blob.handle, &length, offset, buffer, maxLength, 0, 0, 0, SQLCS_IMPLICIT); + + switch(ret) { + case OCI_SUCCESS: + dataBlock += anna::DataBlock((const char*) buffer, length, false); + stop = true; + break; + case OCI_NEED_DATA: + dataBlock += anna::DataBlock((const char*) buffer, length, false); + offset += length; + break; + default: + throw dbms::DatabaseException(oracle::ResultCode(ret, a_blob.error), ANNA_FILE_LOCATION); + } + } while(stop == false); + } catch(dbms::DatabaseException& edbms) { + throw RuntimeException(edbms); + } +} + +void OutputBind::do_write(const dbms::LongBlock& data) const +throw(RuntimeException, dbms::DatabaseException) { + const anna::DataBlock& dataBlock = data.getValue(); + + if(a_blob.handle == NULL) { + string msg("anna::dbms::oracle::OutputBind::do_write | "); + msg += data.asString(); + msg += " | BLOB must be loaded before modification"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + ub1* buffer = (ub1*) dataBlock.getData(); + ub4 length = dataBlock.getSize(); + anna_dbms_oracle_check( + OCILobWrite(a_blob.context, a_blob.error, a_blob.handle, &length, (ub4) 1, buffer, length, OCI_ONE_PIECE, 0, 0, 0, SQLCS_IMPLICIT), + a_blob.error + ); +} + +void OutputBind::decodeDate(dbms::Data& data) const +throw(RuntimeException, dbms::DatabaseException) { + if(data.isNull() == true) + return; + + Date& date = static_cast (data); + sb2 year; + ub1 month, day; + anna_dbms_oracle_check( + OCIDateTimeGetDate(a_datetime.env, a_datetime.error, a_datetime.handle, &year, &month, &day), + a_datetime.error + ); + date.setYear(year); + date.setMonth(month); + date.setDay(day); + ub1 hour, min, sec; + ub4 fsec; + sword status = OCIDateTimeGetTime(a_datetime.env, a_datetime.error, a_datetime.handle, &hour, &min, &sec, &fsec); + + if(status == OCI_SUCCESS) { + date.setHour(hour); + date.setMinute(min); + date.setSecond(sec); + + if(data.getType() == Data::Type::TimeStamp) + static_cast (data).setFractionalSecond(fsec / 1000); + } else { + date.setHour(0); + date.setMinute(0); + date.setSecond(0); + + if(data.getType() == Data::Type::TimeStamp) + static_cast (data).setFractionalSecond(0); + } +} diff --git a/source/dbms.oracle/ResultCode.cpp b/source/dbms.oracle/ResultCode.cpp new file mode 100644 index 0000000..d9a2200 --- /dev/null +++ b/source/dbms.oracle/ResultCode.cpp @@ -0,0 +1,65 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include + +#include +#include + +#include + +using namespace anna; +using namespace anna::dbms; + +oracle::ResultCode::ErrorDecoder oracle::ResultCode::st_errorDecoder; + +oracle::ResultCode::ResultCode(const int status, OCIError* error) : + dbms::ResultCode(0, NULL, &st_errorDecoder) { + char errorText [dbms::ResultCode::MaxErrorLen]; + int errorCode = status; + + if(status == OCI_SUCCESS) { + dbms::ResultCode::set(OCI_SUCCESS, NULL); + return; + } else if(status == OCI_SUCCESS_WITH_INFO) { + OCIErrorGet(error, (ub4) 1, (text*) 0, &errorCode, (unsigned char*) errorText, (ub4) sizeof(errorText), OCI_HTYPE_ERROR); + dbms::ResultCode::set(OCI_SUCCESS, errorText); + LOGINFORMATION(Logger::information(asString(), ANNA_FILE_LOCATION)); + return; + } + + switch(status) { + case OCI_ERROR: + OCIErrorGet(error, (ub4) 1, (text*) 0, &errorCode, (unsigned char*) errorText, (ub4) sizeof(errorText), OCI_HTYPE_ERROR); + break; + case OCI_NEED_DATA: anna_strcpy(errorText, "OCI | Need data"); break; + case OCI_NO_DATA: anna_strcpy(errorText, "OCI | Data not found"); break; + case OCI_INVALID_HANDLE: anna_strcpy(errorText, "OCI | Invalid handle"); break; + default: sprintf(errorText, "OCI | Error code: %d", status); break; + } + + dbms::ResultCode::set(errorCode, errorText); +} + +bool oracle::ResultCode::ErrorDecoder::notFound(const int errorCode) const +throw() { + return errorCode == OCI_NO_DATA; +} + +bool oracle::ResultCode::ErrorDecoder::successful(const int errorCode) const +throw() { + return errorCode == OCI_SUCCESS; +} + +bool oracle::ResultCode::ErrorDecoder::lostConnection(const int errorCode) const +throw() { + return errorCode == 28 || errorCode == 3113 || errorCode == 3114 || errorCode == 1012 || errorCode == 12570 || errorCode == 12571; +} diff --git a/source/dbms.oracle/Statement.cpp b/source/dbms.oracle/Statement.cpp new file mode 100644 index 0000000..1cf6e8a --- /dev/null +++ b/source/dbms.oracle/Statement.cpp @@ -0,0 +1,128 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include + +#include + +using namespace std; +using namespace anna; + +dbms::oracle::Statement::~Statement() { + if(a_ociStmt) + OCIHandleFree(a_ociStmt, OCI_HTYPE_STMT); +} + +void dbms::oracle::Statement::prepare(dbms::Connection* dbmsConnection) +throw(RuntimeException, dbms::DatabaseException) { + LOGMETHOD(TraceMethod tm("anna::dbms::oracle::Statement", "prepare", ANNA_FILE_LOCATION)); + Connection* connection(static_cast (dbmsConnection)); + Database& dbms(static_cast (connection->getDatabase())); + a_ociError = dbms.getErrorHandler(); + + if(a_ociStmt != NULL) { + anna_dbms_oracle_check(OCIHandleFree(a_ociStmt, OCI_HTYPE_STMT), a_ociError); + a_ociStmt = NULL; + } + + const char* expression = dbms::oracle::Statement::getExpression().c_str(); + + anna_dbms_oracle_check(OCIHandleAlloc(dbms, (void**) &a_ociStmt, OCI_HTYPE_STMT, 0, 0), a_ociError); + + anna_dbms_oracle_check( + OCIStmtPrepare(a_ociStmt, a_ociError, (text*) expression, anna_strlen(expression), OCI_NTV_SYNTAX, OCI_DEFAULT), + a_ociError + ); + + int pos = 1; + + for(input_iterator ii = input_begin(), maxii = input_end(); ii != maxii; ii ++) + inputBind(ii)->prepare(this, dbmsConnection, pos ++); + + pos = 1; + + for(output_iterator oo = output_begin(), maxoo = output_end(); oo != maxoo; oo ++) + outputBind(oo)->prepare(this, dbmsConnection, pos ++); +} + +dbms::ResultCode dbms::oracle::Statement::execute(dbms::Connection* dbmsConnection) +throw(RuntimeException, dbms::DatabaseException) { + Connection* connection(static_cast (dbmsConnection)); + + for(input_iterator ii = input_begin(), maxii = input_end(); ii != maxii; ii ++) { + inputBind(ii)->code(); + LOGDEBUG( + string msg("anna::dbms::oracle::Statement::InputBind: "); + msg += inputBind(ii)->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + } + + const sword status = OCIStmtExecute(*connection, a_ociStmt, a_ociError, 1, 0, NULL, NULL, OCI_DEFAULT); + + a_firstFetch = false; + + ResultCode result(status, a_ociError); + + if(result.successful() == true && result.notFound() == false) { + for(output_iterator oo = output_begin(), maxoo = output_end(); oo != maxoo; oo ++) { + outputBind(oo)->decode(); + LOGDEBUG( + string msg("anna::dbms::oracle::Statement::OutputBind: "); + msg += outputBind(oo)->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + } + + a_firstFetch = true; + } + + return result; +} + +//------------------------------------------------------------------------------------------------- +// (1) Si es una consulta de seleccin, entonces, nada m� ejecutar la sentencia el primer registro +// encontrado ya est�cargado en las variables de salida, para obtener los siguientes hay que invocar +// a fetch. Vamos a hacer este esquema m� gen�ico de forma que siempre habr�que invocar a +// 'fetch' para obtener los datos, pero en Oracle, la primera llamada no har�nada. +//------------------------------------------------------------------------------------------------- +bool dbms::oracle::Statement::fetch() +throw(RuntimeException, dbms::DatabaseException) { + bool result; + + if(a_firstFetch == true) { // (1) + a_firstFetch = false; + result = true; + } else { + ResultCode resultCode(OCIStmtFetch(a_ociStmt, a_ociError, 1, OCI_FETCH_NEXT, OCI_DEFAULT), a_ociError); + result = resultCode.successful(); + + if(result == false && resultCode.notFound() == false) + Logger::write(Logger::Error, asString(), resultCode.asString(), ANNA_FILE_LOCATION); + + if(result == true) { + for(output_iterator oo = output_begin(), maxoo = output_end(); oo != maxoo; oo ++) { + outputBind(oo)->decode(); + LOGDEBUG(Logger::write(Logger::Debug, outputBind(oo)->asString(), ANNA_FILE_LOCATION)); + } + } + } + + LOGDEBUG( + string msg("anna::dbms::oracle::Statement::fetch | Result: "); + msg += functions::asString(result); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + return result; +} + + + diff --git a/source/dbms.oracle/internal/sccs.cpp b/source/dbms.oracle/internal/sccs.cpp new file mode 100644 index 0000000..aa19165 --- /dev/null +++ b/source/dbms.oracle/internal/sccs.cpp @@ -0,0 +1,22 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include +#include + +#include + +anna_define_sccs_tag_ex(dbms_oracle, dbms.oracle, 1); + +void anna::dbms::oracle::sccs::activate() +throw() { + dbms::sccs::activate(); + ModuleManager::instantiate().insert(anna_use_sccs_tag(dbms_oracle), "00"); +} + diff --git a/source/dbms/Bind.cpp b/source/dbms/Bind.cpp new file mode 100644 index 0000000..b54cdbe --- /dev/null +++ b/source/dbms/Bind.cpp @@ -0,0 +1,23 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +using namespace anna; + +std::string dbms::Bind::asString() const +throw() { + std::string result("dbms::Bind { Name: "); + result += a_name; + result += " | "; + result += a_data.asString(); + return result += " }"; +} + + diff --git a/source/dbms/Connection.cpp b/source/dbms/Connection.cpp new file mode 100644 index 0000000..9dcdcb5 --- /dev/null +++ b/source/dbms/Connection.cpp @@ -0,0 +1,230 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace anna; + +//----------------------------------------------------------------------------------------------------------- +// (1) Si no tiene variables de salida => consideramos que es un update, insert o delete. +//----------------------------------------------------------------------------------------------------------- +dbms::ResultCode dbms::Connection::execute(Statement* statement) +throw(RuntimeException, dbms::DatabaseException) { + if(statement == NULL) { + string msg(asString()); + msg += " | Cannot execute a NULL sentence"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGMETHOD(TraceMethod ttmm("dbms::Connection", "execute", ANNA_FILE_LOCATION)); + LOGDEBUG( + string msg("Using Connection | "); + msg += asString(); + Logger::debug(msg, ANNA_FILE_LOCATION) + ); + Guard guard(statement, "Statement from dbms::Connection::execute"); + const Microsecond init = functions::hardwareClock(); + + if(statement->a_prepared == false) { + statement->prepare(this); + statement->a_prepared = true; + } + + LOGDEBUG( + string msg("dbms::Connection::execute | "); + msg += statement->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + + if(statement->requiresCommit() == true && a_rollbackPending == true) { // (1) + string msg("dbms::Connection::execute | "); + msg += asString(); + msg += " | Connection has pending ROLLBACKS for execution"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + ResultCode result; + int tryCounter = 0; + + while(true) { + result = statement->execute(this); + + if(result.lostConnection() == false) + break; + + string msg = asString(); + msg += " | "; + msg += statement->asString(); + msg += " | "; + msg += result.asString(); + Logger::alert(msg, ANNA_FILE_LOCATION); + a_database.recover(*this, ++ tryCounter); + } + + statement->measureTiming(init, functions::hardwareClock()); + + if(result.successful() == false && result.notFound() == false) { + string msg(asString()); + msg += " | Sentence: "; + msg += statement->getName(); + Logger::write(Logger::Error, msg, result.asString(), ANNA_FILE_LOCATION); + } + + if(statement->requiresCommit() == true) { // (1) + if(result.successful() == false) { + if(statement->isCritical() == true) { + a_rollbackPending = true; + throw DatabaseException(statement->getName(), result, ANNA_FILE_LOCATION); + } + } else { + a_commitPending ++; + + if(a_maxCommitPending > 0 && a_commitPending > a_maxCommitPending) { + commit(); + a_commitPending = 0; + a_rollbackPending = false; + } + } + } + + return result; +} + +//------------------------------------------------------------------------------------------------ +// (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas +// y los volcados de contexto. +//------------------------------------------------------------------------------------------------ +void dbms::Connection::commit() +throw(RuntimeException, dbms::DatabaseException) { + LOGINFORMATION( + string msg("dbms::Connection::commit | "); + msg += asString(); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + + if(isAvailable() == false) { + string msg(asString()); + msg += " | Unavailable connection"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + do_commit(); + a_commitPending = 0; // (1) + a_rollbackPending = false; +} + +//------------------------------------------------------------------------------------------------ +// (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas +// y los volcados de contexto. +//------------------------------------------------------------------------------------------------ +void dbms::Connection::rollback() +throw() { + LOGWARNING( + string msg("dbms::Connection::rollback | "); + msg += asString(); + Logger::warning(msg, ANNA_FILE_LOCATION); + ); + + if(isAvailable() == false) { + string msg(asString()); + msg += " | Unavailable connection"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + do_rollback(); + a_commitPending = 0; + a_rollbackPending = false; // (1) +} + +void dbms::Connection::lock() +throw(RuntimeException) { + if(isAvailable() == false) { + string msg(asString()); + msg += " | Unavailable connection"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + comm::Resource::lock(); + + if(a_lockingCounter ++ == 0) { + a_commitPending = 0; + a_rollbackPending = false; + + try { + if(do_beginTransaction() == true) + a_commitPending = 1; + } catch(dbms::DatabaseException& edb) { + throw RuntimeException(edb); + } + } + + LOGDEBUG( + string msg("dbms::Connection::lock | "); + msg += asString(); + Logger::debug(msg, ANNA_FILE_LOCATION) + ); +} + +void dbms::Connection::unlock() +throw() { + LOGDEBUG( + string msg("dbms::Connection::unlock | "); + msg += asString(); + Logger::debug(msg, ANNA_FILE_LOCATION) + ); + + if(-- a_lockingCounter <= 0) { + a_lockingCounter = 0; + + try { + if(a_rollbackPending == true) + rollback(); + else if(a_commitPending > 0) + commit(); + } catch(Exception& ex) { + Logger::emergency(ex.getText(), ex.getFromFile(), ex.getFromLine()); + } + } + + comm::Resource::unlock(); +} + +string dbms::Connection::asString() const +throw() { + string result("dbms::Connection { "); + result += comm::Resource::asString(); + result += " | "; + result += a_database.asString(); + result += " | user: "; + result += a_user; + result += functions::asText(" | LockingCounter: ", a_lockingCounter); + result += functions::asText(" | password: ******* | CommitPending: ", a_commitPending); + result += functions::asText(" | RollbackPending: ", a_rollbackPending); + return result += " }"; +} + +xml::Node* dbms::Connection::asXML(xml::Node* parent) const +throw() { + xml::Node* result = comm::Resource::asXML(parent); + result->createAttribute("User", a_user); + result->createAttribute("LockingCounter", a_lockingCounter); + result->createAttribute("CommitPending", a_commitPending); + result->createAttribute("RollbackPending", functions::asString(a_rollbackPending)); + return result; +} + diff --git a/source/dbms/Data.cpp b/source/dbms/Data.cpp new file mode 100644 index 0000000..3e75b0f --- /dev/null +++ b/source/dbms/Data.cpp @@ -0,0 +1,28 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +using namespace anna; + +std::string dbms::Data::asString() const +throw() { + static const char* typeName [] = { "Integer", "String", "Float", "ShortBlock", "LongBlock", "Date", "TimeStamp" }; + std::string result("dbms::Data { Type: "); + result += typeName [a_type]; + result += " | Buffer: "; + result += functions::asHexString(anna_ptrnumber_cast(a_buffer)); + result += " | MaxSize: "; + result += functions::asString(a_maxSize); + result += " | Null: "; + result += functions::asString(a_isNull); + return result += " }"; +} + diff --git a/source/dbms/Database.cpp b/source/dbms/Database.cpp new file mode 100644 index 0000000..987038f --- /dev/null +++ b/source/dbms/Database.cpp @@ -0,0 +1,325 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +Database::Database(const char* className, const char* dbmsName) : + Component(className), + a_name((dbmsName == NULL) ? "local" : dbmsName), + a_type((dbmsName == NULL) ? Type::Local : Type::Remote), + a_failRecoveryHandler(NULL), + a_statementTranslator(NULL) { + dbms::sccs::activate(); +} + +Database::~Database() { + stop(); +} + +void Database::do_initialize() +throw(RuntimeException) { + LOGMETHOD(TraceMethod tm("dbms::Database", "do_initialize", ANNA_FILE_LOCATION)); + int counter(0); + bool error = false; + + for(connection_iterator iic = connection_begin(), maxiic = connection_end(); iic != maxiic; iic ++) { + try { + connection(iic)->open(); + counter ++; + } catch(Exception& ex) { + ex.trace(); + error = true; + } + } + + if(counter == 0 && error == true) { + string msg(asString()); + msg += " | No available connections"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGINFORMATION( + Logger::information(asString(), ANNA_FILE_LOCATION); + ); +} + +void Database::do_stop() +throw() { + LOGMETHOD(TraceMethod tm("dbms::Database", "do_stop", ANNA_FILE_LOCATION)); + + try { + Connection* _connection; + + for(connection_iterator iic = connection_begin(), maxiic = connection_end(); iic != maxiic; iic ++) { + _connection = connection(iic); + _connection->close(); + delete _connection; + } + + a_connections.clear(); + } catch(Exception& ex) { + ex.trace(); + a_connections.clear(); + } +} + +/** + * Para evitar que todos los clones usen la misma conexion a la base de datos, se realiza en + * cada uno de ellos una re-conexion, es decir, se cierra la original (abierta por el proceso + * padre) y se abre una nueva conexion con los mismos parametros y contra la misma base de datos. + */ +void Database::do_cloneChild() +throw(RuntimeException) { + LOGMETHOD(TraceMethod tm("dbms::Database", "do_cloneChild", ANNA_FILE_LOCATION)); + + for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) { + dbms::Connection* conn = connection(ii); + LOGDEBUG( + string msg("dbms::Database::do_cloneChild | "); + msg += conn->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + recover(*conn, 0); + } +} + +Connection* Database::createConnection(const char* name, const char* user, const char* password) +throw(RuntimeException, DatabaseException) { + Guard guard(this, "dbms::Database (createConnection)"); + + if(a_connections.size() >= MaxConnection) { + string msg("Database::createConnection | "); + msg += asString(); + msg += functions::asText(" | Cannot create more than %d connections per database", MaxConnection); + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) { + if(connection(ii)->getName() == name) { + string msg("Database::createConnection | "); + msg += asString(); + msg += " | Connection: "; + msg += name; + msg += " | Previously registered"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + } + + string strname(name); + Connection* result = allocateConnection(strname, user, password); + + if(result == NULL) { + string msg(asString()); + msg += " | "; + msg += strname; + msg += " | Unable to instance connection"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGDEBUG( + string msg("dbms::Database::createConnection | "); + msg += result->asString(); + Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION); + ); + + if(getState() == Component::State::Running) { + try { + result->open(); + a_connections.push_back(result); + } catch(RuntimeException& ex) { + ex.trace(); + delete result; + throw; + } catch(DatabaseException& edbms) { + edbms.trace(); + delete result; + throw; + } + } else + a_connections.push_back(result); + + return result; +} + +Connection& Database::findConnection(const char* name) +throw(RuntimeException) { + Guard guard(this, "dbms::Database (findConnection)"); + Connection* result = NULL; + + for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) { + if(anna_strcmp(connection(ii)->getName().c_str(), name) == 0) { + result = connection(ii); + break; + } + } + + if(result == NULL) { + string msg("Database::findConnection | "); + msg += asString(); + msg += " | Conexion: "; + msg += name; + msg += " | Unregistered"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(result->isAvailable() == false || result->isEnabled() == false) { + string msg("Database::findConnection | "); + msg += asString(); + msg += " | Connection: "; + msg += name; + msg += " | Unavailable"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGDEBUG( + string msg("Database::findConnection | "); + msg += result->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + return *result; +} + +Statement* Database::createStatement(const char* name, const char* expression, const bool isCritical) +throw(RuntimeException) { + if(findStatement(name) != NULL) + throw RuntimeException(functions::asString("Sentence: %s | Name already in use", name), ANNA_FILE_LOCATION); + + Guard guard(this, "dbms::Database::createStatement"); + + if(a_statementTranslator != NULL) + expression = a_statementTranslator->apply(expression); + + Statement* result = allocateStatement(name, expression, isCritical); + LOGDEBUG( + string msg("dbms::Database::createStatement | "); + msg += result->asString(); + + if(a_statementTranslator != NULL) { + msg += " | Translator: "; + msg += a_statementTranslator->getName(); + } + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + a_statements.push_back(result); + return result; +} + +Statement* Database::findStatement(const char* name) +throw() { + Guard guard(this, "dbms::Database::findStatement"); + vector ::iterator ii, maxii; + Statement* result(NULL); + + for(ii = a_statements.begin(), maxii = a_statements.end(); ii != maxii; ii ++) { + if(anna_strcmp((*ii)->getName().c_str(), name) == 0) { + result = *ii; + break; + } + } + + return result; +} + +void Database::releaseStatement(Statement* statement) +throw() { + if(statement == NULL) { + Logger::write(Logger::Warning, asString(), "Cannot release a NULL SQL sentence", ANNA_FILE_LOCATION); + return; + } + + LOGDEBUG( + string msg("dbms::Database::releaseStatement | "); + msg += statement->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + Guard guard(this, "dbms::Database::releaseStatement"); + vector ::iterator end = a_statements.end(); + vector ::iterator ii = find(a_statements.begin(), end, statement); + + if(ii != end) { + a_statements.erase(ii); + delete statement; + } +} + +void Database::recover(Connection& connection, const int tryCounter) +throw(RuntimeException) { + try { + connection.close(); + connection.open(); + } catch(DatabaseException& edbms) { + edbms.trace(); + + if(a_failRecoveryHandler != NULL) + a_failRecoveryHandler->apply(connection, tryCounter); + } +} + +string Database::asString() const +throw() { + string result("dbms::Database { "); + result += Component::asString(); + + if(a_type == Type::Local) + result += " | Type: Local"; + else { + result += " | Type: Remote | Name: "; + result += a_name; + } + + return result += " }"; +} + +xml::Node* Database::asXML(xml::Node* parent) const +throw() { + parent = Component::asXML(parent); + xml::Node* result = parent->createChild("dbms.Database"); + xml::Node* node; + result->createAttribute("Type", (a_type == Type::Local) ? "Local" : "Remote"); + + if(a_type != Type::Local) + result->createAttribute("Name", a_name); + + if(a_statementTranslator != NULL) + result->createAttribute("Translator", a_statementTranslator->getName()); + + node = result-> createChild("Connections"); + + for(const_connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) + connection(ii)->asXML(node); + + node = result-> createChild("Statements"); + + for(const_statement_iterator ii = statement_begin(), maxii = statement_end(); ii != maxii; ii ++) + statement(ii)->asXML(node); + + return result; +} + diff --git a/source/dbms/Date.cpp b/source/dbms/Date.cpp new file mode 100644 index 0000000..5c9afaf --- /dev/null +++ b/source/dbms/Date.cpp @@ -0,0 +1,149 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include + +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +Date::Date(const bool isNulleable, const char* format) : + Data(Type::Date, MaxDateSize, isNulleable) { + Data::setBuffer(a_buffer); + a_buffer [0] = 0; + a_format = (format == NULL) ? NULL : strdup(format); + anna_memset(&a_value, 0, sizeof(a_value)); +} + +Date::Date(const Data::Type::_v type, const bool isNulleable, const char* format) : + Data(type, MaxDateSize, isNulleable) { + Data::setBuffer(a_buffer); + a_buffer [0] = 0; + a_format = (format == NULL) ? NULL : strdup(format); + anna_memset(&a_value, 0, sizeof(a_value)); +} + +Date::Date(const Date& other) : + Data(other) { + Data::setBuffer(a_buffer); + a_buffer [0] = 0; + a_format = (other.a_format == NULL) ? NULL : strdup(other.a_format); + anna_memcpy(&a_value, &other.a_value, sizeof(a_value)); +} + +Date::~Date() { + if(a_format != NULL) + free(a_format); +} + +const char* dbms::Date::getCStringValue() const +throw() { + const char* format; + + if((format = a_format) == NULL) + format = "%d/%m/%Y %H:%M:%S"; + + return (strftime(const_cast (this)->a_buffer, MaxDateSize, format, &a_value) == 0) ? NULL : a_buffer; +} + +Date& Date::operator = (const Date & other) +throw(RuntimeException) { + if(this != &other) { + if(other.isNull() == true) { + setNull(true); + anna_memset(&a_value, 0, sizeof(a_value)); + } else { + setNull(false); + anna_memcpy(&a_value, &other.a_value, sizeof(a_value)); + } + } + + return *this; +} + +void Date::setValue(const char* str) +throw(RuntimeException) { + if(a_format == NULL) { + string msg(asString()); + msg += " | anna::dbms::Data::setValue (const char*) requires format especification"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + tm aux; + char* r = strptime(str, a_format, &aux); + + if(r == NULL) { + string msg(asString()); + msg += " | String: "; + msg += str; + msg += " | Can't be interpreted as valid date"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + Data::setNull(false); + anna_memcpy(&a_value, &aux, sizeof(a_value)); +} + +void Date::setValue(const Second &second) +throw(RuntimeException) { + tm* aux = localtime((time_t*) & second); + + if(aux == NULL) { + string msg(asString()); + msg += functions::asText(" | Second: ", (int) second); + msg += " | Can't be interpreted as valid date"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + Data::setNull(false); + anna_memcpy(&a_value, aux, sizeof(a_value)); +} + +void dbms::Date::set(const char* what, int& variable, const int value, const int min, const int max) +throw(RuntimeException) { + if(value < min) { + string msg(what); + msg += functions::asText(" must be greater than or equal to ", min); + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(value > max && max != -1) { + string msg(what); + msg += functions::asText(" must be less than or equal to ", max); + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + Data::setNull(false); + variable = value; +} + +string dbms::Date::asString() const +throw() { + const char* cstring; + string result("dbms::Date { "); + result += dbms::Data::asString(); + result += " | Format: "; + result += (a_format == NULL) ? "" : a_format; + result += " | Value: "; + + if(Data::isNull() == false) { + if((cstring = getCStringValue()) == NULL) + result += ""; + else + result += cstring; + } else + result += ""; + + return result += " }"; +} + diff --git a/source/dbms/Delivery.cpp b/source/dbms/Delivery.cpp new file mode 100644 index 0000000..8ae88e4 --- /dev/null +++ b/source/dbms/Delivery.cpp @@ -0,0 +1,66 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include +#include +#include + +using namespace std; +using namespace anna; + +void dbms::Delivery::createConnections(dbms::Database& database, const char* prefixName, const char* user, const char* password, const int n) +throw(RuntimeException, dbms::DatabaseException) { + string name; + + for(int i = 0; i < n; i ++) { + name = prefixName; + name += functions::asString("%04d", i); + this->add(database.createConnection(name.c_str(), user, password)); + } + + a_iiConnection = this->begin(); +} + +dbms::Connection& dbms::Delivery::getConnection() +throw(RuntimeException) { + return *(static_cast (comm::Delivery::apply())); +} + +//-------------------------------------------------------------------------------------------------- +// Se invoca desde la seccion critica establecida en comm::Delivery::apply. +// +// (0) Si no hay registrada ninguna conexion +//-------------------------------------------------------------------------------------------------- +comm::Resource* dbms::Delivery::do_apply() +throw(RuntimeException) { + iterator maxii = end(); + + if(a_iiConnection == maxii) // (0) + return NULL; + + iterator init = a_iiConnection; + Connection* result = NULL; + Connection* w; + + do { + w = connection(a_iiConnection); + + if(a_iiConnection == maxii) + a_iiConnection = begin(); + + if(w->isAvailable() == true && w->isEnabled() == true) { + result = w; + break; + } + } while(a_iiConnection != init); + + return result; +} diff --git a/source/dbms/Float.cc.new b/source/dbms/Float.cc.new new file mode 100644 index 0000000..92ffbac --- /dev/null +++ b/source/dbms/Float.cc.new @@ -0,0 +1,19 @@ +#include + +#include + +#include + +using namespace anna; +using namespace anna::dbms; + +std::string dbms::Float::asString () const + throw () +{ + std::string result ("dbms::Float { "); + result += dbms::Data::asString (); + result += " | Value: "; + result += functions::asString (a_format, a_value); + return result += " }"; +} + diff --git a/source/dbms/Float.cpp b/source/dbms/Float.cpp new file mode 100644 index 0000000..7f4185d --- /dev/null +++ b/source/dbms/Float.cpp @@ -0,0 +1,26 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include + +using namespace anna; +using namespace anna::dbms; + +std::string dbms::Float::asString() const +throw() { + std::string result("dbms::Float { "); + result += dbms::Data::asString(); + result += " | Value: "; + result += functions::asString(a_format, a_value); + return result += " }"; +} + diff --git a/source/dbms/Integer.cpp b/source/dbms/Integer.cpp new file mode 100644 index 0000000..605a20b --- /dev/null +++ b/source/dbms/Integer.cpp @@ -0,0 +1,23 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +using namespace anna; + +std::string dbms::Integer::asString() const +throw() { + std::string result("dbms::Integer { "); + result += dbms::Data::asString(); + result += " | Valor: "; + result += functions::asString(a_value); + return result += " }"; +} + diff --git a/source/dbms/LongBlock.cpp b/source/dbms/LongBlock.cpp new file mode 100644 index 0000000..da1796a --- /dev/null +++ b/source/dbms/LongBlock.cpp @@ -0,0 +1,48 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +using namespace anna; + +dbms::LongBlock& dbms::LongBlock::operator = (const dbms::LongBlock & other) +throw(RuntimeException) { + if(this == &other) + return *this; + + if(other.isNull() == true) { + setNull(true); + return *this; + } + + return operator= (other.a_value); +} + +dbms::LongBlock& dbms::LongBlock::operator = (const anna::DataBlock & value) +throw(RuntimeException) { + a_value = value; + setNull(a_value.isEmpty()); + return *this; +} + +std::string dbms::LongBlock::asString() const +throw() { + std::string result("dbms::LongBlock { "); + result += dbms::Data::asString(); + result += " | Size: "; + + if(isNull()) + result += "(null)"; + else + result += functions::asString(a_value.getSize()); + + return result += " }"; +} + diff --git a/source/dbms/OutputBind.cpp b/source/dbms/OutputBind.cpp new file mode 100644 index 0000000..53e6b23 --- /dev/null +++ b/source/dbms/OutputBind.cpp @@ -0,0 +1,37 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include +#include + +using namespace anna; +using namespace std; + +void dbms::OutputBind::write() const +throw(RuntimeException, dbms::DatabaseException) { + const dbms::Data& data = Bind::getData(); + + if(data.getType() != Data::Type::LongBlock) { + string msg("anna::dbms::OutputBind::write | "); + msg += data.asString(); + msg += " | This method only can be called for anna::dbms::LongBlock types"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGDEBUG( + string msg("anna::dbms::OutputBind::write | "); + msg += data.asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + do_write(reinterpret_cast (data)); +} + + diff --git a/source/dbms/ResultCode.cpp b/source/dbms/ResultCode.cpp new file mode 100644 index 0000000..453f78b --- /dev/null +++ b/source/dbms/ResultCode.cpp @@ -0,0 +1,104 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +using namespace std; +using namespace anna::dbms; + +bool ResultCode::notFound() const +throw(anna::RuntimeException) { + if(a_errorDecoder == NULL) { + string msg(asString()); + msg += " | Has no decoder for associated error"; + throw anna::RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return a_errorDecoder->notFound(a_errorCode); +} + +bool ResultCode::successful() const +throw(anna::RuntimeException) { + if(a_errorDecoder == NULL) { + string msg(asString()); + msg += " | Has no decoder for associated error"; + throw anna::RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return a_errorDecoder->successful(a_errorCode); +} + +bool ResultCode::locked() const +throw(anna::RuntimeException) { + if(a_errorDecoder == NULL) { + string msg(asString()); + msg += " | Has no decoder for associated error"; + throw anna::RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return a_errorDecoder->locked(a_errorCode); +} + +bool ResultCode::lostConnection() const +throw(anna::RuntimeException) { + if(a_errorDecoder == NULL) { + string msg(asString()); + msg += " | Has no decoder for associated error"; + throw anna::RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return a_errorDecoder->lostConnection(a_errorCode); +} + +// +// No usamos la std::string porque la gran mayoría de las veces la ejecución de las sentencias SQL será +// correcta => no hará falta reservar ninguna memoria. +// +void ResultCode::copy(const char* text) +throw() { + if(text == NULL) { + if(a_errorText != NULL) { + free(a_errorText); + a_errorText = NULL; + } + } else { + char* aux; + + if((aux = anna_strchr((char*) text, '\n')) != NULL) + * aux = 0; + + const int textLen = anna_strlen(text); + + if(a_errorText == NULL) + a_errorText = strdup(text); + else if(anna_strlen(a_errorText) >= textLen) + anna_strcpy(a_errorText, text); + else { + free(a_errorText); + a_errorText = strdup(text); + } + } +} + +std::string ResultCode::asString() const +throw() { + std::string result("dbms::ResultCode { Error: "); + result += functions::asString(a_errorCode); + result += " | Error: "; + result += (a_errorText == NULL) ? "(null)" : a_errorText; + result += " | Correct: "; + + if(a_errorDecoder != NULL) + result += functions::asString(successful()); + else + result += ""; + + return result += " }"; +} diff --git a/source/dbms/Sentence.cpp b/source/dbms/Sentence.cpp new file mode 100644 index 0000000..34dfaf3 --- /dev/null +++ b/source/dbms/Sentence.cpp @@ -0,0 +1,99 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace anna; + +const string& dbms::Sentence::getName() const +throw() { + static string empty; + return (a_dbStatement == NULL) ? empty : a_dbStatement->getName(); +} + +void dbms::Sentence::initialize(dbms::Database& database) +throw(RuntimeException) { + Guard guard(this, "anna::dbms::Sentence (initialize)"); + a_dbStatement = do_initialize(database); +} + +//------------------------------------------------------------------------------------- +// Ojo!! No activamos la seccion critica en este metodo porque debera estar +// activa externamente ... para recoger datos multiples, etc, etc. +//------------------------------------------------------------------------------------- +dbms::ResultCode dbms::Sentence::execute(dbms::Connection& connection, dbms::Statement* statement) +throw(RuntimeException) { + using namespace anna::dbms; + ResultCode result; + + try { + result = connection.execute(statement); + + if(result.successful() == false) { + if(a_mode == Mode::SilentWhenNotFound && result.notFound() == true) + return result; + + throw DatabaseException(result, ANNA_FILE_LOCATION); + } + } catch(DatabaseException& edb) { + string msg("Sentence: "); + msg += getName(); + msg += " | "; + msg += edb.getText(); + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + return result; +} + +bool dbms::Sentence::fetch() +throw(RuntimeException) { + bool result = false; + + try { + result = a_dbStatement->fetch(); + } catch(dbms::DatabaseException& edb) { + throw RuntimeException(edb); + } + + return result; +} + +string dbms::Sentence::asString() const +/*virtual*/ +throw() { + string result("dbms::Sentence { Mode: "); + result += (a_mode == Mode::SilentWhenNotFound) ? "SilentWhenNotFound" : "ExceptionWhenNotFound"; + result += " | "; + + if(a_dbStatement != NULL) + result += a_dbStatement->asString(); + else + result += "dbms::Statement: "; + + return result += " }"; +} + +/*virtual*/ +xml::Node* dbms::Sentence::asXML(xml::Node* parent) const +throw() { + xml::Node* result = parent->createChild("dbms.Sentence"); + result->createAttribute("Mode", (a_mode == Mode::SilentWhenNotFound) ? "SilentWhenNotFound" : "ExceptionWhenNotFound"); + + if(a_dbStatement) + a_dbStatement->asXML(result); + + return result; +} diff --git a/source/dbms/ShortBlock.cpp b/source/dbms/ShortBlock.cpp new file mode 100644 index 0000000..a47de5f --- /dev/null +++ b/source/dbms/ShortBlock.cpp @@ -0,0 +1,52 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +using namespace anna; + +dbms::ShortBlock& dbms::ShortBlock::operator = (const dbms::ShortBlock & other) +throw(RuntimeException) { + if(this == &other) + return *this; + + if(other.isNull() == true) { + setNull(true); + return *this; + } + + return operator= (other.a_value); +} + +dbms::ShortBlock& dbms::ShortBlock::operator = (const anna::DataBlock & value) +throw(RuntimeException) { + if(value.getSize() > Data::getMaxSize()) { + throw RuntimeException( + functions::asString( + "Block out of range | Max: %d | Current: %d ", Data::getMaxSize(), value.getSize() + ), + ANNA_FILE_LOCATION + ); + } + + a_value = value; + setNull(a_value.isEmpty()); + return *this; +} + +std::string dbms::ShortBlock::asString() const +throw() { + std::string result("dbms::ShortBlock { "); + result += dbms::Data::asString(); + result += " | Value: "; + result += isNull() ? "(null)" : functions::asString(a_value).c_str(); + return result += " }"; +} + diff --git a/source/dbms/Statement.cpp b/source/dbms/Statement.cpp new file mode 100644 index 0000000..e2895e0 --- /dev/null +++ b/source/dbms/Statement.cpp @@ -0,0 +1,86 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +Statement::~Statement() { + InputBind* ibind; + OutputBind* obind; + + for(input_iterator ii = input_begin(), maxii = input_end(); ii != maxii; ii ++) { + ibind = inputBind(ii); + ibind->release(this); + a_database.deallocate(ibind); + } + + for(output_iterator ii = output_begin(), maxii = output_end(); ii != maxii; ii ++) { + obind = outputBind(ii); + obind->release(this); + a_database.deallocate(obind); + } +} + +string Statement::asString() const +throw() { + string result("dbms::Statement { Nombre: "); + result += a_name; + result += functions::asText(" | Var.Entrada: ", input_size()); + result += functions::asText(" | Var.Salida: ", output_size()); + result += " | "; + result += a_measureTiming.asString(); + result += " | Expresion: "; + result += a_expression; + return result += " }"; +} + +xml::Node* dbms::Statement::asXML(xml::Node* parent) const +throw() { + xml::Node* result = parent->createChild("dbms.Statement"); + result->createAttribute("Name", a_name); + xml::Node* node = result->createChild("Timing"); + node->createAttribute("N", a_measureTiming.size()); + node->createAttribute("Accumulator", a_measureTiming.getAccumulator()); + node->createAttribute("Timing", a_measureTiming.asString()); + result->createChild("Expression")->createText(a_expression); + return result; +} + +void Statement::bindInput(const char* name, Data& data) +throw() { + a_inputBinds.push_back(a_database.allocateInputBind(name, data)); +} + +const OutputBind* Statement::bindOutput(const char* name, Data& i) +throw() { + OutputBind* result = a_database.allocateOutputBind(name, i); + a_outputBinds.push_back(result); + return result; +} + +Data& Statement::input(input_iterator& ii) +throw() { + return (*ii)->getData(); +} + +Data& Statement::output(output_iterator& ii) +throw() { + return (*ii)->getData(); +} + diff --git a/source/dbms/String.cpp b/source/dbms/String.cpp new file mode 100644 index 0000000..2e7e6fb --- /dev/null +++ b/source/dbms/String.cpp @@ -0,0 +1,69 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +using namespace anna; +using namespace anna::dbms; + +String& String::operator = (const String & other) +throw(RuntimeException) { + if(this == &other) + return *this; + + if(other.isNull() == true) { + setNull(true); + a_value [0] = 0; + return *this; + } + + return operator= (other.a_value); +} + +String& String::operator = (const char * str) +throw(RuntimeException) { + if(a_value != str) { + if(anna_strlen(str) > Data::getMaxSize()) + throw RuntimeException( + functions::asString("'%s' out of range | MaxLen: %d | Len: %d ", str, Data::getMaxSize(), anna_strlen(str)), + ANNA_FILE_LOCATION + ); + + anna_strcpy(a_value, str); + } + + Data::setNull(false); + return *this; +} + +char* String::strip(char *str) +throw() { + int len; + + if(str == NULL || (len = anna_strlen(str)) == 0) + return str; + + int end = len - 1; + + while(end >= 0 && str [end] == ' ') end --; + + if(end >= 0) + str [++ end] = 0; + + return str; +} + +std::string dbms::String::asString() const +throw() { + std::string result("dbms::String { "); + result += dbms::Data::asString(); + result += " | Value: "; + result += a_value; + return result += " }"; +} + diff --git a/source/dbms/TimeStamp.cpp b/source/dbms/TimeStamp.cpp new file mode 100644 index 0000000..7cfe654 --- /dev/null +++ b/source/dbms/TimeStamp.cpp @@ -0,0 +1,39 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include + +using namespace std; +using namespace anna; +using namespace anna::dbms; + +const char* dbms::TimeStamp::getCStringValue() const +throw() { + const char* format; + + if((format = a_format) == NULL) + format = "%d/%m/%Y %H:%M:%S.%%d"; + + TimeStamp* _this = const_cast (this); + char* result = _this->a_buffer; + + if(strftime(result, MaxDateSize, format, &a_value) == 0) + return NULL; + + if(anna_strstr(result, "%d")) { + anna_strcpy(_this->a_anotherBuffer, result); + sprintf(result, _this->a_anotherBuffer, a_fractionalSecond); + } + + return result; +} diff --git a/source/dbms/functions.cpp b/source/dbms/functions.cpp new file mode 100644 index 0000000..97a4e88 --- /dev/null +++ b/source/dbms/functions.cpp @@ -0,0 +1,60 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace anna; + +/*static*/ +void dbms::functions::verifyDataScheme(dbms::Connection& connection, const char* tableName, const char* requiredPatch, const char* columnID, const char* columnDate) +throw(RuntimeException) { + dbms::Database& database = connection.getDatabase(); + dbms::Statement* statement = NULL; + dbms::String id(8); + string sql = anna::functions::asString( + "select max(%s) from %s where %s in (select max(%s) from %s)", + columnID, tableName, columnDate, + columnDate, tableName + ); + + try { + statement = database.createStatement("dbms::functions::VerifyDataScheme", sql); + statement->bindOutput("max_id", id); + dbms::ResultCode resultCode = connection.execute(statement); + + if(resultCode.successful() == false) + throw dbms::DatabaseException(resultCode, ANNA_FILE_LOCATION); + + statement->fetch(); + const string _id(id.getValue()); + const string _required(requiredPatch); + + if(_required != _id) { + std::string msg("DataScheme is out of date | Current patch: "); + msg += id; + msg += " | Required patch: "; + msg += requiredPatch; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + database.releaseStatement(statement); + } catch(dbms::DatabaseException& edb) { + database.releaseStatement(statement); + throw RuntimeException(edb); + } catch(RuntimeException&) { + database.releaseStatement(statement); + throw; + } +} diff --git a/source/dbms/internal/sccs.cpp b/source/dbms/internal/sccs.cpp new file mode 100644 index 0000000..313df2c --- /dev/null +++ b/source/dbms/internal/sccs.cpp @@ -0,0 +1,28 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include +#include +#include + +#include + +#include + +anna_define_sccs_tag(dbms, 2); + +void anna::dbms::sccs::activate() +throw() { + anna::sccs::activate(); + xml::sccs::activate(); + app::sccs::activate(); + comm::sccs::activate(); + ModuleManager::instantiate().insert(anna_use_sccs_tag(dbms), "00"); +} + diff --git a/source/dbos/Accesor.cpp b/source/dbos/Accesor.cpp new file mode 100644 index 0000000..f77d2aa --- /dev/null +++ b/source/dbos/Accesor.cpp @@ -0,0 +1,105 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace anna; + +/*virtual*/ +dbos::Accesor::~Accesor() { + if(a_statement != NULL && a_database != NULL) + a_database->releaseStatement(a_statement); +} + +//------------------------------------------------------------------------------------------ +// Transfiere la informacion del medio fisico al 'Loader' concreto. +// +// (1) Ojo!! Ejecuta la sentencia SQL y carga el primer registro en el area de intercambio +// Slo habr�que invocar al 'fetch' para coger los siguientes registros' +//------------------------------------------------------------------------------------------ +bool dbos::Accesor::load(dbms::Connection* connection, const dbos::StorageArea* ssaa) +throw(RuntimeException, dbms::DatabaseException) { + + if(connection == NULL) { + std::string msg(ssaa->asString()); + msg += " | "; + msg += asString(); + msg += " | Cannot execute this method with dbms::Connection == NULL"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + dbms::Statement* statement(getStatement()); + + if(statement == NULL) { + std::string msg(ssaa->asString()); + msg += " | "; + msg += asString(); + msg += " | Has no SQL sentence associated"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGDEBUG( + string msg("dbos::Accesor::load | "); + msg += ssaa->asString(); + msg += " | "; + msg += asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + dbms::ResultCode resultCode = connection->execute(statement); // (1) + + if(resultCode.notFound() == true) { + if(a_emodeIsNull == true) { + if(ssaa->getErrorCode() != StorageArea::NoExceptionWhenNotFound) { + std::string msg(ssaa->asString()); + msg += " | "; + msg += asString(); + msg += " | Register not found"; + RuntimeException ex(msg, ANNA_FILE_LOCATION); + ex.setErrorCode(ssaa->getErrorCode()); + throw ex; + } else + return false; + } else { + std::string msg(ssaa->asString()); + msg += " | "; + msg += asString(); + msg += " | Register not found"; + + if(a_exceptionMode == Exception::Mode::Ignore) { + Logger::debug(msg, ANNA_FILE_LOCATION); + return false; + } + + if(a_exceptionMode == Exception::Mode::Throw) { + RuntimeException ex(msg, ANNA_FILE_LOCATION); + ex.setErrorCode(ssaa->getErrorCode()); + throw ex; + } else if(Logger::isActive(Logger::Warning)) { + Logger::warning(msg, ANNA_FILE_LOCATION); + } + } + } + + if(resultCode.successful() == false) { + string msg(ssaa->getName()); + msg += " | "; + msg += asString(); + throw dbms::DatabaseException(msg, resultCode, ANNA_FILE_LOCATION); + } + + return statement->fetch(); +} diff --git a/source/dbos/Repository.cpp b/source/dbos/Repository.cpp new file mode 100644 index 0000000..49dd961 --- /dev/null +++ b/source/dbos/Repository.cpp @@ -0,0 +1,95 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include + +#include +#include +#include + +using namespace std; +using namespace anna; + +dbos::Repository::Repository(const char* name) : + a_name(name) { + sccs::activate(); +} + +dbos::Repository::Repository(const std::string& name) : + a_name(name) { + sccs::activate(); +} + +dbos::StorageArea* dbos::Repository::createStorageArea(const dbos::StorageId index, const char* name, const dbos::Size maxSize, dbos::ObjectAllocator objectAllocator, const int errorCode, const StorageArea::AccessMode::_v accessMode) +throw(RuntimeException) { + Guard guard(this, "dbos::Repository from createStorageArea"); + storage_iterator ii = a_storageAreas.find(index) ; + + if(ii != a_storageAreas.end()) { + string msg(ii->second->asString()); + msg += " | Already in use"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + StorageArea* result = new StorageArea(name, maxSize, objectAllocator, accessMode, errorCode); + a_storageAreas.insert(container::value_type(index, result)); + LOGDEBUG( + string msg("dbos::Repository::createStorageArea | Name: "); + msg += a_name; + msg += " | "; + msg += result->asString(); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + return result; +} + +dbos::StorageArea* dbos::Repository::findStorageArea(const dbos::StorageId index) +throw() { + Guard guard(this, "dbos::Repository from findStorageArea"); + storage_iterator ii = a_storageAreas.find(index); + return (ii != a_storageAreas.end()) ? storageArea(ii) : NULL; +} + +void dbos::Repository::clear() +throw(RuntimeException) { + Guard guard(this, "dbos::Repository from clear"); + LOGWARNING( + string msg("dbos::Repository::clear | Name: "); + msg += a_name; + Logger::warning(msg, ANNA_FILE_LOCATION); + ); + + for(storage_iterator ii = storage_begin(), maxii = storage_end(); ii != maxii; ii ++) + storageArea(ii)->clear(); +} + +xml::Node* dbos::Repository::asXML(xml::Node* parent) const +throw() { + xml::Node* result = parent->createChild("dbos.Repository"); + dbos::Size maxSize(0); + dbos::Size size(0); + const StorageArea* ssaa; + result->createAttribute("Name", a_name); + + for(const_storage_iterator ii = storage_begin(), maxii = storage_end(); ii != maxii; ii ++) { + ssaa = storageArea(ii); + maxSize += ssaa->getMaxSizeOf(); + size += ssaa->getSizeOf(); + ssaa->asXML(result); + } + + result->createAttribute("SizeOf", StorageArea::asMemorySize(size)); + result->createAttribute("MaxSizeOf", StorageArea::asMemorySize(maxSize)); + return result; +} + diff --git a/source/dbos/StorageArea.cpp b/source/dbos/StorageArea.cpp new file mode 100644 index 0000000..830f647 --- /dev/null +++ b/source/dbos/StorageArea.cpp @@ -0,0 +1,1081 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace anna; + +StorageArea::StorageArea(const char* name, const Size maxSize, ObjectAllocator objectAllocator, const StorageArea::AccessMode::_v accessMode, const int errorCode) : + a_name(name), + a_maxSize(maxSize), + a_objectAllocator(objectAllocator), + a_accessMode(accessMode), + a_errorCode(errorCode), + a_indexBlock(0), + a_sizeof(0), + a_doneReuse(0) { + a_blocks.push_back(a_currentBlock = new Block(objectAllocator, maxSize)); + a_hit = a_fault = 0; +} + +StorageArea::~StorageArea() { +} + +//------------------------------------------------------------------------------------------------ +// Carga los datos del objeto y los guarda en el area de almacemiento. +// +// (1) Si no esta en memoria => se carga. +// (1.1) Si el registro no existe => se progresa la posible excepcion. +// +// (2) Cambia la politica de refresco de las areas de almacenamiento ReadWrite/Dirty. La carga solo se +// realizara cuando la copia de utilizacion sea 0. Ademas de la mejora de rendimiento aseguramos que +// la estabilidad de una instancia se mantiene durante toda la vida de esta. Por ejmplo evitamos que +// una instancia A este trabajando con una copia de una instancia que estamos liberando y volviendo +// a cargar con datos totalmente distintos ... imaginad que estamos recorriendo un vector asociado +// o algo asi. +// +// (3) Si no ha podido ser recargada (seguramente tiene mas de una referencia) debe evitar el +// uso en caso de que este marcada como Dirty. +// (4) Si el contador de uso es cero => que ya esta marcado como memoria libre, pero recordar que +// tambien puede estar en la memoria activa (pendiente de liberar y/o volver a usar). En este +// caso se ha reusado. +//------------------------------------------------------------------------------------------------ +Object* StorageArea::instance(Connection* connection, Loader& loader) +throw(RuntimeException, DatabaseException) { + const Index index = loader.getIndex(); + Object* result(NULL); + Instance* instance(NULL); + LOGDEBUG( + string msg("Instantiate (init) | "); + msg += asString(); + msg += " | "; + msg += loader.asString(); + msg += " | Index: "; + msg += functions::asHexString(index); + Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION) + ); + loader.a_connection = connection; + std::string name("dbos::StorageArea::instance with "); + name += typeid(loader).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find(index); + + if(ii == a_directory.end()) { // (1) + a_fault ++; + + if(loader.load(connection, this) == true) { // (1.1) + pair rr; + bool wasInserted = false; + instance = allocate(); + + try { + instance->object->setIndex(index); + /* Al añadir la instancia antes de invocar al método Object::initialize + * nos aseguramos de que se permitan implementar relaciones circulares + */ + rr = a_directory.insert(value_type(index, instance)); + wasInserted = true; + result = instance->object; + instance->flags |= Flag::InProgress; + instance->object->initialize(loader); + instance->object->a_isStored = true; + instance->flags &= Flag::Done; + } catch(DatabaseException&) { + instance->flags &= Flag::Done; + + if(wasInserted) + a_directory.erase(rr.first); + + a_holes.insert(instance, Holes::Mode::ReadyToReuse); + throw; + } catch(RuntimeException&) { + instance->flags &= Flag::Done; + + if(wasInserted) + a_directory.erase(rr.first); + + a_holes.insert(instance, Holes::Mode::ReadyToReuse); + throw; + } + } + } else { + static const bool IgnoreDirty = true; + verifyStatus(instance = StorageArea::instance(ii), IgnoreDirty); + + switch(a_accessMode) { + case AccessMode::ReadOnly: + result = ((instance->flags & Flag::Dirty) == 0) ? instance->object : reload(connection, loader, instance); + break; + case AccessMode::ReadWrite: + result = (instance->copyCounter > 0) ? instance->object : reload(connection, loader, instance); + break; + case AccessMode::ReadEver: + result = reload(connection, loader, instance); + break; + } + + if(instance->flags & Flag::Dirty) { // (3) + string msg(asString()); + msg += " | "; + msg += asString(instance); + msg += " | Instance selected as unusable"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(result != NULL) { + a_holes.erase(instance); // (4) + instance->copyCounter ++; + a_hit ++; + } + } + + LOGINFORMATION( + string msg("Instantiate (final) | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::information(msg, ANNA_FILE_LOCATION) + ); + return result; +} + +Object* StorageArea::instance(Connection* connection, CrossedLoader& crossedLoader, Loader& loader) +throw(RuntimeException, DatabaseException) { + Object* result = NULL; + crossedLoader.a_connection = connection; + // Si el seek devuelve 'true' es que ya tiene cargada la correspondencia entre la clave alternativa y la + // clave principal usada en el Loader recibido como parámetro. + bool loaded = (crossedLoader.seek() == false) ? crossedLoader.load(connection, this) : true; + + if(loaded == true) { + /* + * Transfiere la clave principal conseguida por el cargador cruzado. + */ + loader.upload(crossedLoader); + result = instance(connection, loader); + /* + * Da la posibilidad de que el cargador cruzado mantenga la correspondencia entre sus claves y las claves primarias + */ + crossedLoader.download(loader); + } + + return result; +} + +//------------------------------------------------------------------------- +// Crea un nuevo objeto en el area de almacenamiento. +//------------------------------------------------------------------------- +Object* StorageArea::create(Connection* connection, Creator& creator) +throw(RuntimeException, DatabaseException) { + const Index index = creator.getIndex(); + Instance* instance = NULL; + Object* result = NULL; + + if(a_accessMode == AccessMode::ReadOnly) { + string msg(asString()); + msg += " | Cannot create object with AccessMode::ReadOnly"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGDEBUG( + string msg(asString()); + msg += " | "; + msg += creator.asString(); + msg += " | Index: "; + msg += functions::asHexString(index); + Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION) + ); + creator.a_connection = connection; + std::string name("dbos::StorageArea::create with "); + name += typeid(creator).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find(index); + + if(ii == a_directory.end()) { + pair rr; + bool wasInserted = false; + a_fault ++; + instance = allocate(); + + try { + instance->object->setIndex(index); + instance->copyCounter = 1; + result = instance->object; + rr = a_directory.insert(value_type(index, instance)); + wasInserted = true; + instance->flags |= Flag::InProgress; + instance->object->create(creator); + instance->flags &= Flag::Done; + instance->object->a_isStored = false; + } catch(DatabaseException&) { + instance->flags &= Flag::Done; + + if(wasInserted) + a_directory.erase(rr.first); + + a_holes.insert(instance, Holes::Mode::ReadyToReuse); + throw; + } catch(RuntimeException&) { + instance->flags &= Flag::Done; + + if(wasInserted) + a_directory.erase(rr.first); + + a_holes.insert(instance, Holes::Mode::ReadyToReuse); + throw; + } + } else { + verifyStatus(instance = StorageArea::instance(ii)); + a_hit ++; + a_holes.erase(instance); + instance->copyCounter ++; + result = instance->object; + } + + LOGINFORMATION( + string msg("Create | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::information(msg, ANNA_FILE_LOCATION) + ); + return result; +} + +//------------------------------------------------------------------------- +// Carga los datos del objeto y los guarda en el area de almacemiento. +// +// (1) Si tiene cuenta 0 => estaba en la lista de objetos liberados => +// lo sacamos de ah� +//------------------------------------------------------------------------- +Object* StorageArea::find(Loader& loader) +throw(RuntimeException) { + const Index index = loader.getIndex(); + Instance* instance = NULL; + Object* result = NULL; + LOGDEBUG( + string msg(asString()); + msg += " | "; + msg += loader.asString(); + msg += " | Index: "; + msg += functions::asHexString(index); + Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION) + ); + std::string name("dbos::StorageArea::find with "); + name += typeid(loader).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find(index); + + if(ii != a_directory.end()) { + verifyStatus(instance = StorageArea::instance(ii)); + a_hit ++; + a_holes.erase(instance); + instance->copyCounter ++; + result = instance->object; + } else + a_fault ++; + + LOGDEBUG( + string msg("Find | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION) + ); + return result; +} + +Object* StorageArea::duplicate(const Object* object) +throw(RuntimeException) { + if(object == NULL) return NULL; + + std::string name("dbos::StorageArea::duplicate with "); + name += typeid(*object).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find(object->getIndex()); + + if(ii == a_directory.end()) { + a_fault ++; + string msg(asString()); + msg += " | Index: "; + msg += functions::asHexString(object->getIndex()); + msg += " | Invalid instance"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + Instance* instance = NULL; + verifyStatus(instance = StorageArea::instance(ii)); + a_holes.erase(instance); + instance->copyCounter ++; + a_hit ++; + LOGINFORMATION( + string msg("Duplicate | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::information(msg, ANNA_FILE_LOCATION) + ); + return instance->object; +} + +bool StorageArea::isLoaded(const Loader& loader) +throw(RuntimeException) { + const Index index = loader.getIndex(); + std::string name("dbos::StorageArea::isLoaded with "); + name += typeid(loader).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find(index); + const bool result = (ii != a_directory.end()); + LOGDEBUG( + string msg(asString()); + msg += " | "; + msg += loader.asString(); + msg += " | Index: "; + msg += functions::asHexString((int) index); + msg += functions::asText(" | isLoaded: ", result); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + return result; +} + +void StorageArea::apply(Connection& connection, Recorder& recorder) +throw(RuntimeException, DatabaseException) { + ResultCode resultCode = connection.execute(recorder.getStatement()); + + if(resultCode.successful() == false) + throw DatabaseException(resultCode, ANNA_FILE_LOCATION); +} + +//------------------------------------------------------------------------------------------------ +// Borra un objeto del medio fisico => lo borra tambien de la cache +// +// (1) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y +// si fuera necesario invocara al 'destroy'. +// (2) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la +// marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la +// instancia en cuanto pueda. +//------------------------------------------------------------------------------------------------ +void StorageArea::apply(Connection& connection, Eraser& eraser) +throw(RuntimeException, DatabaseException) { + if(a_accessMode == AccessMode::ReadOnly) { + string msg(asString()); + msg += " | Cannot erase object with AccessMode::ReadOnly"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + Object* object = eraser.getObject(); + eraser.a_connection = &connection; + std::string name("dbos::StorageArea::apply with "); + name += typeid(eraser).name(); + Guard guard(this, name.c_str()); + Instance* instance = NULL; + + if(object != NULL) { + iterator ii = a_directory.find(object->getIndex()); + + if(ii != a_directory.end()) { + instance = StorageArea::instance(ii); + + if(instance->copyCounter > 1) { + instance->flags |= Flag::Incoherent; // (2) + string msg(eraser.asString()); + msg += " | Instances: "; + msg += functions::asString(instance->copyCounter); + msg += " | Cannot delete object"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + } + } + + ResultCode resultCode = connection.execute(eraser.getStatement()); + + if(resultCode.successful() == false) + throw DatabaseException(resultCode, ANNA_FILE_LOCATION); + + if(instance != NULL) { + instance->copyCounter = 0; + quickReusing(instance); // (1) + } +} + +//------------------------------------------------------------------------------------------------ +// Decrementa la cuenta de utilizacin del objeto recibido como parametro. +// +// (1) Si la instancia que estamos liberando esta marcada como 'Incoherente' y es la ultima +// referencia => es el momento de expulsarla de la memoria. +// (2) Queda a la espera de que se vuelva a usar la referencia o que el numero de registros en +// memoria alcance un numero tal que implique comenzar a reusar objetos liberados. +//------------------------------------------------------------------------------------------------ +void StorageArea::release(Object** object) +throw(RuntimeException) { + if(object == NULL) return; + + if(*object == NULL) return; + + std::string name("dbos::StorageArea::release with "); + name += typeid(**object).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find((*object)->getIndex()); + + if(ii == a_directory.end()) + return; + + Instance* instance = StorageArea::instance(ii); + + if(instance->copyCounter > 0) { + if(-- instance->copyCounter == 0) { + if(instance->flags & Flag::Incoherent) // (1) + quickReusing(instance); + else + a_holes.insert(instance, Holes::Mode::TimeWait); // (2) + } + } + + LOGINFORMATION( + string msg("Release | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::information(msg, ANNA_FILE_LOCATION) + ); + *object = NULL; +} + +//------------------------------------------------------------------------------------------------ +// Elimina toda la informacin referente al objeto recibido como parametro. +// +// (1) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la +// marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la +// instancia en cuanto pueda. +// (2) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y +// si fuera necesario invocara al 'destroy'. +//------------------------------------------------------------------------------------------------ +void StorageArea::erase(Object** object) +throw(RuntimeException) { + if(object == NULL) return; + + if(*object == NULL) return; + + std::string name("dbos::StorageArea::erase with "); + name += typeid(**object).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find((*object)->getIndex()); + + if(ii == a_directory.end()) + return; + + Instance* instance = StorageArea::instance(ii); + + if(instance->copyCounter > 1) { + instance->flags |= Flag::Incoherent; // (1) + string msg(asString()); + msg += " | "; + msg += asString(instance); + msg += " | Cannot dump instance"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + LOGDEBUG( + string msg("Erase | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::debug(msg, ANNA_FILE_LOCATION) + ); + instance->copyCounter = 0; + quickReusing(instance); // (2) + *object = NULL; +} + +void StorageArea::dirty(Object* object) +throw(RuntimeException) { + if(object == NULL) return; + + std::string name("dbos::StorageArea::dirty with "); + name += typeid(*object).name(); + Guard guard(this, name.c_str()); + iterator ii = a_directory.find(object->getIndex()); + + if(ii == a_directory.end()) + return; + + Instance* instance = StorageArea::instance(ii); + + if(instance->copyCounter > 1 && Logger::isActive(Logger::Warning)) { + string msg(asString()); + msg += " | "; + msg += asString(instance); + msg += " | More than one copy on instance to be selected"; + Logger::warning(msg, ANNA_FILE_LOCATION); + } + + LOGDEBUG( + string msg("Dirty | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::debug(msg, ANNA_FILE_LOCATION) + ); +} + +//-------------------------------------------------------------------------------------------- +// Descarga todos los objetos de este area de almacenamiento. +// +// (1) Para que permite incluirla en los huecos. +// (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo. +// (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido +// expulsarla de cache. +//-------------------------------------------------------------------------------------------- +void StorageArea::clear() +throw(RuntimeException) { + Guard guard(this, "dbos::StorageArea::clear"); + Instance* instance; + int n = 0; + + for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) { + instance = StorageArea::instance(ii); + instance->copyCounter = 0; // (1) + a_holes.insert(instance, Holes::Mode::ReadyToReuse); + + if((instance->flags & Flag::Empty) == 0) { // (3) + LOGINFORMATION( + string msg("Destroy (clear) | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + instance->object->destroy(); + instance->flags |= Flag::Empty; + } + + instance->flags &= Flag::NoIncoherent; // (4) + n ++; + } + + a_directory.clear(); + LOGWARNING( + string msg("Clear | "); + msg += asString(); + msg += functions::asText(" | Destroyed objects: ", n); + Logger::warning(msg, ANNA_FILE_LOCATION) + ); +} + +//--------------------------------------------------------------------------------------------------- +// Intenta recargar la informacion del medio fisico en la instancia del objeto. +// Si hay algun problema y la instancia no puede guardarse en la de objetos disponibles +// (a_holes.insert == false) => se marca como corrupto y no se podra usar hasta que +// que no se libere su ultima instancia. +// +// (1) Si hubiera algun problema al invocar al 'initialize' no habria que volver a invocar +// a este metodo. +// (2) Recordar que la recarga solo se intenta cuando la copyCounter = 0, por tanto en el caso de +// intentar recargar un registro que ha sido borrado no se vuelve a grabar como Ready porque +// YA esta en la lista de huecos y seria necesario borrarlo de esta, cambiarle el msHoleTime y +// volverlo a grabar, pero la cosa funciona porque se saca del directorio de objetos cargados +// y se libera su memoria. +//--------------------------------------------------------------------------------------------------- +Object* StorageArea::reload(dbms::Connection* connection, Loader& loader, StorageArea::Instance* instance) +throw(RuntimeException, dbms::DatabaseException) { + const bool enableUpdate = (instance->flags & Flag::Dirty) ? true : instance->object->enableUpdate(); + bool hasChanges(false); + + if(enableUpdate == true) { + try { + if(loader.load(connection, this) == false) { + checkIncoherence(instance); // (2) + return NULL; + } + } catch(RuntimeException&) { + checkIncoherence(instance); + throw; + } catch(dbms::DatabaseException&) { + checkIncoherence(instance); + throw; + } + + hasChanges = (instance->flags & Flag::Dirty) ? true : instance->object->hasChanges(loader); + } + + LOGDEBUG( + string msg("Reload | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + msg += anna::functions::asText(" | EnableUpdate: ", enableUpdate); + msg += anna::functions::asText(" | HasChanges: ", hasChanges); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + + if(hasChanges == true) { + LOGINFORMATION( + string msg("Destroy (reload) | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + instance->object->destroy(); + instance->flags |= Flag::Empty; // (1) + instance->object->initialize(loader); + instance->flags &= Flag::NoEmpty; + instance->flags &= Flag::NoDirty; + } + + return instance->object; +} + +//------------------------------------------------------------------------------------------------ +// Cuando intenta recargar la informacion de una instancia desde el medio fisico, pero el +// registro ha sido borrado => +// 1.- Si hay alguna instancia en uso => no puede liberar la instancia porque algun otro +// objeto la tiene referenciada => la marca como corrupta. +// 2.- Si hay una unica instancia en uso => puede liberarla +// +// (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => saca el objeto +// del directorio porque yo no es valido. Recupera la coherencia memoria-medio_fisico +// porque ha conseguido "expulsar" de la cache el registro borrado. +// (2) No puede "expulsar" el registro de la cache porque hay algun otro objeto que lo esta +// referenciando => Marca ESTA instancia como incoherente => no se podra duplicar y volver +// a instanciar, etc, etc +//------------------------------------------------------------------------------------------------ +void StorageArea::checkIncoherence(StorageArea::Instance* instance) +throw() { + if(quickReusing(instance) == true) { + LOGWARNING( + string msg("dbos::StorageArea::checkIncoherence | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + msg += " | Recover coherence between physical media and memory"; + Logger::warning(msg, ANNA_FILE_LOCATION); + ); + } else { // (2) + instance->flags |= Flag::Incoherent; + LOGWARNING( + string msg("dbos::StorageArea::checkIncoherence | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + msg += " | Detected incoherence between physical media and memory"; + Logger::warning(msg, ANNA_FILE_LOCATION); + ); + } +} + +//------------------------------------------------------------------------------------------------ +// (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => (2) +// (2) saca el objeto del directorio porque yo no es valido. +// (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo. +// (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido +// expulsarla de cache. +// +// Recordar que en el caso de intentar recargar un registro que ha sido borrado no se vuelve +// a grabar como Ready porque ya esta en la lista de huecos y seria necesario borrarlo de +// esta, cambiarle el msHoleTime y volverlo a grabar. +//------------------------------------------------------------------------------------------------ +bool StorageArea::quickReusing(StorageArea::Instance* instance) +throw() { + bool result(false); + + if(a_holes.insert(instance, Holes::Mode::ReadyToReuse) == true) { // (1) + iterator ii = a_directory.find(instance->object->getIndex()); + + if(ii != a_directory.end()) // (2) + a_directory.erase(ii); + + if((instance->flags & Flag::Empty) == 0) { // (3) + LOGINFORMATION( + string msg("Destroy (quickreusing) | "); + msg += asString(); + msg += " | "; + msg += asString(instance); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + instance->object->destroy(); + instance->flags |= Flag::Empty; + } + + instance->flags &= Flag::NoIncoherent; // (4) + result = true; + } + + return result; +} + +void StorageArea::verifyStatus(StorageArea::Instance* instance, const bool ignoreDirty) +throw(RuntimeException) { + if(instance->flags & Flag::Incoherent) { + string msg(asString()); + msg += " | "; + msg += asString(instance); + msg += " | Physical media and memory do not match"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(instance->flags & Flag::Empty) { + string msg(asString()); + msg += " | "; + msg += asString(instance); + msg += " | Instance is empty"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(ignoreDirty == false && (instance->flags & Flag::Dirty)) { + string msg(asString()); + msg += " | "; + msg += asString(instance); + msg += " | Instance is temporary locked"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + if(instance->flags & Flag::InProgress) { + string msg(asString()); + msg += " | "; + msg += asString(instance); + msg += " | Instance already has word in progress"; + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } +} + +string StorageArea::asString() const +throw() { + string result("dbos::StorageArea { Name: "); + const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit); + /* Nº real de objetos en uso. En el directorio también se mantienen los que tienen la cuenta de utilización a 0 */ + const int n = a_directory.size() - a_holes.size(); + result += a_name; + result += " | N: "; + result += functions::asString(a_maxSize); + result += " | n: "; + result += functions::asString(n); + result += " | r: "; + result += functions::asString(a_directory.size()); + result += " | AccessMode: "; + result += AccessMode::asString(a_accessMode); + result += " | Holes: "; + result += functions::asString(a_holes.size()); + result += " | Reuse: "; + result += functions::asString(a_doneReuse); + result += " | Hit: "; + result += functions::asString(a_hit); + result += " | Fault: "; + result += functions::asString(a_fault); + result += " | Ratio: "; + result += functions::asString(ratio); + return result += "% }"; +} + +xml::Node* StorageArea::asXML(xml::Node* parent) const +throw() { + xml::Node* result = parent->createChild("dbos.StorageArea"); + xml::Node* node; + const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit); + result->createAttribute("Name", a_name); + result->createAttribute("AccessMode", AccessMode::asString(a_accessMode)); + node = result->createChild("Size"); + node->createAttribute("Value", a_directory.size() - a_holes.size()); + node->createAttribute("Reserve", a_directory.size()); + node->createAttribute("MaxValue", a_maxSize); + node->createAttribute("Holes", a_holes.size()); + node = result->createChild("SizeOf"); + node->createAttribute("Value", asMemorySize(getSizeOf())); + node->createAttribute("MaxValue", asMemorySize(getMaxSizeOf())); + node = result->createChild("Statistics"); + node->createAttribute("Hit", a_hit); + node->createAttribute("Fault", a_fault); + node->createAttribute("Ratio", anna::functions::asString("%d%%", ratio)); + node->createAttribute("DoneReuse", a_doneReuse); + return result; +} + +string StorageArea::asMemorySize(const Size size) +throw() { + string result; + + if(size < 1024) { + result = functions::asString("%u bytes", size); + } else if(size < 1024 * 1024) { + static const Size ka = 1024; + result += functions::asString("%u Kb", size / ka); + } else { + static const Size mega = 1024 * 1024; + result += functions::asString("%u Mb", size / mega); + } + + return result; +} + +StorageArea::Instance* StorageArea::allocate() +throw() { + Instance* result = NULL; + + if((result = reuse()) == NULL) { + if((result = a_currentBlock->getInstance()) == NULL) { + if(++ a_indexBlock == a_blocks.size()) + a_blocks.push_back(a_currentBlock = new Block(a_objectAllocator, a_maxSize)); + else + a_currentBlock = a_blocks [a_indexBlock]; + + result = a_currentBlock->getInstance(); + } + } + + result->copyCounter = 1; +//xxx result->msHoleTime = 0; + result->flags = Flag::None; +//xxx result->hasHole = false; + return result; +} + +//----------------------------------------------------------------------------------------------------- +// Reusa un objeto que lleva demasiado tiempo sin ser utilizado. +// +// (0) Si el tiempo que lleva en la lista de objetos liberados es cero => Podemos usar el re-usado +// rapido; ya ha sido eliminado del directorio, invoca al destroy, etc, etc. +// (0.1) Para asegurar que NO desborde los 32 bits. +// (1) Cuando n -> Nmax => Talpha = now => a poco tiempo que pase sin reusar los registros, estos +// se comienzan a resuar. Cuando n -> 0 => talpha = 0 => No se reusan registros, sino que se crearan +// nuevos. +// (2) Si el primero de los registros disponibles no es suficientemente antiguo => no hay huecos libres +//----------------------------------------------------------------------------------------------------- +StorageArea::Instance* StorageArea::reuse() +throw() { + if(a_holes.empty() == true) + return NULL; + + Instance* result = NULL; + Instance* front = a_holes.front(); + bool quickReuse; + + if((front->flags & Flag::Ready) == false) { + if(a_directory.size() >= a_maxSize) { // El directorio contiene los que tienen cuenta 0 + los que están activos. + result = front; + iterator ii = a_directory.find(result->object->getIndex()); + + if(ii != a_directory.end()) + a_directory.erase(ii); + + if((result->flags & Flag::Empty) == 0) { + LOGINFORMATION( + string msg("Destroy (reuse) | "); + msg += asString(); + msg += " | "; + msg += asString(result); + Logger::information(msg, ANNA_FILE_LOCATION); + ); + result->object->destroy(); + result->flags &= Flag::NoEmpty; + } + } + + quickReuse = false; + } else { + // Si la entrada se cargó en los huecos como "usado rápido" se toma + result = front; + quickReuse = true; + } + + if(result != NULL) { + a_doneReuse ++; + result->flags &= Flag::NoReady; + result->flags &= Flag::NoHasHole; + a_holes.pop_front(); + } + + LOGDEBUG( + string msg("dbos::StorageArea::reuse | "); + msg += asString(); + msg += " | "; + msg += StorageArea::asString(result); + msg += functions::asText(" | QuickReuse: ", quickReuse); + Logger::debug(msg, ANNA_FILE_LOCATION); + ); + return result; +} + +std::string StorageArea::asString(const Instance* instance) +throw() { + std::string result("Instance { "); + + if(instance == NULL) + return result += " } "; + + result += "Reference: "; + result += functions::asHexString(anna_ptrnumber_cast(instance->object)); + result += " | Index: "; + result += functions::asHexString(instance->object->getIndex()); + result += functions::asText(" | Stored: ", instance->object->a_isStored); + result += " | CopyCounter: "; + result += functions::asString((unsigned int) instance->copyCounter); + result += " | Flags ("; + result += functions::asHexString(instance->flags); + result += "):"; + + if(instance->flags == Flag::None) + result += " None"; + else { + if(instance->flags & Flag::Dirty) + result += " Dirty"; + + if(instance->flags & Flag::Incoherent) + result += " Incoherent"; + + if(instance->flags & Flag::Empty) + result += " Empty"; + + if(instance->flags & Flag::HasHole) + result += " HasHole"; + + if(instance->flags & Flag::InProgress) + result += " InProgress"; + + result += (instance->flags & Flag::Ready) ? " Ready" : " TimeWait"; + } + + return result += " }"; +} + +const char* StorageArea::AccessMode::asString(const AccessMode::_v v) +throw() { + static const char* text [] = { "ReadOnly", "ReadWrite", "ReadEver" }; + return text [v]; +} + +/**************************************************************************************** +* Bloque de memoria. +****************************************************************************************/ +StorageArea::Block::Block(ObjectAllocator objectAllocator, const Size maxSize) : + a_size(0) { + a_maxSize = std::min(8U, std::max(256U, maxSize >> 7)); + a_instances = new Instance [a_maxSize]; + + for(int i = 0; i < a_maxSize; i ++) + a_instances [i].object = (*objectAllocator)(); +} + +bool StorageArea::Holes::insert(Instance* instance, const StorageArea::Holes::Mode::_v mode) +throw() { + if(instance->copyCounter > 0) + return false; + + /* Si la instancia ya ha sido registrada en la lista de huecos, sale sin más */ + if((instance->flags & Flag::HasHole) == true) + return true; + + switch(mode) { + case Mode::ReadyToReuse: + instance->holeIterator = a_holes.insert(a_holes.begin(), instance); + instance->flags |= Flag::HasHole; + instance->flags |= Flag::Ready; + a_size ++; + break; + case Mode::TimeWait: + instance->holeIterator = a_holes.insert(a_holes.end(), instance); + instance->flags |= Flag::HasHole; + instance->flags &= Flag::NoReady; + a_size ++; + break; + } + + LOGLOCAL6( + string msg("dbos::StorageArea::Holes::insert | This: "); + msg += functions::asHexString(anna_ptrnumber_cast(this)); + msg += " | "; + msg += StorageArea::asString(instance); + Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION); + ); + return true; +} + +void StorageArea::Holes::erase(Instance* instance) +throw() { + // instance->msHoleTime = 0; + instance->flags |= Flag::Ready; + + if(instance->copyCounter != 0) + return; + + /* Si la instancia NO ha sido registrada en la lista de huecos, sale sin más */ + if((instance->flags & Flag::HasHole) == false) + return; + + LOGLOCAL6( + string msg("dbos::StorageArea::Holes::erase | This: "); + msg += functions::asHexString(anna_ptrnumber_cast(this)); + msg += " | "; + msg += StorageArea::asString(instance); + Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION); + ); + a_holes.erase(instance->holeIterator); + instance->flags &= Flag::NoHasHole; + a_size --; +} + +/**************************************************************************************** +* Iterador + +class A { +public: + A () : a (0) { cout << "C0: " << (int) this << endl; } + A (const int _a): a (_a) { cout << "C1: " << (int) this << " " << a << endl; } + A (const A& other) : a (other.a) { cout << "C2: " << (int) this << " " << a << endl; } + + A& operator = (const A& other) { + cout << "CP: " << (int) this << " " << (a = other.a) << endl; + return *this; + } + + int get () const { return a; } +private: + int a; +}; + +A fx () { return A (2000); } + +int main () +{ + A aa = 100; + A bb (200); + A xx = aa; + A cc = fx (); + + cout << "CC: " << (int) &cc << " " << cc.get () << endl; + +} + +La salida del programucho es la siguiente: + +C1: -4198808 100 +C1: -4198812 200 +C2: -4198816 100 +C1: -4198820 2000 +CC: -4198820 2000 + +Lo que quiere decir que la "cc" la crea directamente sobre la pila y la asigna en fx sin aplicar ningn +otro constructor. + +Por eso cuando hac� StorageArea::Iterator ii = xxx->begin (), maxi = xxx->end (); ..... +no estaba pasando por el contructor copia ni por ningn otro constructor. + +****************************************************************************************/ +// Dejo todo el ejemplo para que sirva de recordatorio. + diff --git a/source/dbos/internal/sccs.cpp b/source/dbos/internal/sccs.cpp new file mode 100644 index 0000000..9c2905d --- /dev/null +++ b/source/dbos/internal/sccs.cpp @@ -0,0 +1,23 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#include +#include + +#include + +#include + +anna_define_sccs_tag(dbos, 1); + +void anna::dbos::sccs::activate() +throw() { + dbms::sccs::activate(); + ModuleManager::instantiate().insert(anna_use_sccs_tag(dbos), "00"); +} +