1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_dbaccess.hxx"
30 
31 #include "tablecontainer.hxx"
32 #include "dbastrings.hrc"
33 #include "table.hxx"
34 #include <comphelper/property.hxx>
35 #include <comphelper/processfactory.hxx>
36 #include <tools/debug.hxx>
37 #include <comphelper/enumhelper.hxx>
38 #include "core_resource.hxx"
39 #include "core_resource.hrc"
40 #include <com/sun/star/sdb/CommandType.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/beans/PropertyState.hpp>
43 #include <com/sun/star/beans/XPropertyState.hpp>
44 #include <com/sun/star/sdbc/XConnection.hpp>
45 #include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
46 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
47 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
48 #include <com/sun/star/sdbc/KeyRule.hpp>
49 #include <com/sun/star/sdbcx/KeyType.hpp>
50 #include <com/sun/star/sdbc/ColumnValue.hpp>
51 #include <com/sun/star/sdbc/XRow.hpp>
52 #include <comphelper/types.hxx>
53 #include <connectivity/dbtools.hxx>
54 #include <comphelper/extract.hxx>
55 #include <connectivity/dbexception.hxx>
56 #include "TableDeco.hxx"
57 #include "sdbcoretools.hxx"
58 #include "ContainerMediator.hxx"
59 #include "definitioncolumn.hxx"
60 #include "objectnameapproval.hxx"
61 #include <tools/string.hxx>
62 #include <rtl/logfile.hxx>
63 #ifndef TOOLS_DIAGNOSE_EX_H
64 #include <tools/diagnose_ex.h>
65 #endif
66 #ifndef TOOLS_DIAGNOSE_EX_H
67 #include <tools/diagnose_ex.h>
68 #endif
69 
70 using namespace dbaccess;
71 using namespace dbtools;
72 using namespace ::com::sun::star::uno;
73 using namespace ::com::sun::star::lang;
74 using namespace ::com::sun::star::beans;
75 using namespace ::com::sun::star::sdbc;
76 using namespace ::com::sun::star::sdb;
77 using namespace ::com::sun::star::sdbcx;
78 using namespace ::com::sun::star::container;
79 using namespace ::com::sun::star::util;
80 using namespace ::osl;
81 using namespace ::comphelper;
82 using namespace ::cppu;
83 using namespace ::connectivity::sdbcx;
84 
85 namespace
86 {
87 	sal_Bool lcl_isPropertySetDefaulted(const Sequence< ::rtl::OUString>& _aNames,const Reference<XPropertySet>& _xProp)
88 	{
89 		Reference<XPropertyState> xState(_xProp,UNO_QUERY);
90 		if ( xState.is() )
91 		{
92 			const ::rtl::OUString* pIter = _aNames.getConstArray();
93 			const ::rtl::OUString* pEnd	  = pIter + _aNames.getLength();
94 			for(;pIter != pEnd;++pIter)
95 			{
96 				try
97 				{
98 					PropertyState aState = xState->getPropertyState(*pIter);
99 					if ( aState != PropertyState_DEFAULT_VALUE )
100 						break;
101 				}
102 				catch(Exception)
103 				{
104                     OSL_ENSURE( 0, "lcl_isPropertySetDefaulted: Exception caught!" );
105 				}
106 			}
107 			return ( pIter == pEnd );
108 		}
109 		return sal_False;
110 	}
111 }
112 //==========================================================================
113 //= OTableContainer
114 //==========================================================================
115 DBG_NAME(OTableContainer)
116 //------------------------------------------------------------------------------
117 OTableContainer::OTableContainer(::cppu::OWeakObject& _rParent,
118 								 ::osl::Mutex& _rMutex,
119 								 const Reference< XConnection >& _xCon,
120 								 sal_Bool _bCase,
121 								 const Reference< XNameContainer >&	_xTableDefinitions,
122 								 IRefreshListener*	_pRefreshListener,
123 								 ::dbtools::IWarningsContainer* _pWarningsContainer
124                                  ,oslInterlockedCount& _nInAppend)
125 	:OFilteredContainer(_rParent,_rMutex,_xCon,_bCase,_pRefreshListener,_pWarningsContainer,_nInAppend)
126 	,m_xTableDefinitions(_xTableDefinitions)
127 	,m_pTableMediator( NULL )
128 	,m_bInDrop(sal_False)
129 {
130 	DBG_CTOR(OTableContainer, NULL);
131 }
132 
133 //------------------------------------------------------------------------------
134 OTableContainer::~OTableContainer()
135 {
136     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::OTableContainer" );
137 	//	dispose();
138 	DBG_DTOR(OTableContainer, NULL);
139 }
140 
141 // -----------------------------------------------------------------------------
142 void OTableContainer::removeMasterContainerListener()
143 {
144     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::removeMasterContainerListener" );
145     try
146     {
147         Reference<XContainer> xCont( m_xMasterContainer, UNO_QUERY_THROW );
148         xCont->removeContainerListener( this );
149     }
150     catch( const Exception& )
151     {
152         DBG_UNHANDLED_EXCEPTION();
153     }
154 }
155 
156 // -----------------------------------------------------------------------------
157 ::rtl::OUString OTableContainer::getTableTypeRestriction() const
158 {
159     // no restriction at all (other than the ones provided externally)
160     return ::rtl::OUString();
161 }
162 
163 // -----------------------------------------------------------------------------
164 // XServiceInfo
165 //------------------------------------------------------------------------------
166 IMPLEMENT_SERVICE_INFO2(OTableContainer, "com.sun.star.sdb.dbaccess.OTableContainer", SERVICE_SDBCX_CONTAINER, SERVICE_SDBCX_TABLES)
167 
168 // -----------------------------------------------------------------------------
169 namespace
170 {
171 void lcl_createDefintionObject(const ::rtl::OUString& _rName
172 						   ,const Reference< XNameContainer >& _xTableDefinitions
173 						   ,Reference<XPropertySet>& _xTableDefinition
174 						   ,Reference<XNameAccess>& _xColumnDefinitions
175 						   ,sal_Bool _bModified)
176 {
177 	if ( _xTableDefinitions.is() )
178 	{
179 		if ( _xTableDefinitions->hasByName(_rName) )
180 			_xTableDefinition.set(_xTableDefinitions->getByName(_rName),UNO_QUERY);
181 		else
182 		{
183 			Sequence< Any > aArguments(1);
184 			PropertyValue aValue;
185 			// set as folder
186 			aValue.Name = PROPERTY_NAME;
187 			aValue.Value <<= _rName;
188 			aArguments[0] <<= aValue;
189 			_xTableDefinition.set(::comphelper::getProcessServiceFactory()->createInstanceWithArguments(SERVICE_SDB_TABLEDEFINITION,aArguments),UNO_QUERY);
190 			_xTableDefinitions->insertByName(_rName,makeAny(_xTableDefinition));
191 			::dbaccess::notifyDataSourceModified(_xTableDefinitions,_bModified);
192 		}
193 		Reference<XColumnsSupplier> xColumnsSupplier(_xTableDefinition,UNO_QUERY);
194 		if ( xColumnsSupplier.is() )
195 			_xColumnDefinitions = xColumnsSupplier->getColumns();
196 	}
197 }
198 // -------------------------------------------------------------------------
199 }
200 // -------------------------------------------------------------------------
201 connectivity::sdbcx::ObjectType OTableContainer::createObject(const ::rtl::OUString& _rName)
202 {
203     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::createObject" );
204 	Reference<XColumnsSupplier > xSup;
205 	if(m_xMasterContainer.is() && m_xMasterContainer->hasByName(_rName))
206 		xSup.set(m_xMasterContainer->getByName(_rName),UNO_QUERY);
207 
208 	connectivity::sdbcx::ObjectType xRet;
209 	if ( m_xMetaData.is() )
210 	{
211 		Reference<XPropertySet> xTableDefinition;
212 		Reference<XNameAccess> xColumnDefinitions;
213 		lcl_createDefintionObject(_rName,m_xTableDefinitions,xTableDefinition,xColumnDefinitions,sal_False);
214 
215 		if ( xSup.is() )
216 		{
217             ODBTableDecorator* pTable = new ODBTableDecorator( m_xConnection, xSup, ::dbtools::getNumberFormats( m_xConnection ) ,xColumnDefinitions);
218 			xRet = pTable;
219 			pTable->construct();
220 		}
221 		else
222 		{
223 			::rtl::OUString sCatalog,sSchema,sTable;
224 			::dbtools::qualifiedNameComponents(m_xMetaData,
225 												_rName,
226 												sCatalog,
227 												sSchema,
228 												sTable,
229 												::dbtools::eInDataManipulation);
230 			Any aCatalog;
231 			if(sCatalog.getLength())
232 				aCatalog <<= sCatalog;
233 			::rtl::OUString sType,sDescription;
234 			Sequence< ::rtl::OUString> aTypeFilter;
235             getAllTableTypeFilter( aTypeFilter );
236 
237 			Reference< XResultSet > xRes =  m_xMetaData.is() ? m_xMetaData->getTables(aCatalog,sSchema,sTable,aTypeFilter) : Reference< XResultSet >();
238 			if(xRes.is() && xRes->next())
239 			{
240 				Reference< XRow > xRow(xRes,UNO_QUERY);
241 				if(xRow.is())
242 				{
243 					sType			= xRow->getString(4);
244 					sDescription	= xRow->getString(5);
245 				}
246 			}
247 			::comphelper::disposeComponent(xRes);
248 			ODBTable* pTable = new ODBTable(this
249 								,m_xConnection
250 								,sCatalog
251 								,sSchema
252 								,sTable
253 								,sType
254 								,sDescription
255 								,xColumnDefinitions);
256 			xRet = pTable;
257 			pTable->construct();
258 		}
259 		Reference<XPropertySet> xDest(xRet,UNO_QUERY);
260 		if ( xTableDefinition.is() )
261 			::comphelper::copyProperties(xTableDefinition,xDest);
262 
263         if ( !m_pTableMediator.is() )
264             m_pTableMediator = new OContainerMediator(
265                     this, m_xTableDefinitions.get(), m_xConnection, OContainerMediator::eTables );
266 		if ( m_pTableMediator.is() )
267 			m_pTableMediator->notifyElementCreated(_rName,xDest);
268 	}
269 
270 	return xRet;
271 }
272 // -----------------------------------------------------------------------------
273 Reference< XPropertySet > OTableContainer::createDescriptor()
274 {
275     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::createDescriptor" );
276 	Reference< XPropertySet > xRet;
277 
278 	// frist we have to look if the master tables does support this
279 	// and if then create a table object as well with the master tables
280 	Reference<XColumnsSupplier > xMasterColumnsSup;
281 	Reference<XDataDescriptorFactory> xDataFactory(m_xMasterContainer,UNO_QUERY);
282 	if ( xDataFactory.is() && m_xMetaData.is() )
283 	{
284 		xMasterColumnsSup = Reference< XColumnsSupplier >( xDataFactory->createDataDescriptor(), UNO_QUERY );
285         ODBTableDecorator* pTable = new ODBTableDecorator( m_xConnection, xMasterColumnsSup, ::dbtools::getNumberFormats( m_xConnection ) ,NULL);
286 		xRet = pTable;
287 		pTable->construct();
288 	}
289 	else
290 	{
291 		ODBTable* pTable = new ODBTable(this, m_xConnection);
292 		xRet = pTable;
293 		pTable->construct();
294 	}
295 	return xRet;
296 }
297 // -----------------------------------------------------------------------------
298 // XAppend
299 ObjectType OTableContainer::appendObject( const ::rtl::OUString& _rForName, const Reference< XPropertySet >& descriptor )
300 {
301     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::appendObject" );
302 	// append the new table with a create stmt
303 	::rtl::OUString aName = getString(descriptor->getPropertyValue(PROPERTY_NAME));
304 	if(m_xMasterContainer.is() && m_xMasterContainer->hasByName(aName))
305 	{
306 		String sMessage(DBACORE_RESSTRING(RID_STR_TABLE_IS_FILTERED));
307 		sMessage.SearchAndReplaceAscii("$name$", aName);
308 		throw SQLException(sMessage,static_cast<XTypeProvider*>(static_cast<OFilteredContainer*>(this)),SQLSTATE_GENERAL,1000,Any());
309 	}
310 
311     Reference< XConnection > xConnection( m_xConnection.get(), UNO_QUERY );
312     PContainerApprove pApprove( new ObjectNameApproval( xConnection, ObjectNameApproval::TypeTable ) );
313     pApprove->approveElement( aName, descriptor );
314 
315 	try
316 	{
317         EnsureReset aReset(m_nInAppend);
318 		Reference<XAppend> xAppend(m_xMasterContainer,UNO_QUERY);
319 		if(xAppend.is())
320 		{
321 			xAppend->appendByDescriptor(descriptor);
322 		}
323 		else
324 		{
325 			::rtl::OUString aSql = ::dbtools::createSqlCreateTableStatement(descriptor,m_xConnection);
326 
327 			Reference<XConnection> xCon = m_xConnection;
328 			OSL_ENSURE(xCon.is(),"Connection is null!");
329 			if ( xCon.is() )
330 			{
331 				Reference< XStatement > xStmt = xCon->createStatement(  );
332 				if ( xStmt.is() )
333 					xStmt->execute(aSql);
334 				::comphelper::disposeComponent(xStmt);
335 			}
336 		}
337 	}
338 	catch(Exception&)
339 	{
340 		throw;
341 	}
342 
343     Reference<XPropertySet> xTableDefinition;
344 	Reference<XNameAccess> xColumnDefinitions;
345 	lcl_createDefintionObject(getNameForObject(descriptor),m_xTableDefinitions,xTableDefinition,xColumnDefinitions,sal_False);
346 	Reference<XColumnsSupplier> xSup(descriptor,UNO_QUERY);
347 	Reference<XDataDescriptorFactory> xFac(xColumnDefinitions,UNO_QUERY);
348 	Reference<XAppend> xAppend(xColumnDefinitions,UNO_QUERY);
349 	sal_Bool bModified = sal_False;
350 	if ( xSup.is() && xColumnDefinitions.is() && xFac.is() && xAppend.is() )
351 	{
352 		Reference<XNameAccess> xNames = xSup->getColumns();
353 		if ( xNames.is() )
354 		{
355 			Reference<XPropertySet> xProp = xFac->createDataDescriptor();
356 			Sequence< ::rtl::OUString> aSeq = xNames->getElementNames();
357 			const ::rtl::OUString* pIter = aSeq.getConstArray();
358 			const ::rtl::OUString* pEnd	  = pIter + aSeq.getLength();
359 			for(;pIter != pEnd;++pIter)
360 			{
361 				if ( !xColumnDefinitions->hasByName(*pIter) )
362 				{
363 					Reference<XPropertySet> xColumn(xNames->getByName(*pIter),UNO_QUERY);
364                     if ( !OColumnSettings::hasDefaultSettings( xColumn ) )
365 					{
366 						::comphelper::copyProperties( xColumn, xProp );
367 						xAppend->appendByDescriptor( xProp );
368 						bModified = sal_True;
369 					}
370 				}
371 			}
372 		}
373 	}
374 	const static ::rtl::OUString s_pTableProps[] = {	::rtl::OUString(PROPERTY_FILTER), ::rtl::OUString(PROPERTY_ORDER)
375 													, ::rtl::OUString(PROPERTY_APPLYFILTER), ::rtl::OUString(PROPERTY_FONT)
376 													, ::rtl::OUString(PROPERTY_ROW_HEIGHT), ::rtl::OUString(PROPERTY_TEXTCOLOR)
377 													, ::rtl::OUString(PROPERTY_TEXTLINECOLOR), ::rtl::OUString(PROPERTY_TEXTEMPHASIS)
378 													, ::rtl::OUString(PROPERTY_TEXTRELIEF) };
379 	Sequence< ::rtl::OUString> aNames(s_pTableProps,sizeof(s_pTableProps)/sizeof(s_pTableProps[0]));
380 	if ( bModified || !lcl_isPropertySetDefaulted(aNames,xTableDefinition) )
381 		::dbaccess::notifyDataSourceModified(m_xTableDefinitions,sal_True);
382 
383     return createObject( _rForName );
384 }
385 // -------------------------------------------------------------------------
386 // XDrop
387 void OTableContainer::dropObject(sal_Int32 _nPos,const ::rtl::OUString _sElementName)
388 {
389     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::dropObject" );
390 	m_bInDrop = sal_True;
391 	try
392 	{
393 		Reference< XDrop > xDrop(m_xMasterContainer,UNO_QUERY);
394 		if(xDrop.is())
395 			xDrop->dropByName(_sElementName);
396 		else
397 		{
398 			::rtl::OUString sCatalog,sSchema,sTable,sComposedName;
399 
400 			sal_Bool bIsView = sal_False;
401 			Reference<XPropertySet> xTable(getObject(_nPos),UNO_QUERY);
402 			if ( xTable.is() && m_xMetaData.is() )
403 			{
404 				if( m_xMetaData.is() && m_xMetaData->supportsCatalogsInTableDefinitions() )
405 					xTable->getPropertyValue(PROPERTY_CATALOGNAME)	>>= sCatalog;
406 				if( m_xMetaData.is() && m_xMetaData->supportsSchemasInTableDefinitions() )
407 					xTable->getPropertyValue(PROPERTY_SCHEMANAME)	>>= sSchema;
408 				xTable->getPropertyValue(PROPERTY_NAME)			>>= sTable;
409 
410 				sComposedName = ::dbtools::composeTableName( m_xMetaData, sCatalog, sSchema, sTable, sal_True, ::dbtools::eInTableDefinitions );
411 
412 				::rtl::OUString sType;
413 				xTable->getPropertyValue(PROPERTY_TYPE)			>>= sType;
414 				bIsView = sType.equalsIgnoreAsciiCase(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("VIEW")));
415 			}
416 
417 			if(!sComposedName.getLength())
418 				::dbtools::throwFunctionSequenceException(static_cast<XTypeProvider*>(static_cast<OFilteredContainer*>(this)));
419 
420 			::rtl::OUString aSql = ::rtl::OUString::createFromAscii("DROP ");
421 
422 			// #104282# OJ
423 			if ( bIsView ) // here we have a view
424 				aSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("VIEW "));
425 			else
426 				aSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("TABLE "));
427 			aSql += sComposedName;
428 			Reference<XConnection> xCon = m_xConnection;
429 			OSL_ENSURE(xCon.is(),"Connection is null!");
430 			if ( xCon.is() )
431 			{
432 				Reference< XStatement > xStmt = xCon->createStatement(  );
433 				if(xStmt.is())
434 					xStmt->execute(aSql);
435 				::comphelper::disposeComponent(xStmt);
436 			}
437 		}
438 
439 		if ( m_xTableDefinitions.is() && m_xTableDefinitions->hasByName(_sElementName) )
440 		{
441 			m_xTableDefinitions->removeByName(_sElementName);
442 		}
443 	}
444 	catch(Exception&)
445 	{
446 		m_bInDrop = sal_False;
447 		throw;
448 	}
449 	m_bInDrop = sal_False;
450 }
451 // -----------------------------------------------------------------------------
452 void SAL_CALL OTableContainer::elementInserted( const ContainerEvent& Event ) throw (RuntimeException)
453 {
454     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::elementInserted" );
455 	::osl::MutexGuard aGuard(m_rMutex);
456 	::rtl::OUString sName;
457 	Event.Accessor >>= sName;
458 	if ( !m_nInAppend && !hasByName(sName) )
459 	{
460 		if(!m_xMasterContainer.is() || m_xMasterContainer->hasByName(sName))
461 		{
462 			ObjectType xName = createObject(sName);
463 			insertElement(sName,xName);
464 			// and notify our listeners
465 			ContainerEvent aEvent(static_cast<XContainer*>(this), makeAny(sName), makeAny(xName), Any());
466             m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvent );
467 		}
468 	}
469 }
470 // -----------------------------------------------------------------------------
471 void SAL_CALL OTableContainer::elementRemoved( const ContainerEvent& /*Event*/ ) throw (RuntimeException)
472 {
473     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::elementRemoved" );
474 }
475 // -----------------------------------------------------------------------------
476 void SAL_CALL OTableContainer::elementReplaced( const ContainerEvent& Event ) throw (RuntimeException)
477 {
478     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::elementReplaced" );
479 	// create a new config entry
480 	{
481 		::rtl::OUString sOldComposedName,sNewComposedName;
482 		Event.ReplacedElement	>>= sOldComposedName;
483 		Event.Accessor			>>= sNewComposedName;
484 
485 		renameObject(sOldComposedName,sNewComposedName);
486 	}
487 }
488 // -----------------------------------------------------------------------------
489 void SAL_CALL OTableContainer::disposing()
490 {
491     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::disposing" );
492 	OFilteredContainer::disposing();
493 	// say our listeners goobye
494 	m_xTableDefinitions	= NULL;
495 	m_pTableMediator = NULL;
496 }
497 // -----------------------------------------------------------------------------
498 void SAL_CALL OTableContainer::disposing( const ::com::sun::star::lang::EventObject& /*Source*/ ) throw (::com::sun::star::uno::RuntimeException)
499 {
500     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "api", "Ocke.Janssen@sun.com", "OTableContainer::disposing" );
501 }
502 
503 // -----------------------------------------------------------------------------
504 void OTableContainer::addMasterContainerListener()
505 {
506     try
507     {
508         Reference< XContainer > xCont( m_xMasterContainer, UNO_QUERY_THROW );
509         xCont->addContainerListener( this );
510     }
511     catch( const Exception& )
512     {
513         DBG_UNHANDLED_EXCEPTION();
514     }
515 }
516 
517