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
