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