1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: TTableHelper.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_connectivity.hxx"
33 #include "connectivity/TTableHelper.hxx"
34 #include <com/sun/star/sdbc/XRow.hpp>
35 #include <com/sun/star/sdbc/XResultSet.hpp>
36 #include <com/sun/star/sdbcx/KeyType.hpp>
37 #include <com/sun/star/sdbc/KeyRule.hpp>
38 #include <cppuhelper/typeprovider.hxx>
39 #include <com/sun/star/lang/DisposedException.hpp>
40 #include <com/sun/star/sdbc/ColumnValue.hpp>
41 #include <comphelper/implementationreference.hxx>
42 #include <comphelper/sequence.hxx>
43 #include <comphelper/extract.hxx>
44 #include <comphelper/types.hxx>
45 #include "connectivity/dbtools.hxx"
46 #include "connectivity/sdbcx/VCollection.hxx"
47 #include <unotools/sharedunocomponent.hxx>
48 #include "TConnection.hxx"
50 using namespace ::comphelper
;
51 using namespace connectivity
;
52 using namespace ::com::sun::star::uno
;
53 using namespace ::com::sun::star::beans
;
54 using namespace ::com::sun::star::sdbcx
;
55 using namespace ::com::sun::star::sdbc
;
56 using namespace ::com::sun::star::container
;
57 using namespace ::com::sun::star::lang
;
60 /// helper class for column property change events which holds the OComponentDefinition weak
61 typedef ::cppu::WeakImplHelper1
< XContainerListener
> OTableContainerListener_BASE
;
62 class OTableContainerListener
: public OTableContainerListener_BASE
64 OTableHelper
* m_pComponent
;
65 ::std::map
< ::rtl::OUString
,bool> m_aRefNames
;
67 OTableContainerListener(const OTableContainerListener
&);
68 void operator =(const OTableContainerListener
&);
70 virtual ~OTableContainerListener(){}
72 OTableContainerListener(OTableHelper
* _pComponent
) : m_pComponent(_pComponent
){}
73 virtual void SAL_CALL
elementInserted( const ::com::sun::star::container::ContainerEvent
& /*Event*/ ) throw (RuntimeException
)
76 virtual void SAL_CALL
elementRemoved( const ::com::sun::star::container::ContainerEvent
& Event
) throw (RuntimeException
)
78 ::rtl::OUString sName
;
79 Event
.Accessor
>>= sName
;
80 if ( m_aRefNames
.find(sName
) != m_aRefNames
.end() )
81 m_pComponent
->refreshKeys();
83 virtual void SAL_CALL
elementReplaced( const ::com::sun::star::container::ContainerEvent
& Event
) throw (RuntimeException
)
85 ::rtl::OUString sOldComposedName
,sNewComposedName
;
86 Event
.ReplacedElement
>>= sOldComposedName
;
87 Event
.Accessor
>>= sNewComposedName
;
88 if ( sOldComposedName
!= sNewComposedName
&& m_aRefNames
.find(sOldComposedName
) != m_aRefNames
.end() )
89 m_pComponent
->refreshKeys();
92 virtual void SAL_CALL
disposing( const EventObject
& /*_rSource*/ ) throw (RuntimeException
)
95 void clear() { m_pComponent
= NULL
; }
96 inline void add(const ::rtl::OUString
& _sRefName
) { m_aRefNames
.insert(::std::map
< ::rtl::OUString
,bool>::value_type(_sRefName
,true)); }
99 namespace connectivity
101 struct OTableHelperImpl
104 Reference
< ::com::sun::star::sdbc::XDatabaseMetaData
> m_xMetaData
;
105 Reference
< ::com::sun::star::sdbc::XConnection
> m_xConnection
;
106 ::comphelper::ImplementationReference
< OTableContainerListener
,XContainerListener
>
107 m_xTablePropertyListener
;
108 ::std::vector
< ColumnDesc
> m_aColumnDesc
;
112 OTableHelper::OTableHelper( sdbcx::OCollection
* _pTables
,
113 const Reference
< XConnection
>& _xConnection
,
115 :OTable_TYPEDEF(_pTables
,_bCase
)
116 ,m_pImpl(new OTableHelperImpl
)
120 m_pImpl
->m_xConnection
= _xConnection
;
121 m_pImpl
->m_xMetaData
= m_pImpl
->m_xConnection
->getMetaData();
123 catch(const Exception
&)
127 // -------------------------------------------------------------------------
128 OTableHelper::OTableHelper( sdbcx::OCollection
* _pTables
,
129 const Reference
< XConnection
>& _xConnection
,
131 const ::rtl::OUString
& _Name
,
132 const ::rtl::OUString
& _Type
,
133 const ::rtl::OUString
& _Description
,
134 const ::rtl::OUString
& _SchemaName
,
135 const ::rtl::OUString
& _CatalogName
136 ) : OTable_TYPEDEF(_pTables
,
143 ,m_pImpl(new OTableHelperImpl
)
147 m_pImpl
->m_xConnection
= _xConnection
;
148 m_pImpl
->m_xMetaData
= m_pImpl
->m_xConnection
->getMetaData();
150 catch(const Exception
&)
154 // -----------------------------------------------------------------------------
155 OTableHelper::~OTableHelper()
158 // -----------------------------------------------------------------------------
159 void SAL_CALL
OTableHelper::disposing()
161 ::osl::MutexGuard
aGuard(m_aMutex
);
162 if ( m_pImpl
->m_xTablePropertyListener
.is() )
164 m_pTables
->removeContainerListener(m_pImpl
->m_xTablePropertyListener
.getRef());
165 m_pImpl
->m_xTablePropertyListener
->clear();
166 m_pImpl
->m_xTablePropertyListener
.dispose();
168 OTable_TYPEDEF::disposing();
170 m_pImpl
->m_xConnection
= NULL
;
171 m_pImpl
->m_xMetaData
= NULL
;
175 // -------------------------------------------------------------------------
178 /** collects ColumnDesc's from a resultset produced by XDatabaseMetaData::getColumns
180 void lcl_collectColumnDescs_throw( const Reference
< XResultSet
>& _rxResult
, ::std::vector
< ColumnDesc
>& _out_rColumns
)
182 Reference
< XRow
> xRow( _rxResult
, UNO_QUERY_THROW
);
183 ::rtl::OUString sName
;
184 OrdinalPosition
nOrdinalPosition( 0 );
185 while ( _rxResult
->next() )
187 sName
= xRow
->getString( 4 ); // COLUMN_NAME
188 sal_Int32 nField5
= xRow
->getInt(5);
189 ::rtl::OUString aField6
= xRow
->getString(6);
190 sal_Int32 nField7
= xRow
->getInt(7)
191 , nField9
= xRow
->getInt(9)
192 , nField11
= xRow
->getInt(11);
193 ::rtl::OUString sField13
= xRow
->getString(13);
194 nOrdinalPosition
= xRow
->getInt( 17 ); // ORDINAL_POSITION
195 _out_rColumns
.push_back( ColumnDesc( sName
,nField5
,aField6
,nField7
,nField9
,nField11
,sField13
, nOrdinalPosition
) );
199 /** checks a given array of ColumnDesc's whether it has reasonable ordinal positions. If not,
200 they will be normalized to be the array index.
202 void lcl_sanitizeColumnDescs( ::std::vector
< ColumnDesc
>& _rColumns
)
204 if ( _rColumns
.empty() )
207 // collect all used ordinals
208 ::std::set
< OrdinalPosition
> aUsedOrdinals
;
209 for ( ::std::vector
< ColumnDesc
>::iterator collect
= _rColumns
.begin();
210 collect
!= _rColumns
.end();
213 aUsedOrdinals
.insert( collect
->nOrdinalPosition
);
215 // we need to have as much different ordinals as we have different columns
216 bool bDuplicates
= aUsedOrdinals
.size() != _rColumns
.size();
217 // and it needs to be a continuous range
218 size_t nOrdinalsRange
= *aUsedOrdinals
.rbegin() - *aUsedOrdinals
.begin() + 1;
219 bool bGaps
= nOrdinalsRange
!= _rColumns
.size();
221 // if that's not the case, normalize it
222 if ( bGaps
|| bDuplicates
)
224 OSL_ENSURE( false, "lcl_sanitizeColumnDescs: database did provide invalid ORDINAL_POSITION values!" );
226 OrdinalPosition nNormalizedPosition
= 1;
227 for ( ::std::vector
< ColumnDesc
>::iterator normalize
= _rColumns
.begin();
228 normalize
!= _rColumns
.end();
231 normalize
->nOrdinalPosition
= nNormalizedPosition
++;
235 // what's left is that the range might not be from 1 to <column count>, but for instance
236 // 0 to <column count>-1.
237 size_t nOffset
= *aUsedOrdinals
.begin() - 1;
238 for ( ::std::vector
< ColumnDesc
>::iterator offset
= _rColumns
.begin();
239 offset
!= _rColumns
.end();
242 offset
->nOrdinalPosition
-= nOffset
;
246 // -------------------------------------------------------------------------
247 void OTableHelper::refreshColumns()
249 TStringVector aVector
;
253 if ( m_CatalogName
.getLength() )
254 aCatalog
<<= m_CatalogName
;
256 ::utl::SharedUNOComponent
< XResultSet
> xResult( getMetaData()->getColumns(
260 ::rtl::OUString::createFromAscii("%")
263 // collect the column names, together with their ordinal position
264 m_pImpl
->m_aColumnDesc
.clear();
265 lcl_collectColumnDescs_throw( xResult
, m_pImpl
->m_aColumnDesc
);
267 // ensure that the ordinal positions as obtained from the meta data do make sense
268 lcl_sanitizeColumnDescs( m_pImpl
->m_aColumnDesc
);
270 // sort by ordinal position
271 ::std::map
< OrdinalPosition
, ::rtl::OUString
> aSortedColumns
;
272 for ( ::std::vector
< ColumnDesc
>::const_iterator copy
= m_pImpl
->m_aColumnDesc
.begin();
273 copy
!= m_pImpl
->m_aColumnDesc
.end();
276 aSortedColumns
[ copy
->nOrdinalPosition
] = copy
->sName
;
278 // copy them to aVector, now that we have the proper ordering
280 aSortedColumns
.begin(),
281 aSortedColumns
.end(),
282 ::std::insert_iterator
< TStringVector
>( aVector
, aVector
.begin() ),
283 ::std::select2nd
< ::std::map
< OrdinalPosition
, ::rtl::OUString
>::value_type
>()
288 m_pColumns
->reFill(aVector
);
290 m_pColumns
= createColumns(aVector
);
292 // -----------------------------------------------------------------------------
293 const ColumnDesc
* OTableHelper::getColumnDescription(const ::rtl::OUString
& _sName
) const
295 const ColumnDesc
* pRet
= NULL
;
296 ::std::vector
< ColumnDesc
>::const_iterator aEnd
= m_pImpl
->m_aColumnDesc
.end();
297 for (::std::vector
< ColumnDesc
>::const_iterator aIter
= m_pImpl
->m_aColumnDesc
.begin();aIter
!= aEnd
;++aIter
)
299 if ( aIter
->sName
== _sName
)
304 } // for (::std::vector< ColumnDesc >::const_iterator aIter = m_pImpl->m_aColumnDesc.begin();aIter != aEnd;++aIter)
307 // -------------------------------------------------------------------------
308 void OTableHelper::refreshPrimaryKeys(TStringVector
& _rNames
)
311 if ( m_CatalogName
.getLength() )
312 aCatalog
<<= m_CatalogName
;
313 Reference
< XResultSet
> xResult
= getMetaData()->getPrimaryKeys(aCatalog
,m_SchemaName
,m_Name
);
317 sdbcx::TKeyProperties
pKeyProps(new sdbcx::KeyProperties(::rtl::OUString(),KeyType::PRIMARY
,0,0));
318 ::rtl::OUString aPkName
;
319 bool bAlreadyFetched
= false;
320 const Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
321 while ( xResult
->next() )
323 pKeyProps
->m_aKeyColumnNames
.push_back(xRow
->getString(4));
324 if ( !bAlreadyFetched
)
326 aPkName
= xRow
->getString(6);
327 bAlreadyFetched
= true;
331 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(aPkName
,pKeyProps
));
332 _rNames
.push_back(aPkName
);
333 } // if ( xResult.is() && xResult->next() )
334 ::comphelper::disposeComponent(xResult
);
336 // -------------------------------------------------------------------------
337 void OTableHelper::refreshForgeinKeys(TStringVector
& _rNames
)
340 if ( m_CatalogName
.getLength() )
341 aCatalog
<<= m_CatalogName
;
342 Reference
< XResultSet
> xResult
= getMetaData()->getImportedKeys(aCatalog
,m_SchemaName
,m_Name
);
343 Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
347 sdbcx::TKeyProperties pKeyProps
;
348 ::rtl::OUString aName
,sCatalog
,aSchema
,sOldFKName
;
349 while( xResult
->next() )
351 // this must be outsid the "if" because we have to call in a right order
352 sCatalog
= xRow
->getString(1);
353 if ( xRow
->wasNull() )
354 sCatalog
= ::rtl::OUString();
355 aSchema
= xRow
->getString(2);
356 aName
= xRow
->getString(3);
358 const ::rtl::OUString sForeignKeyColumn
= xRow
->getString(8);
359 const sal_Int32 nUpdateRule
= xRow
->getInt(10);
360 const sal_Int32 nDeleteRule
= xRow
->getInt(11);
361 const ::rtl::OUString sFkName
= xRow
->getString(12);
363 if ( pKeyProps
.get() )
368 if ( sFkName
.getLength() && !xRow
->wasNull() )
370 if ( sOldFKName
!= sFkName
)
372 if ( pKeyProps
.get() )
373 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(sOldFKName
,pKeyProps
));
375 const ::rtl::OUString sReferencedName
= ::dbtools::composeTableName(getMetaData(),sCatalog
,aSchema
,aName
,sal_False
,::dbtools::eInDataManipulation
);
376 pKeyProps
.reset(new sdbcx::KeyProperties(sReferencedName
,KeyType::FOREIGN
,nUpdateRule
,nDeleteRule
));
377 pKeyProps
->m_aKeyColumnNames
.push_back(sForeignKeyColumn
);
378 _rNames
.push_back(sFkName
);
379 if ( m_pTables
->hasByName(sReferencedName
) )
381 if ( !m_pImpl
->m_xTablePropertyListener
.is() )
382 m_pImpl
->m_xTablePropertyListener
= ::comphelper::ImplementationReference
< OTableContainerListener
,XContainerListener
>( new OTableContainerListener(this) );
383 m_pTables
->addContainerListener(m_pImpl
->m_xTablePropertyListener
.getRef());
384 m_pImpl
->m_xTablePropertyListener
->add(sReferencedName
);
385 } // if ( m_pTables->hasByName(sReferencedName) )
386 sOldFKName
= sFkName
;
387 } // if ( sOldFKName != sFkName )
388 else if ( pKeyProps
.get() )
390 pKeyProps
->m_aKeyColumnNames
.push_back(sForeignKeyColumn
);
393 } // while( xResult->next() )
394 if ( pKeyProps
.get() )
395 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(sOldFKName
,pKeyProps
));
396 ::comphelper::disposeComponent(xResult
);
399 // -------------------------------------------------------------------------
400 void OTableHelper::refreshKeys()
402 m_pImpl
->m_aKeys
.clear();
404 TStringVector aNames
;
408 refreshPrimaryKeys(aNames
);
409 refreshForgeinKeys(aNames
);
410 m_pKeys
= createKeys(aNames
);
413 m_pKeys
= createKeys(aNames
);
415 m_pKeys->reFill(aVector);
419 // -------------------------------------------------------------------------
420 void OTableHelper::refreshIndexes()
422 TStringVector aVector
;
427 if ( m_CatalogName
.getLength() )
428 aCatalog
<<= m_CatalogName
;
429 Reference
< XResultSet
> xResult
= getMetaData()->getIndexInfo(aCatalog
,m_SchemaName
,m_Name
,sal_False
,sal_False
);
433 Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
434 ::rtl::OUString aName
;
435 ::rtl::OUString sCatalogSep
= getMetaData()->getCatalogSeparator();
436 ::rtl::OUString sPreviousRoundName
;
437 while( xResult
->next() )
439 aName
= xRow
->getString(5);
440 if(aName
.getLength())
441 aName
+= sCatalogSep
;
442 aName
+= xRow
->getString(6);
443 if ( aName
.getLength() )
445 // don't insert the name if the last one we inserted was the same
446 if (sPreviousRoundName
!= aName
)
447 aVector
.push_back(aName
);
449 sPreviousRoundName
= aName
;
451 ::comphelper::disposeComponent(xResult
);
456 m_pIndexes
->reFill(aVector
);
458 m_pIndexes
= createIndexes(aVector
);
460 // -----------------------------------------------------------------------------
461 ::rtl::OUString
OTableHelper::getRenameStart() const
463 ::rtl::OUString
sSql(RTL_CONSTASCII_USTRINGPARAM("RENAME "));
464 if ( m_Type
== ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("VIEW")) )
465 sSql
+= ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" VIEW "));
467 sSql
+= ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" TABLE "));
471 // -------------------------------------------------------------------------
473 void SAL_CALL
OTableHelper::rename( const ::rtl::OUString
& newName
) throw(SQLException
, ElementExistException
, RuntimeException
)
475 ::osl::MutexGuard
aGuard(m_aMutex
);
478 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
486 ::rtl::OUString sSql
= getRenameStart();
487 ::rtl::OUString sQuote
= getMetaData()->getIdentifierQuoteString( );
489 ::rtl::OUString sCatalog
,sSchema
,sTable
;
490 ::dbtools::qualifiedNameComponents(getMetaData(),newName
,sCatalog
,sSchema
,sTable
,::dbtools::eInDataManipulation
);
492 ::rtl::OUString sComposedName
;
493 sComposedName
= ::dbtools::composeTableName(getMetaData(),m_CatalogName
,m_SchemaName
,m_Name
,sal_True
,::dbtools::eInDataManipulation
);
494 sSql
+= sComposedName
495 + ::rtl::OUString::createFromAscii(" TO ");
496 sComposedName
= ::dbtools::composeTableName(getMetaData(),sCatalog
,sSchema
,sTable
,sal_True
,::dbtools::eInDataManipulation
);
497 sSql
+= sComposedName
;
499 Reference
< XStatement
> xStmt
= m_pImpl
->m_xConnection
->createStatement( );
502 xStmt
->execute(sSql
);
503 ::comphelper::disposeComponent(xStmt
);
506 OTable_TYPEDEF::rename(newName
);
509 ::dbtools::qualifiedNameComponents(getMetaData(),newName
,m_CatalogName
,m_SchemaName
,m_Name
,::dbtools::eInTableDefinitions
);
511 // -----------------------------------------------------------------------------
512 Reference
< XDatabaseMetaData
> OTableHelper::getMetaData() const
514 return m_pImpl
->m_xMetaData
;
516 // -------------------------------------------------------------------------
517 void SAL_CALL
OTableHelper::alterColumnByIndex( sal_Int32 index
, const Reference
< XPropertySet
>& descriptor
) throw(SQLException
, ::com::sun::star::lang::IndexOutOfBoundsException
, RuntimeException
)
519 ::osl::MutexGuard
aGuard(m_aMutex
);
522 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
528 Reference
< XPropertySet
> xOld
;
529 if(::cppu::extractInterface(xOld
,m_pColumns
->getByIndex(index
)) && xOld
.is())
530 alterColumnByName(getString(xOld
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME
))),descriptor
);
533 // -------------------------------------------------------------------------
534 ::rtl::OUString SAL_CALL
OTableHelper::getName() throw(RuntimeException
)
536 ::rtl::OUString sComposedName
;
537 sComposedName
= ::dbtools::composeTableName(getMetaData(),m_CatalogName
,m_SchemaName
,m_Name
,sal_False
,::dbtools::eInDataManipulation
);
538 return sComposedName
;
540 // -----------------------------------------------------------------------------
541 void SAL_CALL
OTableHelper::acquire() throw()
543 OTable_TYPEDEF::acquire();
545 // -----------------------------------------------------------------------------
546 void SAL_CALL
OTableHelper::release() throw()
548 OTable_TYPEDEF::release();
550 // -----------------------------------------------------------------------------
551 sdbcx::TKeyProperties
OTableHelper::getKeyProperties(const ::rtl::OUString
& _sName
) const
553 sdbcx::TKeyProperties pKeyProps
;
554 TKeyMap::const_iterator aFind
= m_pImpl
->m_aKeys
.find(_sName
);
555 if ( aFind
!= m_pImpl
->m_aKeys
.end() )
557 pKeyProps
= aFind
->second
;
559 else // only a fall back
561 OSL_ENSURE(0,"No key with the given name found");
562 pKeyProps
.reset(new sdbcx::KeyProperties());
567 // -----------------------------------------------------------------------------
568 void OTableHelper::addKey(const ::rtl::OUString
& _sName
,const sdbcx::TKeyProperties
& _aKeyProperties
)
570 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(_sName
,_aKeyProperties
));
572 // -----------------------------------------------------------------------------
573 ::rtl::OUString
OTableHelper::getTypeCreatePattern() const
575 return ::rtl::OUString();
577 // -----------------------------------------------------------------------------
578 Reference
< XConnection
> OTableHelper::getConnection() const
580 return m_pImpl
->m_xConnection
;