Updated license
[anna.git] / source / dbms / Connection.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <anna/config/defines.hpp>
38 #include <anna/core/tracing/TraceMethod.hpp>
39
40 #include <anna/xml/Node.hpp>
41 #include <anna/xml/Attribute.hpp>
42
43 #include <anna/dbms/Connection.hpp>
44 #include <anna/dbms/Database.hpp>
45 #include <anna/dbms/Statement.hpp>
46 #include <anna/dbms/Statement.hpp>
47
48 using namespace std;
49 using namespace anna;
50
51 //-----------------------------------------------------------------------------------------------------------
52 // (1) Si no tiene variables de salida => consideramos que es un update, insert o delete.
53 //-----------------------------------------------------------------------------------------------------------
54 dbms::ResultCode dbms::Connection::execute(Statement* statement)
55 throw(RuntimeException, dbms::DatabaseException) {
56   if(statement == NULL) {
57     string msg(asString());
58     msg += " | Cannot execute a NULL sentence";
59     throw RuntimeException(msg, ANNA_FILE_LOCATION);
60   }
61
62   LOGMETHOD(TraceMethod ttmm("dbms::Connection", "execute", ANNA_FILE_LOCATION));
63   LOGDEBUG(
64     string msg("Using Connection | ");
65     msg += asString();
66     Logger::debug(msg, ANNA_FILE_LOCATION)
67   );
68   Guard guard(statement, "Statement from dbms::Connection::execute");
69   const Microsecond init = functions::hardwareClock();
70
71   if(statement->a_prepared == false) {
72     statement->prepare(this);
73     statement->a_prepared = true;
74   }
75
76   LOGDEBUG(
77     string msg("dbms::Connection::execute | ");
78     msg += statement->asString();
79     Logger::debug(msg, ANNA_FILE_LOCATION);
80   );
81
82   if(statement->requiresCommit() == true && a_rollbackPending == true) {        // (1)
83     string msg("dbms::Connection::execute | ");
84     msg += asString();
85     msg += " | Connection has pending ROLLBACKS for execution";
86     throw RuntimeException(msg, ANNA_FILE_LOCATION);
87   }
88
89   ResultCode result;
90   int tryCounter = 0;
91
92   while(true) {
93     result = statement->execute(this);
94
95     if(result.lostConnection() == false)
96       break;
97
98     string msg = asString();
99     msg += " | ";
100     msg += statement->asString();
101     msg += " | ";
102     msg += result.asString();
103     Logger::alert(msg, ANNA_FILE_LOCATION);
104     a_database.recover(*this, ++ tryCounter);
105   }
106
107   statement->measureTiming(init, functions::hardwareClock());
108
109   if(result.successful() == false && result.notFound() == false) {
110     string msg(asString());
111     msg += " | Sentence: ";
112     msg += statement->getName();
113     Logger::write(Logger::Error, msg, result.asString(), ANNA_FILE_LOCATION);
114   }
115
116   if(statement->requiresCommit() == true) {    // (1)
117     if(result.successful() == false) {
118       if(statement->isCritical() == true) {
119         a_rollbackPending = true;
120         throw DatabaseException(statement->getName(), result, ANNA_FILE_LOCATION);
121       }
122     } else {
123       a_commitPending ++;
124
125       if(a_maxCommitPending > 0 && a_commitPending > a_maxCommitPending)  {
126         commit();
127         a_commitPending = 0;
128         a_rollbackPending = false;
129       }
130     }
131   }
132
133   return result;
134 }
135
136 //------------------------------------------------------------------------------------------------
137 // (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas
138 // y los volcados de contexto.
139 //------------------------------------------------------------------------------------------------
140 void dbms::Connection::commit()
141 throw(RuntimeException, dbms::DatabaseException) {
142   LOGINFORMATION(
143     string msg("dbms::Connection::commit | ");
144     msg += asString();
145     Logger::information(msg, ANNA_FILE_LOCATION);
146   );
147
148   if(isAvailable() == false) {
149     string msg(asString());
150     msg += " | Unavailable connection";
151     throw RuntimeException(msg, ANNA_FILE_LOCATION);
152   }
153
154   do_commit();
155   a_commitPending = 0;                 // (1)
156   a_rollbackPending = false;
157 }
158
159 //------------------------------------------------------------------------------------------------
160 // (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas
161 // y los volcados de contexto.
162 //------------------------------------------------------------------------------------------------
163 void dbms::Connection::rollback()
164 throw() {
165   LOGWARNING(
166     string msg("dbms::Connection::rollback | ");
167     msg += asString();
168     Logger::warning(msg, ANNA_FILE_LOCATION);
169   );
170
171   if(isAvailable() == false) {
172     string msg(asString());
173     msg += " | Unavailable connection";
174     throw RuntimeException(msg, ANNA_FILE_LOCATION);
175   }
176
177   do_rollback();
178   a_commitPending = 0;
179   a_rollbackPending = false;               // (1)
180 }
181
182 void dbms::Connection::lock()
183 throw(RuntimeException) {
184   if(isAvailable() == false) {
185     string msg(asString());
186     msg += " | Unavailable connection";
187     throw RuntimeException(msg, ANNA_FILE_LOCATION);
188   }
189
190   comm::Resource::lock();
191
192   if(a_lockingCounter ++ == 0) {
193     a_commitPending = 0;
194     a_rollbackPending = false;
195
196     try {
197       if(do_beginTransaction() == true)
198         a_commitPending = 1;
199     } catch(dbms::DatabaseException& edb) {
200       throw RuntimeException(edb);
201     }
202   }
203
204   LOGDEBUG(
205     string msg("dbms::Connection::lock | ");
206     msg += asString();
207     Logger::debug(msg, ANNA_FILE_LOCATION)
208   );
209 }
210
211 void dbms::Connection::unlock()
212 throw() {
213   LOGDEBUG(
214     string msg("dbms::Connection::unlock | ");
215     msg += asString();
216     Logger::debug(msg, ANNA_FILE_LOCATION)
217   );
218
219   if(-- a_lockingCounter <= 0) {
220     a_lockingCounter = 0;
221
222     try {
223       if(a_rollbackPending == true)
224         rollback();
225       else if(a_commitPending > 0)
226         commit();
227     } catch(Exception& ex) {
228       Logger::emergency(ex.getText(), ex.getFromFile(), ex.getFromLine());
229     }
230   }
231
232   comm::Resource::unlock();
233 }
234
235 string dbms::Connection::asString() const
236 throw() {
237   string result("dbms::Connection { ");
238   result += comm::Resource::asString();
239   result += " | ";
240   result += a_database.asString();
241   result += " | user: ";
242   result += a_user;
243   result += functions::asText(" | LockingCounter: ", a_lockingCounter);
244   result += functions::asText(" | password: ******* | CommitPending: ", a_commitPending);
245   result += functions::asText(" | RollbackPending: ", a_rollbackPending);
246   return result += " }";
247 }
248
249 xml::Node* dbms::Connection::asXML(xml::Node* parent) const
250 throw() {
251   xml::Node* result = comm::Resource::asXML(parent);
252   result->createAttribute("User", a_user);
253   result->createAttribute("LockingCounter", a_lockingCounter);
254   result->createAttribute("CommitPending", a_commitPending);
255   result->createAttribute("RollbackPending", functions::asString(a_rollbackPending));
256   return result;
257 }
258