Main Page | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

FirebirdDatabaseLayer.cpp

Go to the documentation of this file.
00001 #include "../include/FirebirdDatabaseLayer.h"
00002 #include "../include/FirebirdResultSet.h"
00003 #include "../include/DatabaseErrorCodes.h"
00004 #include "../include/DatabaseLayerException.h"
00005 
00006 #include "wx/tokenzr.h"
00007 #include "wx/regex.h"
00008 
00009 // ctor()
00010 FirebirdDatabaseLayer::FirebirdDatabaseLayer()
00011  : DatabaseLayer()
00012 {
00013   m_pDatabase = NULL;
00014   m_pTransaction = NULL;
00015 
00016   m_strServer = _("localhost");
00017   m_strDatabase = _("");
00018   m_strRole = wxEmptyString;
00019 }
00020 
00021 FirebirdDatabaseLayer::FirebirdDatabaseLayer(const wxString& strDatabase)
00022  : DatabaseLayer()
00023 {
00024   m_pDatabase = NULL;
00025   m_pTransaction = NULL;
00026 
00027   m_strServer = _("localhost");
00028   m_strUser = _("");
00029   m_strPassword = _("");
00030   m_strRole = wxEmptyString;
00031 
00032   Open(strDatabase);
00033 }
00034 
00035 FirebirdDatabaseLayer::FirebirdDatabaseLayer(const wxString& strDatabase, const wxString& strUser, const wxString& strPassword)
00036  : DatabaseLayer()
00037 {
00038   m_pDatabase = NULL;
00039   m_pTransaction = NULL;
00040 
00041   m_strServer = _("");  // assume embedded database in this case
00042   m_strUser = strUser;
00043   m_strPassword = strPassword;
00044   m_strRole = wxEmptyString;
00045 
00046   Open(strDatabase);
00047 }
00048 
00049 FirebirdDatabaseLayer::FirebirdDatabaseLayer(const wxString& strServer, const wxString& strDatabase, const wxString& strUser, const wxString& strPassword)
00050  : DatabaseLayer()
00051 {
00052   m_pDatabase = NULL;
00053   m_pTransaction = NULL;
00054 
00055   m_strServer = strServer;
00056   m_strUser = strUser;
00057   m_strPassword = strPassword;
00058   m_strRole = wxEmptyString;
00059 
00060   Open(strDatabase);
00061 }
00062 
00063 FirebirdDatabaseLayer::FirebirdDatabaseLayer(const wxString& strServer, const wxString& strDatabase, const wxString& strUser, const wxString& strPassword, const wxString& strRole)
00064  : DatabaseLayer()
00065 {
00066   m_pDatabase = NULL;
00067   m_pTransaction = NULL;
00068 
00069   m_strServer = strServer;
00070   m_strUser = strUser;
00071   m_strPassword = strPassword;
00072   m_strRole = strRole;
00073 
00074   Open(strDatabase);
00075 }
00076 
00077 // dtor()
00078 FirebirdDatabaseLayer::~FirebirdDatabaseLayer()
00079 {
00080   Close();
00081 }
00082 
00083 // open database
00084 bool FirebirdDatabaseLayer::Open(const wxString& strDatabase)
00085 {
00086   m_strDatabase = strDatabase;
00087   return Open();
00088 }
00089 
00090 bool FirebirdDatabaseLayer::Open(const wxString& strDatabase, const wxString& strUser, const wxString& strPassword)
00091 {
00092   m_strUser = strUser;
00093   m_strPassword = strPassword;
00094 
00095   return Open(strDatabase);
00096 }
00097 
00098 bool FirebirdDatabaseLayer::Open(const wxString& strServer, const wxString& strDatabase, const wxString& strUser, const wxString& strPassword)
00099 {
00100   m_strServer = strServer;
00101   m_strUser = strUser;
00102   m_strPassword = strPassword;
00103 
00104   return Open(strDatabase);
00105 }
00106 
00107 bool FirebirdDatabaseLayer::Open()
00108 {
00109   ResetErrorCodes();
00110 
00111   // Set up the connection parameter string
00112   //int nParameterStringLength;
00113   //char szParameterString[512];
00114   //char* pParameterString = szParameterString;
00115 
00116   // Firebird accepts all the other ISO_8859 encoding names but ISO-8859-1 needs a little tweaking to be recognized
00117   //wxString encodingName = wxLocale::GetSystemEncodingName();
00118   
00119   //wxCharBuffer systemEncoding;
00120   //if (encodingName == wxT("ISO-8859-1"))
00121   // systemEncoding = "ISO8859_1";
00122   //else
00123   // systemEncoding = encodingName.mb_str(*wxConvCurrent);
00124   wxCharBuffer systemEncoding = "UTF-8";
00125 
00126   wxCSConv conv(_("UTF-8"));
00127   SetEncoding(&conv);
00128   
00129   //char* pDpb = new char(512);
00130   char* pDpb;
00131   short nDpbLength = 0;
00132   wxCharBuffer userCharBuffer = ConvertToUnicodeStream(m_strUser);
00133   wxCharBuffer passwordCharBuffer = ConvertToUnicodeStream(m_strPassword);
00134   wxCharBuffer roleCharBuffer = ConvertToUnicodeStream(m_strRole);
00135   
00136   pDpb = (char*)0;
00137   
00138   if (m_strRole == wxEmptyString)
00139   {
00140     isc_expand_dpb(&pDpb, &nDpbLength, isc_dpb_user_name, (const char*)userCharBuffer,
00141         isc_dpb_password, (const char*)passwordCharBuffer, isc_dpb_lc_ctype, (const char*)systemEncoding, NULL);
00142   }
00143   else
00144   {
00145     isc_expand_dpb(&pDpb, &nDpbLength, isc_dpb_user_name, (const char*)userCharBuffer,
00146         isc_dpb_password, (const char*)passwordCharBuffer, isc_dpb_lc_ctype, (const char*)systemEncoding, 
00147         isc_dpb_sql_role_name, (const char*)roleCharBuffer, NULL);
00148   }
00149     
00150   // Combine the server and databsae path strings to pass into the isc_attach_databse function
00151   wxString strDatabaseUrl;
00152   if (m_strServer.IsEmpty())
00153     strDatabaseUrl = m_strDatabase; // Embedded database, just supply the file name
00154   else
00155     strDatabaseUrl = m_strServer + _(":") + m_strDatabase;
00156  
00157   m_pDatabase = NULL;
00158   m_pTransaction = NULL;
00159 
00160   wxCharBuffer urlBuffer = ConvertToUnicodeStream(strDatabaseUrl);
00161   //int nReturn = isc_attach_database(m_Status, 0, urlBuffer, &m_pDatabase, nParameterStringLength, szParameterString);
00162   int nReturn = isc_attach_database(m_Status, 0, (char*)(const char*)urlBuffer, &m_pDatabase, nDpbLength, pDpb);
00163   if (nReturn != 0)
00164   {
00165     InterpretErrorCodes();
00166     ThrowDatabaseException();
00167 
00168     return false;
00169   }
00170   return true;
00171 }
00172 
00173 // close database  
00174 bool FirebirdDatabaseLayer::Close()
00175 {
00176   CloseResultSets();
00177   CloseStatements();
00178 
00179   if (m_pDatabase)
00180   {
00181     int nReturn = isc_detach_database(m_Status, &m_pDatabase);
00182     m_pDatabase = 0L;
00183     if (nReturn != 0)
00184     {
00185       InterpretErrorCodes();
00186       ThrowDatabaseException();
00187       return false;
00188     }
00189   }
00190 
00191   return true;
00192 }
00193 
00194 // transaction support
00195 void FirebirdDatabaseLayer::BeginTransaction()
00196 {
00197   ResetErrorCodes();
00198 
00199   wxLogDebug(_("Beginning transaction\n"));
00200   if (m_pDatabase)
00201   {
00202     m_pTransaction = 0L;
00203     int nReturn = isc_start_transaction(m_Status, &m_pTransaction, 1, &m_pDatabase, 0 /*tpb_length*/, NULL/*tpb*/);
00204     if (nReturn != 0)
00205     {
00206       InterpretErrorCodes();
00207       ThrowDatabaseException();
00208     }
00209   }
00210 }
00211 
00212 void FirebirdDatabaseLayer::Commit()
00213 {
00214   ResetErrorCodes();
00215 
00216   wxLogDebug(_("Committing transaction\n"));
00217   if (m_pDatabase && m_pTransaction)
00218   {
00219     int nReturn = isc_commit_transaction(m_Status, &m_pTransaction);
00220     // We're done with the transaction, so set it to NULL so that we know that a new transaction must be started if we run any queries
00221     m_pTransaction = NULL;
00222     if (nReturn != 0)
00223     {
00224       InterpretErrorCodes();
00225       ThrowDatabaseException();
00226     }
00227   }
00228 }
00229 
00230 void FirebirdDatabaseLayer::RollBack()
00231 {
00232   ResetErrorCodes();
00233 
00234   wxLogDebug(_("Rolling back transaction\n"));
00235   if (m_pDatabase && m_pTransaction)
00236   {
00237     int nReturn = isc_rollback_transaction(m_Status, &m_pTransaction);
00238     // We're done with the transaction, so set it to NULL so that we know that a new transaction must be started if we run any queries
00239     m_pTransaction = NULL;
00240     if (nReturn != 0)
00241     {
00242       InterpretErrorCodes();
00243       ThrowDatabaseException();
00244     }
00245   }
00246 }
00247 
00248 // query database
00249 bool FirebirdDatabaseLayer::RunQuery(const wxString& strQuery, bool bParseQuery)
00250 {
00251   ResetErrorCodes();
00252   if (m_pDatabase != NULL)
00253   {
00254     wxCharBuffer sqlDebugBuffer = ConvertToUnicodeStream(strQuery);
00255     wxLogDebug(_("Running query: \"%s\"\n"), (const char*)sqlDebugBuffer);
00256 
00257     wxArrayString QueryArray;
00258     if (bParseQuery)
00259       QueryArray = ParseQueries(strQuery);
00260     else
00261       QueryArray.push_back(strQuery);
00262 
00263     wxArrayString::iterator start = QueryArray.begin();
00264     wxArrayString::iterator stop = QueryArray.end();
00265 
00266     if (QueryArray.size() > 0)
00267     {
00268       bool bQuickieTransaction = false;
00269     
00270       if (m_pTransaction == NULL)
00271       {
00272         // If there's no transaction is progress, run this as a quick one-timer transaction
00273         bQuickieTransaction = true;
00274       }
00275 
00276       if (bQuickieTransaction)
00277       {
00278         BeginTransaction();
00279         if (GetErrorCode() != DATABASE_LAYER_OK)
00280         {
00281           wxLogError(_("Unable to start transaction"));
00282           ThrowDatabaseException();
00283           return false;
00284         }
00285       }
00286 
00287       while (start != stop)
00288       {
00289         wxCharBuffer sqlBuffer = ConvertToUnicodeStream(*start);
00290         //int nReturn = isc_dsql_execute_immediate(m_Status, &m_pDatabase, &m_pTransaction, 0, (char*)(const char*)sqlBuffer, 1, NULL);
00291         int nReturn = isc_dsql_execute_immediate(m_Status, &m_pDatabase, &m_pTransaction, GetEncodedStreamLength(*start), (char*)(const char*)sqlBuffer, 1, NULL);
00292         if (nReturn != 0)
00293         {
00294           InterpretErrorCodes();
00295           // Manually try to rollback the transaction rather than calling the member RollBack function
00296           //  so that we can ignore the error messages
00297           isc_rollback_transaction(m_Status, &m_pTransaction);
00298           m_pTransaction = NULL;
00299 
00300           ThrowDatabaseException();
00301           return false;
00302         }
00303         start++;
00304       }
00305 
00306       if (bQuickieTransaction)
00307       {
00308         Commit();
00309         if (GetErrorCode() != DATABASE_LAYER_OK)
00310         {
00311           ThrowDatabaseException();
00312           return false;
00313         }
00314       }
00315     }
00316 
00317     return true;
00318   }
00319   else
00320   {
00321     wxLogError(_("Database handle is NULL"));
00322     return false;
00323   }
00324 }
00325 
00326 DatabaseResultSet* FirebirdDatabaseLayer::RunQueryWithResults(const wxString& strQuery)
00327 {
00328   ResetErrorCodes();
00329   if (m_pDatabase != NULL)
00330   {
00331     wxCharBuffer sqlDebugBuffer = ConvertToUnicodeStream(strQuery);
00332     wxLogDebug(_("Running query: \"%s\"\n"), (const char*)sqlDebugBuffer);
00333     
00334     wxArrayString QueryArray = ParseQueries(strQuery);
00335 
00336     if (QueryArray.size() > 0)
00337     {
00338       bool bQuickieTransaction = false;
00339     
00340       if (m_pTransaction == NULL)
00341       {
00342         // If there's no transaction is progress, run this as a quick one-timer transaction
00343         bQuickieTransaction = true;
00344       }
00345 
00346       if (QueryArray.size() > 1)
00347       {
00348         if (bQuickieTransaction)
00349         {
00350           BeginTransaction();
00351           if (GetErrorCode() != DATABASE_LAYER_OK)
00352           {
00353             wxLogError(_("Unable to start transaction"));
00354             ThrowDatabaseException();
00355             return NULL;
00356           }
00357         }
00358       
00359         // Assume that only the last statement in the array returns the result set
00360         for (unsigned int i=0; i<QueryArray.size()-1; i++)
00361         {
00362           RunQuery(QueryArray[i], false);
00363           if (GetErrorCode() != DATABASE_LAYER_OK)
00364           {
00365             ThrowDatabaseException();
00366             return NULL;
00367           }
00368         }
00369       
00370         // Now commit all the previous queries before calling the query that returns a result set
00371         if (bQuickieTransaction)
00372         {
00373           Commit();
00374           if (GetErrorCode() != DATABASE_LAYER_OK)
00375           {
00376             ThrowDatabaseException();
00377             return NULL;
00378           }
00379         }
00380       } // End check if there are more than one query in the array
00381       
00382       isc_tr_handle pQueryTransaction = NULL;
00383       bool bManageTransaction = false;
00384       if (bQuickieTransaction)
00385       {
00386         bManageTransaction = true;
00387         int nReturn = isc_start_transaction(m_Status, &pQueryTransaction, 1, &m_pDatabase, 0 /*tpb_length*/, NULL/*tpb*/);
00388         if (nReturn != 0)
00389         {
00390           InterpretErrorCodes();
00391           ThrowDatabaseException();
00392         }
00393       }
00394       else
00395       {
00396         pQueryTransaction = m_pTransaction;
00397       }
00398       
00399       isc_stmt_handle pStatement = NULL;
00400       int nReturn = isc_dsql_allocate_statement(m_Status, &m_pDatabase, &pStatement);
00401       if (nReturn != 0)
00402       {
00403         InterpretErrorCodes();
00404 
00405         // Manually try to rollback the transaction rather than calling the member RollBack function
00406         //  so that we can ignore the error messages
00407         isc_rollback_transaction(m_Status, &pQueryTransaction);
00408 
00409         ThrowDatabaseException();
00410         return NULL;
00411       }
00412       
00413       wxCharBuffer sqlBuffer = ConvertToUnicodeStream(QueryArray[QueryArray.size()-1]);
00414       nReturn = isc_dsql_prepare(m_Status, &pQueryTransaction, &pStatement, 0, (char*)(const char*)sqlBuffer, 1, NULL);
00415       if (nReturn != 0)
00416       {
00417         InterpretErrorCodes();
00418 
00419         // Manually try to rollback the transaction rather than calling the member RollBack function
00420         //  so that we can ignore the error messages
00421         isc_rollback_transaction(m_Status, &pQueryTransaction);
00422 
00423         ThrowDatabaseException();
00424         return NULL;
00425       }
00426 
00427 //--------------------------------------------------------------
00428 
00429       XSQLDA* pOutputSqlda = (XSQLDA*)malloc(XSQLDA_LENGTH(1));
00430       pOutputSqlda->sqln = 1;
00431       pOutputSqlda->version = SQLDA_VERSION1;
00432 
00433       // Make sure that we have enough space allocated for the result set
00434       nReturn = isc_dsql_describe(m_Status, &pStatement, 1, pOutputSqlda);
00435       if (nReturn != 0)
00436       {
00437         free(pOutputSqlda);
00438         InterpretErrorCodes();
00439 
00440         // Manually try to rollback the transaction rather than calling the member RollBack function
00441         //  so that we can ignore the error messages
00442         isc_rollback_transaction(m_Status, &pQueryTransaction);
00443 
00444         ThrowDatabaseException();
00445         return NULL;
00446       }
00447 
00448       if (pOutputSqlda->sqld > pOutputSqlda->sqln)
00449       {
00450         int nColumns = pOutputSqlda->sqld;
00451         free(pOutputSqlda);
00452         pOutputSqlda = (XSQLDA*)malloc(XSQLDA_LENGTH(nColumns));
00453         pOutputSqlda->sqln = nColumns;
00454         pOutputSqlda->version = SQLDA_VERSION1;
00455         nReturn = isc_dsql_describe(m_Status, &pStatement, 1, pOutputSqlda);
00456         if (nReturn != 0)
00457         {
00458           free(pOutputSqlda);
00459           InterpretErrorCodes();
00460 
00461           // Manually try to rollback the transaction rather than calling the member RollBack function
00462           //  so that we can ignore the error messages
00463           isc_rollback_transaction(m_Status, &pQueryTransaction);
00464 
00465           ThrowDatabaseException();
00466           return NULL;
00467         }
00468       }
00469 
00470       // Create the result set object
00471       FirebirdResultSet* pResultSet = new FirebirdResultSet(m_pDatabase, pQueryTransaction, pStatement, pOutputSqlda, true, bManageTransaction);
00472       pResultSet->SetEncoding(GetEncoding());
00473       if (pResultSet->GetErrorCode() != DATABASE_LAYER_OK)
00474       {
00475         SetErrorCode(pResultSet->GetErrorCode());
00476         SetErrorMessage(pResultSet->GetErrorMessage());
00477     
00478         // Manually try to rollback the transaction rather than calling the member RollBack function
00479         //  so that we can ignore the error messages
00480         isc_rollback_transaction(m_Status, &pQueryTransaction);
00481 
00482         // Wrap the result set deletion in try/catch block if using exceptions.
00483         //We want to make sure the original error gets to the user
00484 #ifndef DONT_USE_DATABASE_LAYER_EXCEPTIONS
00485         try
00486         {
00487 #endif
00488         delete pResultSet;
00489 #ifndef DONT_USE_DATABASE_LAYER_EXCEPTIONS
00490         }
00491         catch (DatabaseLayerException& e)
00492         {
00493         }
00494 #endif
00495 
00496         ThrowDatabaseException();
00497       }
00498   
00499       // Now execute the SQL
00500       nReturn = isc_dsql_execute(m_Status, &pQueryTransaction, &pStatement, 1, NULL);
00501       if (nReturn != 0)
00502       {
00503         InterpretErrorCodes();
00504 
00505         // Manually try to rollback the transaction rather than calling the member RollBack function
00506         //  so that we can ignore the error messages
00507         isc_rollback_transaction(m_Status, &pQueryTransaction);
00508 
00509         // Wrap the result set deletion in try/catch block if using exceptions.
00510         //  We want to make sure the isc_dsql_execute error gets to the user
00511 #ifndef DONT_USE_DATABASE_LAYER_EXCEPTIONS
00512         try
00513         {
00514 #endif
00515         delete pResultSet;
00516 #ifndef DONT_USE_DATABASE_LAYER_EXCEPTIONS
00517         }
00518         catch (DatabaseLayerException& e)
00519         {
00520         }
00521 #endif
00522     
00523         ThrowDatabaseException();
00524         return NULL;
00525       }
00526       
00527 //--------------------------------------------------------------
00528 
00529       LogResultSetForCleanup(pResultSet);
00530       return pResultSet;
00531     }
00532     else
00533       return NULL;
00534   }
00535   else
00536   {
00537     wxLogError(_("Database handle is NULL"));
00538     return NULL;
00539   }
00540 }
00541 
00542 PreparedStatement* FirebirdDatabaseLayer::PrepareStatement(const wxString& strQuery)
00543 {
00544   ResetErrorCodes();
00545   
00546   FirebirdPreparedStatement* pStatement = FirebirdPreparedStatement::CreateStatement(m_pDatabase, m_pTransaction, strQuery, GetEncoding());
00547   if (pStatement && (pStatement->GetErrorCode() != DATABASE_LAYER_OK))
00548   {
00549     SetErrorCode(pStatement->GetErrorCode());
00550     SetErrorMessage(pStatement->GetErrorMessage());
00551     wxDELETE(pStatement); // This sets the pointer to NULL after deleting it
00552 
00553     ThrowDatabaseException();
00554   }
00555 
00556   LogStatementForCleanup(pStatement);
00557   return pStatement;
00558 }
00559 
00560 int FirebirdDatabaseLayer::TranslateErrorCode(int nCode)
00561 {
00562   // Ultimately, this will probably be a map of Firebird database error code values to DatabaseLayer values
00563   // For now though, we'll just return the original error code
00564   return nCode;
00565 }
00566 
00567 wxString FirebirdDatabaseLayer::TranslateErrorCodeToString(int nCode, ISC_STATUS_ARRAY status)
00568 {
00569   char szError[512];
00570   wxString strReturn;
00571   if (nCode < -900)  // Error codes less than -900 indicate that it wasn't a SQL error but an ibase system error
00572   {
00573     long *pVector = status;
00574     isc_interprete(szError, &pVector);
00575     //puts(szError);
00576     strReturn = wxString::Format(_("%s\n"), szError);
00577     while (isc_interprete(szError, &pVector))
00578     {
00579       //puts(szError);
00580       strReturn += wxString::Format(_("%s\n"), szError);
00581     }
00582   }
00583   else
00584   {
00585     isc_sql_interprete(nCode, szError, sizeof(szError));
00586     wxCharBuffer systemEncoding = wxLocale::GetSystemEncodingName().mb_str(*wxConvCurrent);
00587     strReturn = DatabaseStringConverter::ConvertFromUnicodeStream(szError, (const char*)systemEncoding);
00588   }
00589   //puts(strReturn.mb_str(wxConvUTF8));
00590   return strReturn;
00591 }
00592 
00593 void FirebirdDatabaseLayer::InterpretErrorCodes()
00594 {
00595   wxLogDebug(_("FirebirdDatabaseLayer::InterpretErrorCodes()\n"));
00596 
00597   long nSqlCode = isc_sqlcode(m_Status);
00598   SetErrorMessage(FirebirdDatabaseLayer::TranslateErrorCodeToString(nSqlCode, m_Status));
00599   if (nSqlCode < -900)  // Error codes less than -900 indicate that it wasn't a SQL error but an ibase system error
00600   {
00601     SetErrorCode(FirebirdDatabaseLayer::TranslateErrorCode(m_Status[1]));
00602   }
00603   else
00604   {
00605     SetErrorCode(FirebirdDatabaseLayer::TranslateErrorCode(nSqlCode));
00606   }
00607 }
00608 

Generated on Sat May 13 17:31:34 2006 for databaselayer by  doxygen 1.4.1