1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "Blob.hxx"
21 #include "Catalog.hxx"
22 #include "Clob.hxx"
23 #include "Connection.hxx"
24 #include "DatabaseMetaData.hxx"
25 #include "PreparedStatement.hxx"
26 #include "Statement.hxx"
27 #include "Util.hxx"
28 
29 #include <stdexcept>
30 
31 #include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
32 #include <com/sun/star/embed/ElementModes.hpp>
33 #include <com/sun/star/io/XStream.hpp>
34 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
35 #include <com/sun/star/sdbc/SQLException.hpp>
36 #include <com/sun/star/sdbc/XRow.hpp>
37 #include <com/sun/star/sdbc/TransactionIsolation.hpp>
38 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
39 #include <com/sun/star/ucb/XSimpleFileAccess2.hpp>
40 
41 #include <connectivity/dbexception.hxx>
42 #include <strings.hrc>
43 #include <resource/sharedresources.hxx>
44 
45 #include <comphelper/processfactory.hxx>
46 #include <comphelper/servicehelper.hxx>
47 #include <comphelper/storagehelper.hxx>
48 #include <cppuhelper/exc_hlp.hxx>
49 #include <unotools/tempfile.hxx>
50 
51 #include <osl/file.hxx>
52 #include <rtl/strbuf.hxx>
53 #include <sal/log.hxx>
54 
55 using namespace connectivity::firebird;
56 using namespace connectivity;
57 
58 using namespace ::osl;
59 
60 using namespace ::com::sun::star;
61 using namespace ::com::sun::star::beans;
62 using namespace ::com::sun::star::container;
63 using namespace ::com::sun::star::document;
64 using namespace ::com::sun::star::embed;
65 using namespace ::com::sun::star::io;
66 using namespace ::com::sun::star::lang;
67 using namespace ::com::sun::star::sdbc;
68 using namespace ::com::sun::star::sdbcx;
69 using namespace ::com::sun::star::uno;
70 
71 /**
72  * Location within the .odb that an embedded .fdb will be stored.
73  * Only relevant for embedded dbs.
74  */
75 constexpr OUStringLiteral our_sFDBLocation( u"firebird.fdb" );
76 /**
77  * Older version of LO may store the database in a .fdb file
78  */
79 constexpr OUStringLiteral our_sFBKLocation( u"firebird.fbk" );
80 
81 Connection::Connection()
82     : Connection_BASE(m_aMutex)
83     , m_bIsEmbedded(false)
84     , m_bIsFile(false)
85     , m_bIsAutoCommit(true)
86     , m_bIsReadOnly(false)
87     , m_aTransactionIsolation(TransactionIsolation::REPEATABLE_READ)
88 #if SAL_TYPES_SIZEOFPOINTER == 8
89     , m_aDBHandle(0)
90     , m_aTransactionHandle(0)
91 #else
92     , m_aDBHandle(nullptr)
93     , m_aTransactionHandle(nullptr)
94 #endif
95     , m_xCatalog(nullptr)
96     , m_xMetaData(nullptr)
97 {
98 }
99 
100 Connection::~Connection()
101 {
102     if(!isClosed())
103         close();
104 }
105 
106 namespace {
107 
108 struct ConnectionGuard
109 {
110     oslInterlockedCount& m_refCount;
111     explicit ConnectionGuard(oslInterlockedCount& refCount)
112         : m_refCount(refCount)
113     {
114         osl_atomic_increment(&m_refCount);
115     }
116     ~ConnectionGuard()
117     {
118         osl_atomic_decrement(&m_refCount);
119     }
120 };
121 
122 }
123 
124 void Connection::construct(const OUString& url, const Sequence< PropertyValue >& info)
125 {
126     ConnectionGuard aGuard(m_refCount);
127 
128     try
129     {
130         m_sConnectionURL = url;
131 
132         bool bIsNewDatabase = false;
133         // the database may be stored as an
134         // fdb file in older versions
135         bool bIsFdbStored = false;
136         if (url == "sdbc:embedded:firebird")
137         {
138             m_bIsEmbedded = true;
139 
140             const PropertyValue* pIter = info.getConstArray();
141             const PropertyValue* pEnd = pIter + info.getLength();
142 
143             for (;pIter != pEnd; ++pIter)
144             {
145                 if ( pIter->Name == "Storage" )
146                 {
147                     m_xEmbeddedStorage.set(pIter->Value,UNO_QUERY);
148                 }
149                 else if ( pIter->Name == "Document" )
150                 {
151                     pIter->Value >>= m_xParentDocument;
152                 }
153             }
154 
155             if ( !m_xEmbeddedStorage.is() )
156             {
157                 ::connectivity::SharedResources aResources;
158                 const OUString sMessage = aResources.getResourceString(STR_NO_STORAGE);
159                 ::dbtools::throwGenericSQLException(sMessage ,*this);
160             }
161 
162             bIsNewDatabase = !m_xEmbeddedStorage->hasElements();
163 
164             m_pDatabaseFileDir.reset(new ::utl::TempFile(nullptr, true));
165             m_pDatabaseFileDir->EnableKillingFile();
166             m_sFirebirdURL = m_pDatabaseFileDir->GetFileName() + "/firebird.fdb";
167             m_sFBKPath = m_pDatabaseFileDir->GetFileName() + "/firebird.fbk";
168 
169             SAL_INFO("connectivity.firebird", "Temporary .fdb location:  " << m_sFirebirdURL);
170 
171             if (!bIsNewDatabase)
172             {
173                 if (m_xEmbeddedStorage->hasByName(our_sFBKLocation) &&
174                     m_xEmbeddedStorage->isStreamElement(our_sFBKLocation))
175                 {
176                     SAL_INFO("connectivity.firebird", "Extracting* .fbk from .odb" );
177                     loadDatabaseFile(our_sFBKLocation, m_sFBKPath);
178                 }
179                 else if(m_xEmbeddedStorage->hasByName(our_sFDBLocation) &&
180                         m_xEmbeddedStorage->isStreamElement(our_sFDBLocation))
181                 {
182                     SAL_INFO("connectivity.firebird", "Found .fdb instead of .fbk");
183                     bIsFdbStored = true;
184                     loadDatabaseFile(our_sFDBLocation, m_sFirebirdURL);
185                 }
186                 else
187                 {
188                     // There might be files which are not firebird databases.
189                     // This is not a problem.
190                     bIsNewDatabase = true;
191                 }
192             }
193             // TODO: Get DB properties from XML
194 
195         }
196         // External file AND/OR remote connection
197         else if (url.startsWith("sdbc:firebird:"))
198         {
199             m_sFirebirdURL = url.copy(OUString("sdbc:firebird:").getLength());
200             if (m_sFirebirdURL.startsWith("file://"))
201             {
202                 m_bIsFile = true;
203                 uno::Reference< ucb::XSimpleFileAccess > xFileAccess =
204                     ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext());
205                 if (!xFileAccess->exists(m_sFirebirdURL))
206                     bIsNewDatabase = true;
207 
208                 osl::FileBase::getSystemPathFromFileURL(m_sFirebirdURL, m_sFirebirdURL);
209             }
210         }
211 
212         std::string dpbBuffer;
213         {
214             OString userName;
215             OString userPassword;
216 
217             dpbBuffer.push_back(isc_dpb_version1);
218             dpbBuffer.push_back(isc_dpb_sql_dialect);
219             dpbBuffer.push_back(1); // 1 byte long
220             dpbBuffer.push_back(SQL_DIALECT_CURRENT);
221 
222             // set UTF8 as default character set of the database
223             const char sCharset[] = "UTF8";
224             dpbBuffer.push_back(isc_dpb_set_db_charset);
225             dpbBuffer.push_back(sizeof(sCharset) - 1);
226             dpbBuffer.append(sCharset);
227             // set UTF8 as default character set of the connection
228             dpbBuffer.push_back(isc_dpb_lc_ctype);
229             dpbBuffer.push_back(sizeof(sCharset) - 1);
230             dpbBuffer.append(sCharset);
231 
232             // Do any more dpbBuffer additions here
233 
234             if (m_bIsEmbedded || m_bIsFile)
235             {
236                 userName = "sysdba";
237                 userPassword = "masterkey";
238             }
239             else
240             {
241                 for (const auto& rIter : info)
242                 {
243                     if (rIter.Name == "user")
244                     {
245                         if (OUString value; rIter.Value >>= value)
246                             userName = OUStringToOString(value, RTL_TEXTENCODING_UTF8);
247                     }
248                     else if (rIter.Name == "password")
249                     {
250                         if (OUString value; rIter.Value >>= value)
251                             userPassword = OUStringToOString(value, RTL_TEXTENCODING_UTF8);
252                     }
253                 }
254             }
255 
256             if (!userName.isEmpty())
257             {
258                 const sal_Int32 nMaxUsername = 255; //max size
259                 int nUsernameLength = std::min(userName.getLength(), nMaxUsername);
260                 dpbBuffer.push_back(isc_dpb_user_name);
261                 dpbBuffer.push_back(nUsernameLength);
262                 dpbBuffer.append(userName.getStr(), nUsernameLength);
263             }
264 
265             if (!userPassword.isEmpty())
266             {
267                 const sal_Int32 nMaxPassword = 255; //max size
268                 int nPasswordLength = std::min(userPassword.getLength(), nMaxPassword);
269                 dpbBuffer.push_back(isc_dpb_password);
270                 dpbBuffer.push_back(nPasswordLength);
271                 dpbBuffer.append(userPassword.getStr(), nPasswordLength);
272             }
273         }
274 
275         // use isc_dpb_utf8_filename to identify encoding of filenames
276         dpbBuffer.push_back(isc_dpb_utf8_filename);
277         dpbBuffer.push_back(0); // no filename here, it is passed to functions directly
278 
279         ISC_STATUS_ARRAY status;            /* status vector */
280         ISC_STATUS aErr;
281         const OString sFirebirdURL = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8);
282         if (bIsNewDatabase)
283         {
284             aErr = isc_create_database(status,
285                                        sFirebirdURL.getLength(),
286                                        sFirebirdURL.getStr(),
287                                        &m_aDBHandle,
288                                        dpbBuffer.size(),
289                                        dpbBuffer.c_str(),
290                                        0);
291             if (aErr)
292             {
293                 evaluateStatusVector(status, u"isc_create_database", *this);
294             }
295         }
296         else
297         {
298             if (m_bIsEmbedded && !bIsFdbStored) // We need to restore the .fbk first
299             {
300                 runBackupService(isc_action_svc_restore);
301             }
302 
303             aErr = isc_attach_database(status,
304                                        sFirebirdURL.getLength(),
305                                        sFirebirdURL.getStr(),
306                                        &m_aDBHandle,
307                                        dpbBuffer.size(),
308                                        dpbBuffer.c_str());
309             if (aErr)
310             {
311                 evaluateStatusVector(status, u"isc_attach_database", *this);
312             }
313         }
314 
315         if (m_bIsEmbedded) // Add DocumentEventListener to save the .fdb as needed
316         {
317             // We need to attach as a document listener in order to be able to store
318             // the temporary db back into the .odb when saving
319             uno::Reference<XDocumentEventBroadcaster> xBroadcaster(m_xParentDocument, UNO_QUERY);
320 
321             if (xBroadcaster.is())
322                 xBroadcaster->addDocumentEventListener(this);
323             else
324                 assert(false);
325         }
326     }
327     catch (const Exception&)
328     {
329         throw;
330     }
331     catch (const std::exception&)
332     {
333         throw;
334     }
335     catch (...) // const Firebird::Exception& firebird throws this, but doesn't install the fb_exception.h that declares it
336 
337     {
338         throw std::runtime_error("Generic Firebird::Exception");
339     }
340 }
341 
342 void Connection::notifyDatabaseModified()
343 {
344     if (m_xParentDocument.is()) // Only true in embedded mode
345         m_xParentDocument->setModified(true);
346 }
347 
348 //----- XServiceInfo ---------------------------------------------------------
349 IMPLEMENT_SERVICE_INFO(Connection, "com.sun.star.sdbc.drivers.firebird.Connection",
350                                                     "com.sun.star.sdbc.Connection")
351 
352 Reference< XBlob> Connection::createBlob(ISC_QUAD const * pBlobId)
353 {
354     MutexGuard aGuard(m_aMutex);
355     checkDisposed(Connection_BASE::rBHelper.bDisposed);
356 
357     Reference< XBlob > xReturn = new Blob(&m_aDBHandle,
358                                           &m_aTransactionHandle,
359                                           *pBlobId);
360 
361     m_aStatements.push_back(WeakReferenceHelper(xReturn));
362     return xReturn;
363 }
364 
365 Reference< XClob> Connection::createClob(ISC_QUAD const * pBlobId)
366 {
367     MutexGuard aGuard(m_aMutex);
368     checkDisposed(Connection_BASE::rBHelper.bDisposed);
369 
370     Reference< XClob > xReturn = new Clob(&m_aDBHandle,
371                                           &m_aTransactionHandle,
372                                           *pBlobId);
373 
374     m_aStatements.push_back(WeakReferenceHelper(xReturn));
375     return xReturn;
376 }
377 
378 //----- XUnoTunnel ----------------------------------------------------------
379 // virtual
380 sal_Int64 SAL_CALL Connection::getSomething(const css::uno::Sequence<sal_Int8>& rId)
381 {
382     return comphelper::getSomethingImpl(rId, this);
383 }
384 
385 // static
386 css::uno::Sequence<sal_Int8> Connection::getUnoTunnelId()
387 {
388     static const comphelper::UnoIdInit implId;
389     return implId.getSeq();
390 }
391 
392 //----- XConnection ----------------------------------------------------------
393 Reference< XStatement > SAL_CALL Connection::createStatement( )
394 {
395     MutexGuard aGuard( m_aMutex );
396     checkDisposed(Connection_BASE::rBHelper.bDisposed);
397 
398     // the pre
399     if(m_aTypeInfo.empty())
400         buildTypeInfo();
401 
402     // create a statement
403     // the statement can only be executed once
404     Reference< XStatement > xReturn = new OStatement(this);
405     m_aStatements.push_back(WeakReferenceHelper(xReturn));
406     return xReturn;
407 }
408 
409 Reference< XPreparedStatement > SAL_CALL Connection::prepareStatement(
410             const OUString& _sSql)
411 {
412     SAL_INFO("connectivity.firebird", "prepareStatement() "
413              "called with sql: " << _sSql);
414     MutexGuard aGuard(m_aMutex);
415     checkDisposed(Connection_BASE::rBHelper.bDisposed);
416 
417     if(m_aTypeInfo.empty())
418         buildTypeInfo();
419 
420     Reference< XPreparedStatement > xReturn = new OPreparedStatement(this, _sSql);
421     m_aStatements.push_back(WeakReferenceHelper(xReturn));
422 
423     return xReturn;
424 }
425 
426 Reference< XPreparedStatement > SAL_CALL Connection::prepareCall(
427                 const OUString& _sSql )
428 {
429     SAL_INFO("connectivity.firebird", "prepareCall(). "
430              "_sSql: " << _sSql);
431 
432     MutexGuard aGuard( m_aMutex );
433     checkDisposed(Connection_BASE::rBHelper.bDisposed);
434 
435     // OUString sSqlStatement (transformPreparedStatement( _sSql ));
436 
437     // not implemented yet :-) a task to do
438     return nullptr;
439 }
440 
441 OUString SAL_CALL Connection::nativeSQL( const OUString& _sSql )
442 {
443     MutexGuard aGuard( m_aMutex );
444     // We do not need to adapt the SQL for Firebird atm.
445     return _sSql;
446 }
447 
448 void SAL_CALL Connection::setAutoCommit( sal_Bool autoCommit )
449 {
450     MutexGuard aGuard( m_aMutex );
451     checkDisposed(Connection_BASE::rBHelper.bDisposed);
452 
453     m_bIsAutoCommit = autoCommit;
454 
455     if (m_aTransactionHandle)
456     {
457         setupTransaction();
458     }
459 }
460 
461 sal_Bool SAL_CALL Connection::getAutoCommit()
462 {
463     MutexGuard aGuard( m_aMutex );
464     checkDisposed(Connection_BASE::rBHelper.bDisposed);
465 
466     return m_bIsAutoCommit;
467 }
468 
469 void Connection::setupTransaction()
470 {
471     MutexGuard aGuard( m_aMutex );
472     ISC_STATUS status_vector[20];
473 
474     // TODO: is this sensible? If we have changed parameters then transaction
475     // is lost...
476     if (m_aTransactionHandle)
477     {
478         disposeStatements();
479         isc_rollback_transaction(status_vector, &m_aTransactionHandle);
480     }
481 
482     char aTransactionIsolation = 0;
483     switch (m_aTransactionIsolation)
484     {
485         // TODO: confirm that these are correct.
486         case TransactionIsolation::READ_UNCOMMITTED:
487             aTransactionIsolation = isc_tpb_concurrency;
488             break;
489         case TransactionIsolation::READ_COMMITTED:
490             aTransactionIsolation = isc_tpb_read_committed;
491             break;
492         case TransactionIsolation::REPEATABLE_READ:
493             aTransactionIsolation = isc_tpb_consistency;
494             break;
495         case TransactionIsolation::SERIALIZABLE:
496             aTransactionIsolation = isc_tpb_consistency;
497             break;
498         default:
499             assert( false ); // We must have a valid TransactionIsolation.
500     }
501 
502     // You cannot pass an empty tpb parameter so we have to do some pointer
503     // arithmetic to avoid problems. (i.e. aTPB[x] = 0 is invalid)
504     char aTPB[5];
505     char* pTPB = aTPB;
506 
507     *pTPB++ = isc_tpb_version3;
508     if (m_bIsAutoCommit)
509         *pTPB++ = isc_tpb_autocommit;
510     *pTPB++ = (!m_bIsReadOnly ? isc_tpb_write : isc_tpb_read);
511     *pTPB++ = aTransactionIsolation;
512     *pTPB++ = isc_tpb_wait;
513 
514     isc_start_transaction(status_vector,
515                           &m_aTransactionHandle,
516                           1,
517                           &m_aDBHandle,
518                           pTPB - aTPB, // bytes used in TPB
519                           aTPB);
520 
521     evaluateStatusVector(status_vector,
522                          u"isc_start_transaction",
523                          *this);
524 }
525 
526 isc_tr_handle& Connection::getTransaction()
527 {
528     MutexGuard aGuard( m_aMutex );
529     if (!m_aTransactionHandle)
530     {
531         setupTransaction();
532     }
533     return m_aTransactionHandle;
534 }
535 
536 void SAL_CALL Connection::commit()
537 {
538     MutexGuard aGuard( m_aMutex );
539     checkDisposed(Connection_BASE::rBHelper.bDisposed);
540 
541     ISC_STATUS status_vector[20];
542 
543     if (!m_bIsAutoCommit && m_aTransactionHandle)
544     {
545         disposeStatements();
546         isc_commit_transaction(status_vector, &m_aTransactionHandle);
547         evaluateStatusVector(status_vector,
548                              u"isc_commit_transaction",
549                              *this);
550     }
551 }
552 
553 void Connection::loadDatabaseFile(const OUString& srcLocation, const OUString& tmpLocation)
554 {
555     Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(srcLocation,
556             ElementModes::READ));
557 
558     uno::Reference< ucb::XSimpleFileAccess2 > xFileAccess =
559         ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
560     if ( !xFileAccess.is() )
561     {
562         ::connectivity::SharedResources aResources;
563         // TODO FIXME: this does _not_ look like the right error message
564         const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION);
565         ::dbtools::throwGenericSQLException(sMessage ,*this);
566     }
567     xFileAccess->writeFile(tmpLocation,xDBStream->getInputStream());
568 }
569 
570 isc_svc_handle Connection::attachServiceManager()
571 {
572     ISC_STATUS_ARRAY aStatusVector;
573 #if SAL_TYPES_SIZEOFPOINTER == 8
574     isc_svc_handle aServiceHandle = 0;
575 #else
576     isc_svc_handle aServiceHandle = nullptr;
577 #endif
578 
579     char aSPBBuffer[256];
580     char* pSPB = aSPBBuffer;
581     *pSPB++ = isc_spb_version;
582     *pSPB++ = isc_spb_current_version;
583     *pSPB++ = isc_spb_user_name;
584     OUString sUserName("SYSDBA");
585     char aLength = static_cast<char>(sUserName.getLength());
586     *pSPB++ = aLength;
587     strncpy(pSPB,
588             OUStringToOString(sUserName,
589                               RTL_TEXTENCODING_UTF8).getStr(),
590             aLength);
591     pSPB += aLength;
592     // TODO: do we need ", isc_dpb_trusted_auth, 1, 1" -- probably not but ...
593     if (isc_service_attach(aStatusVector,
594                             0, // Denotes null-terminated string next
595                             "service_mgr",
596                             &aServiceHandle,
597                             pSPB - aSPBBuffer,
598                             aSPBBuffer))
599     {
600         evaluateStatusVector(aStatusVector,
601                              u"isc_service_attach",
602                              *this);
603     }
604 
605     return aServiceHandle;
606 }
607 
608 void Connection::detachServiceManager(isc_svc_handle aServiceHandle)
609 {
610     ISC_STATUS_ARRAY aStatusVector;
611     if (isc_service_detach(aStatusVector,
612                             &aServiceHandle))
613     {
614         evaluateStatusVector(aStatusVector,
615                              u"isc_service_detach",
616                              *this);
617     }
618 }
619 
620 void Connection::runBackupService(const short nAction)
621 {
622     assert(nAction == isc_action_svc_backup
623            || nAction == isc_action_svc_restore);
624 
625     ISC_STATUS_ARRAY aStatusVector;
626 
627     // convert paths to 8-Bit strings
628     OString sFDBPath = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8);
629     OString sFBKPath = OUStringToOString(m_sFBKPath, RTL_TEXTENCODING_UTF8);
630 
631 
632     OStringBuffer aRequest; // byte array
633 
634 
635     aRequest.append(static_cast<char>(nAction));
636 
637     aRequest.append(char(isc_spb_dbname)); // .fdb
638     sal_uInt16 nFDBLength = sFDBPath.getLength();
639     aRequest.append(static_cast<char>(nFDBLength & 0xFF)); // least significant byte first
640     aRequest.append(static_cast<char>((nFDBLength >> 8) & 0xFF));
641     aRequest.append(sFDBPath);
642 
643     aRequest.append(char(isc_spb_bkp_file)); // .fbk
644     sal_uInt16 nFBKLength = sFBKPath.getLength();
645     aRequest.append(static_cast<char>(nFBKLength & 0xFF));
646     aRequest.append(static_cast<char>((nFBKLength >> 8) & 0xFF));
647     aRequest.append(sFBKPath);
648 
649     if (nAction == isc_action_svc_restore)
650     {
651         aRequest.append(char(isc_spb_options)); // 4-Byte bitmask
652         char sOptions[4];
653         char * pOptions = sOptions;
654 #ifdef _WIN32
655 #pragma warning(push)
656 #pragma warning(disable: 4310) // cast truncates data
657 #endif
658         ADD_SPB_NUMERIC(pOptions, isc_spb_res_create);
659 #ifdef _WIN32
660 #pragma warning(pop)
661 #endif
662         aRequest.append(sOptions, 4);
663     }
664 
665     isc_svc_handle aServiceHandle;
666     aServiceHandle = attachServiceManager();
667 
668     if (isc_service_start(aStatusVector,
669                             &aServiceHandle,
670                             nullptr,
671                             aRequest.getLength(),
672                             aRequest.getStr()))
673     {
674         evaluateStatusVector(aStatusVector, u"isc_service_start", *this);
675     }
676 
677     char aInfoSPB = isc_info_svc_line;
678     char aResults[256];
679 
680     // query blocks until success or error
681     if(isc_service_query(aStatusVector,
682                       &aServiceHandle,
683                       nullptr, // Reserved null
684                       0,nullptr, // "send" spb -- size and spb -- not needed?
685                       1,
686                       &aInfoSPB,
687                       sizeof(aResults),
688                       aResults))
689     {
690         evaluateStatusVector(aStatusVector, u"isc_service_query", *this);
691     }
692 
693     detachServiceManager(aServiceHandle);
694 }
695 
696 
697 void SAL_CALL Connection::rollback()
698 {
699     MutexGuard aGuard( m_aMutex );
700     checkDisposed(Connection_BASE::rBHelper.bDisposed);
701 
702     ISC_STATUS status_vector[20];
703 
704     if (!m_bIsAutoCommit && m_aTransactionHandle)
705     {
706         isc_rollback_transaction(status_vector, &m_aTransactionHandle);
707     }
708 }
709 
710 sal_Bool SAL_CALL Connection::isClosed(  )
711 {
712     MutexGuard aGuard( m_aMutex );
713 
714     // just simple -> we are close when we are disposed that means someone called dispose(); (XComponent)
715     return Connection_BASE::rBHelper.bDisposed;
716 }
717 
718 Reference< XDatabaseMetaData > SAL_CALL Connection::getMetaData(  )
719 {
720     MutexGuard aGuard( m_aMutex );
721     checkDisposed(Connection_BASE::rBHelper.bDisposed);
722 
723     // here we have to create the class with biggest interface
724     // The answer is 42 :-)
725     Reference< XDatabaseMetaData > xMetaData = m_xMetaData;
726     if(!xMetaData.is())
727     {
728         xMetaData = new ODatabaseMetaData(this); // need the connection because it can return it
729         m_xMetaData = xMetaData;
730     }
731 
732     return xMetaData;
733 }
734 
735 void SAL_CALL Connection::setReadOnly(sal_Bool readOnly)
736 {
737     MutexGuard aGuard( m_aMutex );
738     checkDisposed(Connection_BASE::rBHelper.bDisposed);
739 
740     m_bIsReadOnly = readOnly;
741     setupTransaction();
742 }
743 
744 sal_Bool SAL_CALL Connection::isReadOnly()
745 {
746     MutexGuard aGuard( m_aMutex );
747     checkDisposed(Connection_BASE::rBHelper.bDisposed);
748 
749     return m_bIsReadOnly;
750 }
751 
752 void SAL_CALL Connection::setCatalog(const OUString& /*catalog*/)
753 {
754     ::dbtools::throwFunctionNotSupportedSQLException("setCatalog", *this);
755 }
756 
757 OUString SAL_CALL Connection::getCatalog()
758 {
759     ::dbtools::throwFunctionNotSupportedSQLException("getCatalog", *this);
760     return OUString();
761 }
762 
763 void SAL_CALL Connection::setTransactionIsolation( sal_Int32 level )
764 {
765     MutexGuard aGuard( m_aMutex );
766     checkDisposed(Connection_BASE::rBHelper.bDisposed);
767 
768     m_aTransactionIsolation = level;
769     setupTransaction();
770 }
771 
772 sal_Int32 SAL_CALL Connection::getTransactionIsolation(  )
773 {
774     MutexGuard aGuard( m_aMutex );
775     checkDisposed(Connection_BASE::rBHelper.bDisposed);
776 
777     return m_aTransactionIsolation;
778 }
779 
780 Reference< XNameAccess > SAL_CALL Connection::getTypeMap()
781 {
782     ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::getTypeMap", *this );
783     return nullptr;
784 }
785 
786 void SAL_CALL Connection::setTypeMap(const Reference< XNameAccess >&)
787 {
788     ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this );
789 }
790 
791 //----- XCloseable -----------------------------------------------------------
792 void SAL_CALL Connection::close(  )
793 {
794     // we just dispose us
795     {
796         MutexGuard aGuard( m_aMutex );
797         checkDisposed(Connection_BASE::rBHelper.bDisposed);
798 
799     }
800     dispose();
801 }
802 
803 // XWarningsSupplier
804 Any SAL_CALL Connection::getWarnings(  )
805 {
806     // when you collected some warnings -> return it
807     return Any();
808 }
809 
810 void SAL_CALL Connection::clearWarnings(  )
811 {
812     // you should clear your collected warnings here
813 }
814 
815 // XDocumentEventListener
816 void SAL_CALL Connection::documentEventOccured( const DocumentEvent& Event )
817 {
818     MutexGuard aGuard(m_aMutex);
819 
820     if (!m_bIsEmbedded)
821         return;
822 
823     if (Event.EventName != "OnSave" && Event.EventName != "OnSaveAs")
824         return;
825 
826     commit(); // Commit and close transaction
827     if ( !(m_bIsEmbedded && m_xEmbeddedStorage.is()) )
828         return;
829 
830     SAL_INFO("connectivity.firebird", "Writing .fbk from running db");
831     try
832     {
833         runBackupService(isc_action_svc_backup);
834     }
835     catch (const SQLException& e)
836     {
837         auto a = cppu::getCaughtException();
838         throw WrappedTargetRuntimeException(e.Message, e.Context, a);
839     }
840 
841 
842     Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sFBKLocation,
843                                                     ElementModes::WRITE));
844 
845     // TODO: verify the backup actually exists -- the backup service
846     // can fail without giving any sane error messages / telling us
847     // that it failed.
848     using namespace ::comphelper;
849     Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
850     Reference< XInputStream > xInputStream;
851     if (!xContext.is())
852         return;
853 
854     xInputStream =
855             OStorageHelper::GetInputStreamFromURL(m_sFBKPath, xContext);
856     if (xInputStream.is())
857         OStorageHelper::CopyInputToOutput( xInputStream,
858                                     xDBStream->getOutputStream());
859 
860     // remove old fdb file if exists
861     uno::Reference< ucb::XSimpleFileAccess > xFileAccess =
862         ucb::SimpleFileAccess::create(xContext);
863     if (xFileAccess->exists(m_sFirebirdURL))
864         xFileAccess->kill(m_sFirebirdURL);
865 }
866 // XEventListener
867 void SAL_CALL Connection::disposing(const EventObject& /*rSource*/)
868 {
869     MutexGuard aGuard( m_aMutex );
870 
871     m_xEmbeddedStorage.clear();
872 }
873 
874 void Connection::buildTypeInfo()
875 {
876     MutexGuard aGuard( m_aMutex );
877 
878     Reference< XResultSet> xRs = getMetaData ()->getTypeInfo ();
879     Reference< XRow> xRow(xRs,UNO_QUERY);
880     // Information for a single SQL type
881 
882     // Loop on the result set until we reach end of file
883 
884     while (xRs->next ())
885     {
886         OTypeInfo aInfo;
887         aInfo.aTypeName         = xRow->getString   (1);
888         aInfo.nType             = xRow->getShort    (2);
889         aInfo.nPrecision        = xRow->getInt      (3);
890         // aLiteralPrefix    = xRow->getString   (4);
891         // aLiteralSuffix    = xRow->getString   (5);
892         // aCreateParams     = xRow->getString   (6);
893         // bNullable         = xRow->getBoolean  (7);
894         // bCaseSensitive    = xRow->getBoolean  (8);
895         // nSearchType       = xRow->getShort    (9);
896         // bUnsigned         = xRow->getBoolean  (10);
897         // bCurrency         = xRow->getBoolean  (11);
898         // bAutoIncrement    = xRow->getBoolean  (12);
899         aInfo.aLocalTypeName    = xRow->getString   (13);
900         // nMinimumScale     = xRow->getShort    (14);
901         aInfo.nMaximumScale     = xRow->getShort    (15);
902         // nNumPrecRadix     = (sal_Int16)xRow->getInt(18);
903 
904 
905         // Now that we have the type info, save it
906         // in the Hashtable if we don't already have an
907         // entry for this SQL type.
908 
909         m_aTypeInfo.push_back(aInfo);
910     }
911 
912     SAL_INFO("connectivity.firebird", "buildTypeInfo(). "
913              "Type info built.");
914 
915     // Close the result set/statement.
916 
917     Reference< XCloseable> xClose(xRs,UNO_QUERY);
918     xClose->close();
919 
920     SAL_INFO("connectivity.firebird", "buildTypeInfo(). "
921              "Closed.");
922 }
923 
924 void Connection::disposing()
925 {
926     MutexGuard aGuard(m_aMutex);
927 
928     disposeStatements();
929 
930     m_xMetaData = css::uno::WeakReference< css::sdbc::XDatabaseMetaData>();
931 
932     ISC_STATUS_ARRAY status;            /* status vector */
933     if (m_aTransactionHandle)
934     {
935         // TODO: confirm whether we need to ask the user here.
936         isc_rollback_transaction(status, &m_aTransactionHandle);
937     }
938 
939     if (m_aDBHandle)
940     {
941         if (isc_detach_database(status, &m_aDBHandle))
942         {
943             evaluateStatusVector(status, u"isc_detach_database", *this);
944         }
945     }
946     // TODO: write to storage again?
947 
948     cppu::WeakComponentImplHelperBase::disposing();
949 
950     m_pDatabaseFileDir.reset();
951 }
952 
953 void Connection::disposeStatements()
954 {
955     MutexGuard aGuard(m_aMutex);
956     for (auto const& statement : m_aStatements)
957     {
958         Reference< XComponent > xComp(statement.get(), UNO_QUERY);
959         if (xComp.is())
960             xComp->dispose();
961     }
962     m_aStatements.clear();
963 }
964 
965 uno::Reference< XTablesSupplier > Connection::createCatalog()
966 {
967     MutexGuard aGuard(m_aMutex);
968 
969     // m_xCatalog is a weak reference. Reuse it if it still exists.
970     Reference< XTablesSupplier > xCatalog = m_xCatalog;
971     if (xCatalog.is())
972     {
973         return xCatalog;
974     }
975     else
976     {
977         xCatalog = new Catalog(this);
978         m_xCatalog = xCatalog;
979         return m_xCatalog;
980     }
981 
982 }
983 
984 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
985