Add CCA.xml example as reference for mocking dynamic operation
[anna.git] / source / dbms / Connection.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <anna/config/defines.hpp>
10 #include <anna/core/tracing/TraceMethod.hpp>
11
12 #include <anna/xml/Node.hpp>
13 #include <anna/xml/Attribute.hpp>
14
15 #include <anna/dbms/Connection.hpp>
16 #include <anna/dbms/Database.hpp>
17 #include <anna/dbms/Statement.hpp>
18 #include <anna/dbms/Statement.hpp>
19
20 using namespace std;
21 using namespace anna;
22
23 //-----------------------------------------------------------------------------------------------------------
24 // (1) Si no tiene variables de salida => consideramos que es un update, insert o delete.
25 //-----------------------------------------------------------------------------------------------------------
26 dbms::ResultCode dbms::Connection::execute(Statement* statement)
27 throw(RuntimeException, dbms::DatabaseException) {
28   if(statement == NULL) {
29     string msg(asString());
30     msg += " | Cannot execute a NULL sentence";
31     throw RuntimeException(msg, ANNA_FILE_LOCATION);
32   }
33
34   LOGMETHOD(TraceMethod ttmm("dbms::Connection", "execute", ANNA_FILE_LOCATION));
35   LOGDEBUG(
36     string msg("Using Connection | ");
37     msg += asString();
38     Logger::debug(msg, ANNA_FILE_LOCATION)
39   );
40   Guard guard(statement, "Statement from dbms::Connection::execute");
41   const Microsecond init = functions::hardwareClock();
42
43   if(statement->a_prepared == false) {
44     statement->prepare(this);
45     statement->a_prepared = true;
46   }
47
48   LOGDEBUG(
49     string msg("dbms::Connection::execute | ");
50     msg += statement->asString();
51     Logger::debug(msg, ANNA_FILE_LOCATION);
52   );
53
54   if(statement->requiresCommit() == true && a_rollbackPending == true) {        // (1)
55     string msg("dbms::Connection::execute | ");
56     msg += asString();
57     msg += " | Connection has pending ROLLBACKS for execution";
58     throw RuntimeException(msg, ANNA_FILE_LOCATION);
59   }
60
61   ResultCode result;
62   int tryCounter = 0;
63
64   while(true) {
65     result = statement->execute(this);
66
67     if(result.lostConnection() == false)
68       break;
69
70     string msg = asString();
71     msg += " | ";
72     msg += statement->asString();
73     msg += " | ";
74     msg += result.asString();
75     Logger::alert(msg, ANNA_FILE_LOCATION);
76     a_database.recover(*this, ++ tryCounter);
77   }
78
79   statement->measureTiming(init, functions::hardwareClock());
80
81   if(result.successful() == false && result.notFound() == false) {
82     string msg(asString());
83     msg += " | Sentence: ";
84     msg += statement->getName();
85     Logger::write(Logger::Error, msg, result.asString(), ANNA_FILE_LOCATION);
86   }
87
88   if(statement->requiresCommit() == true) {    // (1)
89     if(result.successful() == false) {
90       if(statement->isCritical() == true) {
91         a_rollbackPending = true;
92         throw DatabaseException(statement->getName(), result, ANNA_FILE_LOCATION);
93       }
94     } else {
95       a_commitPending ++;
96
97       if(a_maxCommitPending > 0 && a_commitPending > a_maxCommitPending)  {
98         commit();
99         a_commitPending = 0;
100         a_rollbackPending = false;
101       }
102     }
103   }
104
105   return result;
106 }
107
108 //------------------------------------------------------------------------------------------------
109 // (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas
110 // y los volcados de contexto.
111 //------------------------------------------------------------------------------------------------
112 void dbms::Connection::commit()
113 throw(RuntimeException, dbms::DatabaseException) {
114   LOGINFORMATION(
115     string msg("dbms::Connection::commit | ");
116     msg += asString();
117     Logger::information(msg, ANNA_FILE_LOCATION);
118   );
119
120   if(isAvailable() == false) {
121     string msg(asString());
122     msg += " | Unavailable connection";
123     throw RuntimeException(msg, ANNA_FILE_LOCATION);
124   }
125
126   do_commit();
127   a_commitPending = 0;                 // (1)
128   a_rollbackPending = false;
129 }
130
131 //------------------------------------------------------------------------------------------------
132 // (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas
133 // y los volcados de contexto.
134 //------------------------------------------------------------------------------------------------
135 void dbms::Connection::rollback()
136 throw() {
137   LOGWARNING(
138     string msg("dbms::Connection::rollback | ");
139     msg += asString();
140     Logger::warning(msg, ANNA_FILE_LOCATION);
141   );
142
143   if(isAvailable() == false) {
144     string msg(asString());
145     msg += " | Unavailable connection";
146     throw RuntimeException(msg, ANNA_FILE_LOCATION);
147   }
148
149   do_rollback();
150   a_commitPending = 0;
151   a_rollbackPending = false;               // (1)
152 }
153
154 void dbms::Connection::lock()
155 throw(RuntimeException) {
156   if(isAvailable() == false) {
157     string msg(asString());
158     msg += " | Unavailable connection";
159     throw RuntimeException(msg, ANNA_FILE_LOCATION);
160   }
161
162   comm::Resource::lock();
163
164   if(a_lockingCounter ++ == 0) {
165     a_commitPending = 0;
166     a_rollbackPending = false;
167
168     try {
169       if(do_beginTransaction() == true)
170         a_commitPending = 1;
171     } catch(dbms::DatabaseException& edb) {
172       throw RuntimeException(edb);
173     }
174   }
175
176   LOGDEBUG(
177     string msg("dbms::Connection::lock | ");
178     msg += asString();
179     Logger::debug(msg, ANNA_FILE_LOCATION)
180   );
181 }
182
183 void dbms::Connection::unlock()
184 throw() {
185   LOGDEBUG(
186     string msg("dbms::Connection::unlock | ");
187     msg += asString();
188     Logger::debug(msg, ANNA_FILE_LOCATION)
189   );
190
191   if(-- a_lockingCounter <= 0) {
192     a_lockingCounter = 0;
193
194     try {
195       if(a_rollbackPending == true)
196         rollback();
197       else if(a_commitPending > 0)
198         commit();
199     } catch(Exception& ex) {
200       Logger::emergency(ex.getText(), ex.getFromFile(), ex.getFromLine());
201     }
202   }
203
204   comm::Resource::unlock();
205 }
206
207 string dbms::Connection::asString() const
208 throw() {
209   string result("dbms::Connection { ");
210   result += comm::Resource::asString();
211   result += " | ";
212   result += a_database.asString();
213   result += " | user: ";
214   result += a_user;
215   result += functions::asText(" | LockingCounter: ", a_lockingCounter);
216   result += functions::asText(" | password: ******* | CommitPending: ", a_commitPending);
217   result += functions::asText(" | RollbackPending: ", a_rollbackPending);
218   return result += " }";
219 }
220
221 xml::Node* dbms::Connection::asXML(xml::Node* parent) const
222 throw() {
223   xml::Node* result = comm::Resource::asXML(parent);
224   result->createAttribute("User", a_user);
225   result->createAttribute("LockingCounter", a_lockingCounter);
226   result->createAttribute("CommitPending", a_commitPending);
227   result->createAttribute("RollbackPending", functions::asString(a_rollbackPending));
228   return result;
229 }
230