Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "Catalog.hxx"
21 : #include "Connection.hxx"
22 : #include "DatabaseMetaData.hxx"
23 : #include "Driver.hxx"
24 : #include "PreparedStatement.hxx"
25 : #include "Statement.hxx"
26 : #include "Tables.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/frame/Desktop.hpp>
34 : #include <com/sun/star/frame/FrameSearchFlag.hpp>
35 : #include <com/sun/star/frame/XController.hpp>
36 : #include <com/sun/star/frame/XFrame.hpp>
37 : #include <com/sun/star/frame/XFrames.hpp>
38 : #include <com/sun/star/frame/XModel.hpp>
39 : #include <com/sun/star/io/TempFile.hpp>
40 : #include <com/sun/star/io/XStream.hpp>
41 : #include <com/sun/star/lang/DisposedException.hpp>
42 : #include <com/sun/star/lang/EventObject.hpp>
43 : #include <com/sun/star/sdbc/ColumnValue.hpp>
44 : #include <com/sun/star/sdbc/XRow.hpp>
45 : #include <com/sun/star/sdbc/TransactionIsolation.hpp>
46 : #include <com/sun/star/ucb/SimpleFileAccess.hpp>
47 : #include <com/sun/star/ucb/XSimpleFileAccess2.hpp>
48 :
49 : #include <connectivity/dbexception.hxx>
50 : #include <connectivity/sqlparse.hxx>
51 : #include <resource/common_res.hrc>
52 : #include <resource/hsqldb_res.hrc>
53 : #include <resource/sharedresources.hxx>
54 :
55 : #include <comphelper/processfactory.hxx>
56 : #include <comphelper/storagehelper.hxx>
57 : #include <unotools/tempfile.hxx>
58 : #include <unotools/localfilehelper.hxx>
59 : #include <unotools/ucbstreamhelper.hxx>
60 :
61 : using namespace connectivity::firebird;
62 : using namespace connectivity;
63 :
64 : using namespace ::osl;
65 :
66 : using namespace ::com::sun::star;
67 : using namespace ::com::sun::star::beans;
68 : using namespace ::com::sun::star::container;
69 : using namespace ::com::sun::star::document;
70 : using namespace ::com::sun::star::embed;
71 : using namespace ::com::sun::star::frame;
72 : using namespace ::com::sun::star::io;
73 : using namespace ::com::sun::star::lang;
74 : using namespace ::com::sun::star::sdbc;
75 : using namespace ::com::sun::star::sdbcx;
76 : using namespace ::com::sun::star::uno;
77 :
78 4 : const OUString Connection::our_sDBLocation( "firebird.fdb" );
79 :
80 4 : Connection::Connection(FirebirdDriver* _pDriver)
81 : : Connection_BASE(m_aMutex)
82 : , OSubComponent<Connection, Connection_BASE>((::cppu::OWeakObject*)_pDriver, this)
83 : , m_xDriver(_pDriver)
84 : , m_sConnectionURL()
85 : , m_sFirebirdURL()
86 : , m_bIsEmbedded(false)
87 : , m_xEmbeddedStorage(0)
88 : , m_bIsFile(false)
89 : , m_sUser()
90 : , m_bIsAutoCommit(false)
91 : , m_bIsReadOnly(false)
92 : , m_aTransactionIsolation(TransactionIsolation::REPEATABLE_READ)
93 : , m_aDBHandle(0)
94 : , m_aTransactionHandle(0)
95 : , m_xCatalog(0)
96 : , m_xMetaData(0)
97 4 : , m_aStatements()
98 : {
99 4 : }
100 :
101 12 : Connection::~Connection()
102 : {
103 4 : if(!isClosed())
104 0 : close();
105 8 : }
106 :
107 235 : void SAL_CALL Connection::release() throw()
108 : {
109 235 : relase_ChildImpl();
110 235 : }
111 :
112 : struct ConnectionGuard
113 : {
114 : oslInterlockedCount& m_refCount;
115 4 : ConnectionGuard(oslInterlockedCount& refCount)
116 4 : : m_refCount(refCount)
117 : {
118 4 : osl_atomic_increment(&m_refCount);
119 4 : }
120 4 : ~ConnectionGuard()
121 : {
122 4 : osl_atomic_decrement(&m_refCount);
123 4 : }
124 : };
125 :
126 4 : void Connection::construct(const ::rtl::OUString& url, const Sequence< PropertyValue >& info)
127 : throw (SQLException, RuntimeException, std::exception)
128 : {
129 4 : ConnectionGuard aGuard(m_refCount);
130 :
131 : try
132 : {
133 4 : m_sConnectionURL = url;
134 :
135 4 : bool bIsNewDatabase = false;
136 4 : OUString aStorageURL;
137 4 : if (url.equals("sdbc:embedded:firebird"))
138 : {
139 4 : m_bIsEmbedded = true;
140 :
141 4 : const PropertyValue* pIter = info.getConstArray();
142 4 : const PropertyValue* pEnd = pIter + info.getLength();
143 :
144 16 : for (;pIter != pEnd; ++pIter)
145 : {
146 12 : if ( pIter->Name == "Storage" )
147 : {
148 4 : m_xEmbeddedStorage.set(pIter->Value,UNO_QUERY);
149 : }
150 8 : else if ( pIter->Name == "URL" )
151 : {
152 4 : pIter->Value >>= aStorageURL;
153 : }
154 4 : else if ( pIter->Name == "Document" )
155 : {
156 4 : pIter->Value >>= m_xParentDocument;
157 : }
158 : }
159 :
160 4 : if ( !m_xEmbeddedStorage.is() )
161 : {
162 0 : ::connectivity::SharedResources aResources;
163 0 : const OUString sMessage = aResources.getResourceString(STR_NO_STORAGE);
164 0 : ::dbtools::throwGenericSQLException(sMessage ,*this);
165 : }
166 :
167 4 : bIsNewDatabase = !m_xEmbeddedStorage->hasElements();
168 :
169 4 : m_pExtractedFDBFile.reset(new ::utl::TempFile(NULL, true));
170 4 : m_sFirebirdURL = m_pExtractedFDBFile->GetFileName() + "/firebird.fdb";
171 :
172 : SAL_INFO("connectivity.firebird", "Temporary .fdb location: " << m_sFirebirdURL);
173 :
174 4 : if (!bIsNewDatabase)
175 : {
176 : SAL_INFO("connectivity.firebird", "Extracting .fdb from .odb" );
177 2 : if (!m_xEmbeddedStorage->isStreamElement(our_sDBLocation))
178 : {
179 0 : ::connectivity::SharedResources aResources;
180 0 : const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION);
181 0 : ::dbtools::throwGenericSQLException(sMessage ,*this);
182 : }
183 :
184 2 : Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sDBLocation,
185 2 : ElementModes::READ));
186 :
187 : uno::Reference< ucb::XSimpleFileAccess2 > xFileAccess(
188 : ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ),
189 4 : uno::UNO_QUERY );
190 2 : if ( !xFileAccess.is() )
191 : {
192 0 : ::connectivity::SharedResources aResources;
193 0 : const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION);
194 0 : ::dbtools::throwGenericSQLException(sMessage ,*this);
195 : }
196 :
197 4 : xFileAccess->writeFile(m_sFirebirdURL,xDBStream->getInputStream());
198 : }
199 : // TOOO: Get DB properties from XML
200 :
201 : }
202 : // External file AND/OR remote connection
203 0 : else if (url.startsWith("sdbc:firebird:"))
204 : {
205 0 : m_sFirebirdURL = url.copy(OUString("sdbc:firebird:").getLength());
206 0 : if (m_sFirebirdURL.startsWith("file://"))
207 : {
208 0 : m_bIsFile = true;
209 : uno::Reference< ucb::XSimpleFileAccess > xFileAccess(
210 : ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext()),
211 0 : uno::UNO_QUERY);
212 0 : if (!xFileAccess->exists(m_sFirebirdURL))
213 0 : bIsNewDatabase = true;
214 :
215 0 : m_sFirebirdURL = m_sFirebirdURL.copy(OUString("file://").getLength());
216 : }
217 : }
218 :
219 : char dpbBuffer[1 + 3 + 257 + 257 ]; // Expand as needed
220 4 : int dpbLength = 0;
221 : {
222 : char* dpb;
223 4 : char userName[256] = "";
224 4 : char userPassword[256] = "";
225 :
226 4 : dpb = dpbBuffer;
227 4 : *dpb++ = isc_dpb_version1;
228 :
229 4 : *dpb++ = isc_dpb_sql_dialect;
230 4 : *dpb++ = 1; // 1 byte long
231 4 : *dpb++ = FIREBIRD_SQL_DIALECT;
232 : // Do any more dpbBuffer additions here
233 :
234 4 : if (m_bIsEmbedded || m_bIsFile)
235 : {
236 4 : *dpb++ = isc_dpb_trusted_auth;
237 4 : *dpb++ = 1; // Length of data
238 4 : *dpb++ = 1; // TRUE
239 : }
240 : else
241 : {
242 : // TODO: parse password from connection string as needed?
243 : }
244 :
245 4 : if (strlen(userName))
246 : {
247 0 : int nUsernameLength = strlen(userName);
248 0 : *dpb++ = isc_dpb_user_name;
249 0 : *dpb++ = (char) nUsernameLength;
250 0 : strcpy(dpb, userName);
251 0 : dpb+= nUsernameLength;
252 : }
253 :
254 4 : if (strlen(userPassword))
255 : {
256 0 : int nPasswordLength = strlen(userPassword);
257 0 : *dpb++ = isc_dpb_password;
258 0 : *dpb++ = (char) nPasswordLength;
259 0 : strcpy(dpb, userPassword);
260 0 : dpb+= nPasswordLength;
261 : }
262 :
263 4 : dpbLength = dpb - dpbBuffer;
264 : }
265 :
266 : ISC_STATUS_ARRAY status; /* status vector */
267 : ISC_STATUS aErr;
268 4 : if (bIsNewDatabase)
269 : {
270 : aErr = isc_create_database(status,
271 2 : m_sFirebirdURL.getLength(),
272 : OUStringToOString(m_sFirebirdURL,RTL_TEXTENCODING_UTF8).getStr(),
273 : &m_aDBHandle,
274 : dpbLength,
275 : dpbBuffer,
276 4 : 0);
277 2 : if (aErr)
278 : {
279 0 : evaluateStatusVector(status, "isc_create_database", *this);
280 : }
281 : }
282 : else
283 : {
284 : aErr = isc_attach_database(status,
285 2 : m_sFirebirdURL.getLength(),
286 : OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8).getStr(),
287 : &m_aDBHandle,
288 : dpbLength,
289 4 : dpbBuffer);
290 2 : if (aErr)
291 : {
292 0 : evaluateStatusVector(status, "isc_attach_database", *this);
293 : }
294 : }
295 :
296 4 : if (m_bIsEmbedded) // Add DocumentEventListener to save the .fdb as needed
297 : {
298 : // TODO: this is only needed when we change icu versions, so ideally
299 : // we somehow keep track of which icu version we have. There might
300 : // be something db internal that we can check, or we might have to store
301 : // it in the .odb.
302 4 : rebuildIndexes();
303 :
304 : // We need to attach as a document listener in order to be able to store
305 : // the temporary db back into the .odb when saving
306 4 : uno::Reference<XDocumentEventBroadcaster> xBroadcaster(m_xParentDocument, UNO_QUERY);
307 :
308 4 : if (xBroadcaster.is())
309 4 : xBroadcaster->addDocumentEventListener(this);
310 : else
311 4 : assert(false);
312 4 : }
313 : }
314 0 : catch (const Exception&)
315 : {
316 0 : throw;
317 : }
318 0 : catch (const std::exception&)
319 : {
320 0 : throw;
321 : }
322 0 : catch (...) // const Firebird::Exception& firebird throws this, but doesn't install the fb_exception.h that declares it
323 :
324 : {
325 0 : throw std::runtime_error("Generic Firebird::Exception");
326 4 : }
327 4 : }
328 :
329 0 : void Connection::notifyDatabaseModified()
330 : {
331 0 : if (m_xParentDocument.is()) // Only true in embedded mode
332 0 : m_xParentDocument->setModified(sal_True);
333 0 : }
334 :
335 : //----- XServiceInfo ---------------------------------------------------------
336 0 : IMPLEMENT_SERVICE_INFO(Connection, "com.sun.star.sdbc.drivers.firebird.Connection",
337 : "com.sun.star.sdbc.Connection")
338 :
339 0 : Reference< XBlob> Connection::createBlob(ISC_QUAD* pBlobId)
340 : throw(SQLException, RuntimeException)
341 : {
342 0 : MutexGuard aGuard(m_aMutex);
343 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
344 :
345 : Reference< XBlob > xReturn = new Blob(&m_aDBHandle,
346 : &m_aTransactionHandle,
347 0 : *pBlobId);
348 :
349 0 : m_aStatements.push_back(WeakReferenceHelper(xReturn));
350 0 : return xReturn;
351 : }
352 :
353 :
354 : //----- XConnection ----------------------------------------------------------
355 22 : Reference< XStatement > SAL_CALL Connection::createStatement( )
356 : throw(SQLException, RuntimeException, std::exception)
357 : {
358 22 : MutexGuard aGuard( m_aMutex );
359 22 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
360 :
361 : // the pre
362 22 : if(m_aTypeInfo.empty())
363 4 : buildTypeInfo();
364 :
365 : // create a statement
366 : // the statement can only be executed once
367 22 : Reference< XStatement > xReturn = new OStatement(this);
368 22 : m_aStatements.push_back(WeakReferenceHelper(xReturn));
369 22 : return xReturn;
370 : }
371 :
372 0 : OUString Connection::transformPreparedStatement(const OUString& _sSQL)
373 : {
374 0 : OUString sSqlStatement (_sSQL);
375 : try
376 : {
377 0 : OSQLParser aParser( m_xDriver->getContext() );
378 0 : OUString sErrorMessage;
379 0 : OUString sNewSql;
380 0 : OSQLParseNode* pNode = aParser.parseTree(sErrorMessage,_sSQL);
381 0 : if(pNode)
382 : { // special handling for parameters
383 0 : OSQLParseNode::substituteParameterNames(pNode);
384 0 : pNode->parseNodeToStr( sNewSql, this );
385 0 : delete pNode;
386 0 : sSqlStatement = sNewSql;
387 0 : }
388 : }
389 0 : catch(const Exception&)
390 : {
391 : SAL_WARN("connectivity.firebird", "failed to remove named parameters from '" << _sSQL << "'");
392 : }
393 0 : return sSqlStatement;
394 : }
395 :
396 0 : Reference< XPreparedStatement > SAL_CALL Connection::prepareStatement(
397 : const OUString& _sSql)
398 : throw(SQLException, RuntimeException, std::exception)
399 : {
400 : SAL_INFO("connectivity.firebird", "prepareStatement() "
401 : "called with sql: " << _sSql);
402 0 : MutexGuard aGuard(m_aMutex);
403 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
404 :
405 0 : if(m_aTypeInfo.empty())
406 0 : buildTypeInfo();
407 :
408 0 : OUString sSqlStatement (transformPreparedStatement( _sSql ));
409 :
410 : Reference< XPreparedStatement > xReturn = new OPreparedStatement(this,
411 : m_aTypeInfo,
412 0 : sSqlStatement);
413 0 : m_aStatements.push_back(WeakReferenceHelper(xReturn));
414 :
415 0 : return xReturn;
416 : }
417 :
418 0 : Reference< XPreparedStatement > SAL_CALL Connection::prepareCall(
419 : const OUString& _sSql ) throw(SQLException, RuntimeException, std::exception)
420 : {
421 : SAL_INFO("connectivity.firebird", "prepareCall(). "
422 : "_sSql: " << _sSql);
423 :
424 0 : MutexGuard aGuard( m_aMutex );
425 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
426 :
427 : // OUString sSqlStatement (transformPreparedStatement( _sSql ));
428 :
429 : // not implemented yet :-) a task to do
430 0 : return NULL;
431 : }
432 :
433 0 : OUString SAL_CALL Connection::nativeSQL( const OUString& _sSql )
434 : throw(SQLException, RuntimeException, std::exception)
435 : {
436 0 : MutexGuard aGuard( m_aMutex );
437 : // We do not need to adapt the SQL for Firebird atm.
438 0 : return _sSql;
439 : }
440 :
441 0 : void SAL_CALL Connection::setAutoCommit( sal_Bool autoCommit )
442 : throw(SQLException, RuntimeException, std::exception)
443 : {
444 0 : MutexGuard aGuard( m_aMutex );
445 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
446 :
447 0 : m_bIsAutoCommit = autoCommit;
448 :
449 0 : if (m_aTransactionHandle)
450 : {
451 0 : setupTransaction();
452 0 : }
453 0 : }
454 :
455 0 : sal_Bool SAL_CALL Connection::getAutoCommit() throw(SQLException, RuntimeException, std::exception)
456 : {
457 0 : MutexGuard aGuard( m_aMutex );
458 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
459 :
460 0 : return m_bIsAutoCommit;
461 : }
462 :
463 8 : void Connection::setupTransaction()
464 : throw (SQLException)
465 : {
466 8 : MutexGuard aGuard( m_aMutex );
467 : ISC_STATUS status_vector[20];
468 :
469 : // TODO: is this sensible? If we have changed parameters then transaction
470 : // is lost...
471 8 : if (m_aTransactionHandle)
472 : {
473 0 : disposeStatements();
474 0 : isc_rollback_transaction(status_vector, &m_aTransactionHandle);
475 : }
476 :
477 8 : char aTransactionIsolation = 0;
478 8 : switch (m_aTransactionIsolation)
479 : {
480 : // TODO: confirm that these are correct.
481 : case(TransactionIsolation::READ_UNCOMMITTED):
482 0 : aTransactionIsolation = isc_tpb_concurrency;
483 0 : break;
484 : case(TransactionIsolation::READ_COMMITTED):
485 0 : aTransactionIsolation = isc_tpb_read_committed;
486 0 : break;
487 : case(TransactionIsolation::REPEATABLE_READ):
488 8 : aTransactionIsolation = isc_tpb_consistency;
489 8 : break;
490 : case(TransactionIsolation::SERIALIZABLE):
491 0 : aTransactionIsolation = isc_tpb_consistency;
492 0 : break;
493 : default:
494 : assert( false ); // We must have a valid TransactionIsolation.
495 : }
496 :
497 : // You cannot pass an empty tpb parameter so we have to do some pointer
498 : // arithmetic to avoid problems. (i.e. aTPB[x] = 0 is invalid)
499 : char aTPB[5];
500 8 : char* pTPB = aTPB;
501 :
502 8 : *pTPB++ = isc_tpb_version3;
503 8 : if (m_bIsAutoCommit)
504 0 : *pTPB++ = isc_tpb_autocommit;
505 8 : *pTPB++ = (!m_bIsReadOnly ? isc_tpb_write : isc_tpb_read);
506 8 : *pTPB++ = aTransactionIsolation;
507 8 : *pTPB++ = isc_tpb_wait;
508 :
509 : isc_start_transaction(status_vector,
510 : &m_aTransactionHandle,
511 : 1,
512 : &m_aDBHandle,
513 : pTPB - aTPB, // bytes used in TPB
514 8 : aTPB);
515 :
516 : evaluateStatusVector(status_vector,
517 : "isc_start_transaction",
518 8 : *this);
519 8 : }
520 :
521 36 : isc_tr_handle& Connection::getTransaction()
522 : throw (SQLException)
523 : {
524 36 : MutexGuard aGuard( m_aMutex );
525 36 : if (!m_aTransactionHandle)
526 : {
527 8 : setupTransaction();
528 : }
529 36 : return m_aTransactionHandle;
530 : }
531 :
532 4 : void SAL_CALL Connection::commit() throw(SQLException, RuntimeException, std::exception)
533 : {
534 4 : MutexGuard aGuard( m_aMutex );
535 4 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
536 :
537 : ISC_STATUS status_vector[20];
538 :
539 4 : if (!m_bIsAutoCommit && m_aTransactionHandle)
540 : {
541 4 : disposeStatements();
542 4 : isc_commit_transaction(status_vector, &m_aTransactionHandle);
543 : evaluateStatusVector(status_vector,
544 : "isc_commit_transaction",
545 4 : *this);
546 4 : }
547 4 : }
548 :
549 0 : void SAL_CALL Connection::rollback() throw(SQLException, RuntimeException, std::exception)
550 : {
551 0 : MutexGuard aGuard( m_aMutex );
552 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
553 :
554 : ISC_STATUS status_vector[20];
555 :
556 0 : if (!m_bIsAutoCommit && m_aTransactionHandle)
557 : {
558 0 : isc_rollback_transaction(status_vector, &m_aTransactionHandle);
559 0 : }
560 0 : }
561 :
562 4 : sal_Bool SAL_CALL Connection::isClosed( ) throw(SQLException, RuntimeException, std::exception)
563 : {
564 4 : MutexGuard aGuard( m_aMutex );
565 :
566 : // just simple -> we are close when we are disposed taht means someone called dispose(); (XComponent)
567 4 : return Connection_BASE::rBHelper.bDisposed;
568 : }
569 :
570 56 : Reference< XDatabaseMetaData > SAL_CALL Connection::getMetaData( ) throw(SQLException, RuntimeException, std::exception)
571 : {
572 56 : MutexGuard aGuard( m_aMutex );
573 56 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
574 :
575 : // here we have to create the class with biggest interface
576 : // The answer is 42 :-)
577 56 : Reference< XDatabaseMetaData > xMetaData = m_xMetaData;
578 56 : if(!xMetaData.is())
579 : {
580 8 : xMetaData = new ODatabaseMetaData(this); // need the connection because it can return it
581 8 : m_xMetaData = xMetaData;
582 : }
583 :
584 56 : return xMetaData;
585 : }
586 :
587 0 : void SAL_CALL Connection::setReadOnly(sal_Bool readOnly)
588 : throw(SQLException, RuntimeException, std::exception)
589 : {
590 0 : MutexGuard aGuard( m_aMutex );
591 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
592 :
593 0 : m_bIsReadOnly = readOnly;
594 0 : setupTransaction();
595 0 : }
596 :
597 0 : sal_Bool SAL_CALL Connection::isReadOnly() throw(SQLException, RuntimeException, std::exception)
598 : {
599 0 : MutexGuard aGuard( m_aMutex );
600 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
601 :
602 0 : return m_bIsReadOnly;
603 : }
604 :
605 0 : void SAL_CALL Connection::setCatalog(const OUString& /*catalog*/)
606 : throw(SQLException, RuntimeException, std::exception)
607 : {
608 0 : ::dbtools::throwFunctionNotSupportedSQLException("setCatalog", *this);
609 0 : }
610 :
611 0 : OUString SAL_CALL Connection::getCatalog()
612 : throw(SQLException, RuntimeException, std::exception)
613 : {
614 0 : ::dbtools::throwFunctionNotSupportedSQLException("getCatalog", *this);
615 0 : return OUString();
616 : }
617 :
618 0 : void SAL_CALL Connection::setTransactionIsolation( sal_Int32 level ) throw(SQLException, RuntimeException, std::exception)
619 : {
620 0 : MutexGuard aGuard( m_aMutex );
621 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
622 :
623 0 : m_aTransactionIsolation = level;
624 0 : setupTransaction();
625 0 : }
626 :
627 0 : sal_Int32 SAL_CALL Connection::getTransactionIsolation( ) throw(SQLException, RuntimeException, std::exception)
628 : {
629 0 : MutexGuard aGuard( m_aMutex );
630 0 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
631 :
632 0 : return m_aTransactionIsolation;
633 : }
634 :
635 0 : Reference< XNameAccess > SAL_CALL Connection::getTypeMap() throw(SQLException, RuntimeException, std::exception)
636 : {
637 0 : ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::getTypeMap", *this );
638 0 : return 0;
639 : }
640 :
641 0 : void SAL_CALL Connection::setTypeMap(const Reference< XNameAccess >& typeMap)
642 : throw(SQLException, RuntimeException, std::exception)
643 : {
644 0 : ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this );
645 : (void) typeMap;
646 0 : }
647 :
648 : //----- XCloseable -----------------------------------------------------------
649 4 : void SAL_CALL Connection::close( ) throw(SQLException, RuntimeException, std::exception)
650 : {
651 : // we just dispose us
652 : {
653 4 : MutexGuard aGuard( m_aMutex );
654 4 : checkDisposed(Connection_BASE::rBHelper.bDisposed);
655 :
656 : }
657 4 : dispose();
658 0 : }
659 :
660 : // XWarningsSupplier
661 0 : Any SAL_CALL Connection::getWarnings( ) throw(SQLException, RuntimeException, std::exception)
662 : {
663 : // when you collected some warnings -> return it
664 0 : return Any();
665 : }
666 :
667 0 : void SAL_CALL Connection::clearWarnings( ) throw(SQLException, RuntimeException, std::exception)
668 : {
669 : // you should clear your collected warnings here
670 0 : }
671 :
672 : // XDocumentEventListener
673 13 : void SAL_CALL Connection::documentEventOccured( const DocumentEvent& _Event )
674 : throw(RuntimeException, std::exception)
675 : {
676 13 : MutexGuard aGuard(m_aMutex);
677 :
678 13 : if (!m_bIsEmbedded)
679 13 : return;
680 :
681 13 : if (_Event.EventName == "OnSave" || _Event.EventName == "OnSaveAs")
682 : {
683 0 : commit(); // Commit and close transaction
684 0 : if ( m_bIsEmbedded && m_xEmbeddedStorage.is() )
685 : {
686 : SAL_INFO("connectivity.firebird", "Writing .fdb into .odb" );
687 :
688 0 : Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sDBLocation,
689 0 : ElementModes::WRITE));
690 :
691 : using namespace ::comphelper;
692 0 : Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
693 0 : Reference< XInputStream > xInputStream;
694 0 : if (xContext.is())
695 0 : xInputStream =
696 0 : OStorageHelper::GetInputStreamFromURL(m_sFirebirdURL, xContext);
697 0 : if (xInputStream.is())
698 : OStorageHelper::CopyInputToOutput( xInputStream,
699 0 : xDBStream->getOutputStream());
700 : // TODO: ensure db is in safe state
701 : }
702 13 : }
703 : }
704 : // XEventListener
705 4 : void SAL_CALL Connection::disposing(const EventObject& /*rSource*/)
706 : throw (RuntimeException, std::exception)
707 : {
708 4 : MutexGuard aGuard( m_aMutex );
709 :
710 4 : m_xEmbeddedStorage.clear();
711 4 : }
712 :
713 4 : void Connection::buildTypeInfo() throw( SQLException)
714 : {
715 4 : MutexGuard aGuard( m_aMutex );
716 :
717 8 : Reference< XResultSet> xRs = getMetaData ()->getTypeInfo ();
718 8 : Reference< XRow> xRow(xRs,UNO_QUERY);
719 : // Information for a single SQL type
720 :
721 : // Loop on the result set until we reach end of file
722 :
723 52 : while (xRs->next ())
724 : {
725 44 : OTypeInfo aInfo;
726 44 : aInfo.aTypeName = xRow->getString (1);
727 44 : aInfo.nType = xRow->getShort (2);
728 44 : aInfo.nPrecision = xRow->getInt (3);
729 44 : aInfo.aLiteralPrefix = xRow->getString (4);
730 44 : aInfo.aLiteralSuffix = xRow->getString (5);
731 44 : aInfo.aCreateParams = xRow->getString (6);
732 44 : aInfo.bNullable = xRow->getBoolean (7) == ColumnValue::NULLABLE;
733 44 : aInfo.bCaseSensitive = xRow->getBoolean (8);
734 44 : aInfo.nSearchType = xRow->getShort (9);
735 44 : aInfo.bUnsigned = xRow->getBoolean (10);
736 44 : aInfo.bCurrency = xRow->getBoolean (11);
737 44 : aInfo.bAutoIncrement = xRow->getBoolean (12);
738 44 : aInfo.aLocalTypeName = xRow->getString (13);
739 44 : aInfo.nMinimumScale = xRow->getShort (14);
740 44 : aInfo.nMaximumScale = xRow->getShort (15);
741 44 : aInfo.nNumPrecRadix = (sal_Int16)xRow->getInt(18);
742 :
743 :
744 :
745 : // Now that we have the type info, save it
746 : // in the Hashtable if we don't already have an
747 : // entry for this SQL type.
748 :
749 44 : m_aTypeInfo.push_back(aInfo);
750 44 : }
751 :
752 : SAL_INFO("connectivity.firebird", "buildTypeInfo(). "
753 : "Type info built.");
754 :
755 : // Close the result set/statement.
756 :
757 8 : Reference< XCloseable> xClose(xRs,UNO_QUERY);
758 4 : xClose->close();
759 :
760 : SAL_INFO("connectivity.firebird", "buildTypeInfo(). "
761 4 : "Closed.");
762 4 : }
763 :
764 4 : void Connection::disposing()
765 : {
766 4 : MutexGuard aGuard(m_aMutex);
767 :
768 4 : disposeStatements();
769 :
770 4 : m_xMetaData = ::com::sun::star::uno::WeakReference< ::com::sun::star::sdbc::XDatabaseMetaData>();
771 :
772 : ISC_STATUS_ARRAY status; /* status vector */
773 4 : if (m_aTransactionHandle)
774 : {
775 : // TODO: confirm whether we need to ask the user here.
776 4 : isc_rollback_transaction(status, &m_aTransactionHandle);
777 : }
778 :
779 4 : if (isc_detach_database(status, &m_aDBHandle))
780 : {
781 0 : evaluateStatusVector(status, "isc_detach_database", *this);
782 : }
783 : // TODO: write to storage again?
784 :
785 4 : dispose_ChildImpl();
786 4 : cppu::WeakComponentImplHelperBase::disposing();
787 4 : m_xDriver.clear();
788 :
789 4 : if (m_pExtractedFDBFile)
790 : {
791 4 : ::utl::removeTree(m_pExtractedFDBFile->GetURL());
792 0 : m_pExtractedFDBFile.reset();
793 4 : }
794 0 : }
795 :
796 8 : void Connection::disposeStatements()
797 : {
798 8 : MutexGuard aGuard(m_aMutex);
799 30 : for (OWeakRefArray::iterator i = m_aStatements.begin(); m_aStatements.end() != i; ++i)
800 : {
801 22 : Reference< XComponent > xComp(i->get(), UNO_QUERY);
802 22 : if (xComp.is())
803 8 : xComp->dispose();
804 22 : }
805 8 : m_aStatements.clear();
806 8 : }
807 :
808 4 : uno::Reference< XTablesSupplier > Connection::createCatalog()
809 : {
810 4 : MutexGuard aGuard(m_aMutex);
811 :
812 : // m_xCatalog is a weak reference. Reuse it if it still exists.
813 8 : Reference< XTablesSupplier > xCatalog = m_xCatalog;
814 4 : if (xCatalog.is())
815 : {
816 0 : return xCatalog;
817 : }
818 : else
819 : {
820 4 : xCatalog = new Catalog(this);
821 4 : m_xCatalog = xCatalog;
822 4 : return m_xCatalog;
823 4 : }
824 :
825 : }
826 :
827 4 : void Connection::rebuildIndexes() throw (SQLException, RuntimeException, std::exception)
828 : {
829 4 : MutexGuard aGuard(m_aMutex);
830 :
831 : try
832 : {
833 : // We only need to do this for character based columns on user-created tables.
834 :
835 : // Ideally we'd use a FOR SELECT ... INTO .... DO ..., but that seems to
836 : // only be possible using PSQL, i.e. using a stored procedure.
837 : OUString sSql(
838 : // multiple columns possible per index, only select once
839 : "SELECT DISTINCT indices.RDB$INDEX_NAME "
840 : "FROM RDB$INDICES indices "
841 : "JOIN RDB$INDEX_SEGMENTS index_segments "
842 : "ON (indices.RDB$INDEX_NAME = index_segments.RDB$INDEX_NAME) "
843 : "JOIN RDB$RELATION_FIELDS relation_fields "
844 : "ON (index_segments.RDB$FIELD_NAME = relation_fields.RDB$FIELD_NAME) "
845 : "JOIN RDB$FIELDS fields "
846 : "ON (relation_fields.RDB$FIELD_SOURCE = fields.RDB$FIELD_NAME) "
847 :
848 : "WHERE (indices.RDB$SYSTEM_FLAG = 0) "
849 : // TODO: what about blr_text2 etc. ?
850 8 : "AND ((fields.RDB$FIELD_TYPE = " + OUString::number((int) blr_text) + ") "
851 16 : " OR (fields.RDB$FIELD_TYPE = " + OUString::number((int) blr_varying) + ")) "
852 : "AND (indices.RDB$INDEX_INACTIVE IS NULL OR indices.RDB$INDEX_INACTIVE = 0) "
853 8 : );
854 :
855 8 : uno::Reference< XStatement > xCharIndicesStatement = createStatement();
856 : uno::Reference< XResultSet > xCharIndices =
857 8 : xCharIndicesStatement->executeQuery(sSql);
858 8 : uno::Reference< XRow > xRow(xCharIndices, UNO_QUERY_THROW);
859 :
860 8 : uno::Reference< XStatement > xAlterIndexStatement = createStatement();
861 :
862 : // ALTER is a DDL statement, hence using Statement will cause a commit
863 : // after every alter -- in this case this is inappropriate (xCharIndicesStatement
864 : // and its ResultSet become invalidated) hence we use the native api.
865 8 : while (xCharIndices->next())
866 : {
867 0 : OUString sIndexName(sanitizeIdentifier(xRow->getString(1)));
868 : SAL_INFO("connectivity.firebird", "rebuilding index " + sIndexName);
869 : OString sAlterIndex = "ALTER INDEX \""
870 0 : + OUStringToOString(sIndexName, RTL_TEXTENCODING_UTF8)
871 0 : + "\" ACTIVE";
872 :
873 : ISC_STATUS_ARRAY aStatusVector;
874 : ISC_STATUS aErr;
875 :
876 : aErr = isc_dsql_execute_immediate(aStatusVector,
877 0 : &getDBHandle(),
878 0 : &getTransaction(),
879 : 0, // Length: 0 for null terminated
880 : sAlterIndex.getStr(),
881 : FIREBIRD_SQL_DIALECT,
882 0 : NULL);
883 0 : if (aErr)
884 : evaluateStatusVector(aStatusVector,
885 : "rebuildIndexes:isc_dsql_execute_immediate",
886 0 : *this);
887 0 : }
888 8 : commit();
889 : }
890 0 : catch (const Exception&)
891 : {
892 0 : throw;
893 : }
894 0 : catch (const std::exception&)
895 : {
896 0 : throw;
897 : }
898 0 : catch (...) // const Firebird::Exception& firebird throws this, but doesn't install the fb_exception.h that declares it
899 : {
900 0 : throw std::runtime_error("Generic Firebird::Exception");
901 4 : }
902 :
903 16 : }
904 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|