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_connectivity.hxx"
30 #include "hsqldb/HTable.hxx"
31 #include "hsqldb/HTables.hxx"
32 #include <com/sun/star/sdbc/XRow.hpp>
33 #include <com/sun/star/sdbc/XResultSet.hpp>
34 #include <com/sun/star/sdbcx/KeyType.hpp>
35 #include <com/sun/star/sdbc/KeyRule.hpp>
36 #include <cppuhelper/typeprovider.hxx>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <com/sun/star/sdbc/ColumnValue.hpp>
39 #include <com/sun/star/sdbcx/Privilege.hpp>
40 #include <comphelper/property.hxx>
41 #include <comphelper/extract.hxx>
42 #include <comphelper/types.hxx>
43 #include "connectivity/dbtools.hxx"
44 #include "connectivity/sdbcx/VColumn.hxx"
45 #include "connectivity/TKeys.hxx"
46 #include "connectivity/TIndexes.hxx"
47 #include "connectivity/TColumnsHelper.hxx"
48 #include "hsqldb/HCatalog.hxx"
49 #include "hsqldb/HColumns.hxx"
50 #include "TConnection.hxx"
51 
52 #include <tools/diagnose_ex.h>
53 
54 
55 using namespace ::comphelper;
56 using namespace connectivity::hsqldb;
57 using namespace connectivity::sdbcx;
58 using namespace connectivity;
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::beans;
61 using namespace ::com::sun::star::sdbcx;
62 using namespace ::com::sun::star::sdbc;
63 using namespace ::com::sun::star::container;
64 using namespace ::com::sun::star::lang;
65 
66 OHSQLTable::OHSQLTable(	sdbcx::OCollection* _pTables,
67 						   const Reference<	XConnection >& _xConnection)
68 	:OTableHelper(_pTables,_xConnection,sal_True)
69 {
70 	// we create a new table here, so we should have all the rights or ;-)
71 	m_nPrivileges = Privilege::DROP			|
72 					Privilege::REFERENCE	|
73 					Privilege::ALTER		|
74 					Privilege::CREATE		|
75 					Privilege::READ			|
76 					Privilege::DELETE		|
77 					Privilege::UPDATE		|
78 					Privilege::INSERT		|
79 					Privilege::SELECT;
80 	construct();
81 }
82 // -------------------------------------------------------------------------
83 OHSQLTable::OHSQLTable(	sdbcx::OCollection* _pTables,
84 						   const Reference< XConnection >& _xConnection,
85 					const ::rtl::OUString& _Name,
86 					const ::rtl::OUString& _Type,
87 					const ::rtl::OUString& _Description ,
88 					const ::rtl::OUString& _SchemaName,
89 					const ::rtl::OUString& _CatalogName,
90 					sal_Int32 _nPrivileges
91 				) : OTableHelper(	_pTables,
92 									_xConnection,
93 									sal_True,
94 									_Name,
95 									_Type,
96 									_Description,
97 									_SchemaName,
98 									_CatalogName)
99  , m_nPrivileges(_nPrivileges)
100 {
101 	construct();
102 }
103 // -------------------------------------------------------------------------
104 void OHSQLTable::construct()
105 {
106 	OTableHelper::construct();
107 	if ( !isNew() )
108 		registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRIVILEGES),	PROPERTY_ID_PRIVILEGES,PropertyAttribute::READONLY,&m_nPrivileges,	::getCppuType(&m_nPrivileges));
109 }
110 // -----------------------------------------------------------------------------
111 ::cppu::IPropertyArrayHelper* OHSQLTable::createArrayHelper( sal_Int32 /*_nId*/ ) const
112 {
113     return doCreateArrayHelper();
114 }
115 // -------------------------------------------------------------------------
116 ::cppu::IPropertyArrayHelper & OHSQLTable::getInfoHelper()
117 {
118 	return *static_cast<OHSQLTable_PROP*>(const_cast<OHSQLTable*>(this))->getArrayHelper(isNew() ? 1 : 0);
119 }
120 // -----------------------------------------------------------------------------
121 sdbcx::OCollection* OHSQLTable::createColumns(const TStringVector& _rNames)
122 {
123 	OHSQLColumns* pColumns = new OHSQLColumns(*this,sal_True,m_aMutex,_rNames);
124 	pColumns->setParent(this);
125 	return pColumns;
126 }
127 // -----------------------------------------------------------------------------
128 sdbcx::OCollection* OHSQLTable::createKeys(const TStringVector& _rNames)
129 {
130 	return new OKeysHelper(this,m_aMutex,_rNames);
131 }
132 // -----------------------------------------------------------------------------
133 sdbcx::OCollection* OHSQLTable::createIndexes(const TStringVector& _rNames)
134 {
135 	return new OIndexesHelper(this,m_aMutex,_rNames);
136 }
137 //--------------------------------------------------------------------------
138 Sequence< sal_Int8 > OHSQLTable::getUnoTunnelImplementationId()
139 {
140 	static ::cppu::OImplementationId * pId = 0;
141 	if (! pId)
142 	{
143 		::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
144 		if (! pId)
145 		{
146 			static ::cppu::OImplementationId aId;
147 			pId = &aId;
148 		}
149 	}
150 	return pId->getImplementationId();
151 }
152 
153 // com::sun::star::lang::XUnoTunnel
154 //------------------------------------------------------------------
155 sal_Int64 OHSQLTable::getSomething( const Sequence< sal_Int8 > & rId ) throw (RuntimeException)
156 {
157 	return (rId.getLength() == 16 && 0 == rtl_compareMemory(getUnoTunnelImplementationId().getConstArray(),  rId.getConstArray(), 16 ) )
158 				? reinterpret_cast< sal_Int64 >( this )
159 				: OTable_TYPEDEF::getSomething(rId);
160 }
161 // -------------------------------------------------------------------------
162 // XAlterTable
163 void SAL_CALL OHSQLTable::alterColumnByName( const ::rtl::OUString& colName, const Reference< XPropertySet >& descriptor ) throw(SQLException, NoSuchElementException, RuntimeException)
164 {
165 	::osl::MutexGuard aGuard(m_aMutex);
166 	checkDisposed(
167 #ifdef GCC
168 		::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
169 #else
170 		rBHelper.bDisposed
171 #endif
172 		);
173 
174 	if ( m_pColumns && !m_pColumns->hasByName(colName) )
175 		throw NoSuchElementException(colName,*this);
176 
177 
178 	if ( !isNew() )
179 	{
180 		// first we have to check what should be altered
181 		Reference<XPropertySet> xProp;
182 		m_pColumns->getByName(colName) >>= xProp;
183 		// first check the types
184 		sal_Int32 nOldType = 0,nNewType = 0,nOldPrec = 0,nNewPrec = 0,nOldScale = 0,nNewScale = 0;
185         ::rtl::OUString sOldTypeName, sNewTypeName;
186 
187 		::dbtools::OPropertyMap& rProp = OMetaConnection::getPropMap();
188 
189         // type/typename
190         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE))			>>= nOldType;
191 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE))	>>= nNewType;
192         xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME))		>>= sOldTypeName;
193 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME))>>= sNewTypeName;
194 
195         // and precsions and scale
196 		xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION))	>>= nOldPrec;
197 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION))>>= nNewPrec;
198 		xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE))		>>= nOldScale;
199 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE))	>>= nNewScale;
200 
201         // second: check the "is nullable" value
202 		sal_Int32 nOldNullable = 0,nNewNullable = 0;
203 		xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE))		>>= nOldNullable;
204 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE))	>>= nNewNullable;
205 
206 		// check also the auto_increment
207 		sal_Bool bOldAutoIncrement = sal_False,bAutoIncrement = sal_False;
208 		xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))		>>= bOldAutoIncrement;
209 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))	>>= bAutoIncrement;
210 
211         // now we should look if the name of the column changed
212 		::rtl::OUString sNewColumnName;
213 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_NAME)) >>= sNewColumnName;
214 		if ( !sNewColumnName.equals(colName) )
215 		{
216 			const ::rtl::OUString sQuote = getMetaData()->getIdentifierQuoteString(  );
217 
218             ::rtl::OUString sSql = getAlterTableColumnPart();
219 			sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" ALTER COLUMN "));
220 			sSql += ::dbtools::quoteName(sQuote,colName);
221 
222             sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" RENAME TO "));
223             sSql += ::dbtools::quoteName(sQuote,sNewColumnName);
224 
225             executeStatement(sSql);
226 		}
227 
228 		if  (   nOldType != nNewType
229             ||  sOldTypeName != sNewTypeName
230 			||	nOldPrec != nNewPrec
231 			||	nOldScale != nNewScale
232 			||	nNewNullable != nOldNullable
233 			||	bOldAutoIncrement != bAutoIncrement )
234 		{
235 			// special handling because they change the type names to distinguish
236 			// if a column should be an auto_incmrement one
237 			if ( bOldAutoIncrement != bAutoIncrement )
238 			{
239 				/// TODO: insert special handling for auto increment "IDENTITY" and primary key
240 			}
241 			alterColumnType(nNewType,sNewColumnName,descriptor);
242 		}
243 
244 		// third: check the default values
245 		::rtl::OUString sNewDefault,sOldDefault;
246 		xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE))		>>= sOldDefault;
247 		descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)) >>= sNewDefault;
248 
249 		if(sOldDefault.getLength())
250 		{
251 			dropDefaultValue(colName);
252 			if(sNewDefault.getLength() && sOldDefault != sNewDefault)
253 				alterDefaultValue(sNewDefault,sNewColumnName);
254 		}
255 		else if(!sOldDefault.getLength() && sNewDefault.getLength())
256 			alterDefaultValue(sNewDefault,sNewColumnName);
257 
258 		m_pColumns->refresh();
259 	}
260 	else
261 	{
262 		if(m_pColumns)
263 		{
264 			m_pColumns->dropByName(colName);
265 			m_pColumns->appendByDescriptor(descriptor);
266 		}
267 	}
268 
269 }
270 // -----------------------------------------------------------------------------
271 void OHSQLTable::alterColumnType(sal_Int32 nNewType,const ::rtl::OUString& _rColName, const Reference<XPropertySet>& _xDescriptor)
272 {
273 	::rtl::OUString sSql = getAlterTableColumnPart();
274 
275     sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" ALTER COLUMN "));
276 #if OSL_DEBUG_LEVEL > 0
277     try
278     {
279         ::rtl::OUString sDescriptorName;
280         OSL_ENSURE( _xDescriptor.is()
281                 &&  ( _xDescriptor->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_NAME ) ) >>= sDescriptorName )
282                 &&  ( sDescriptorName == _rColName ),
283                 "OHSQLTable::alterColumnType: unexpected column name!" );
284     }
285     catch( const Exception& )
286     {
287     	DBG_UNHANDLED_EXCEPTION();
288     }
289 #else
290     (void)_rColName;
291 #endif
292 
293     OHSQLColumn* pColumn = new OHSQLColumn(sal_True);
294 	Reference<XPropertySet> xProp = pColumn;
295 	::comphelper::copyProperties(_xDescriptor,xProp);
296 	xProp->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE),makeAny(nNewType));
297 
298 	sSql += ::dbtools::createStandardColumnPart(xProp,getConnection());
299 	executeStatement(sSql);
300 }
301 // -----------------------------------------------------------------------------
302 void OHSQLTable::alterDefaultValue(const ::rtl::OUString& _sNewDefault,const ::rtl::OUString& _rColName)
303 {
304 	::rtl::OUString sSql = getAlterTableColumnPart();
305 	sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" ALTER COLUMN "));
306 
307 	const ::rtl::OUString sQuote = getMetaData()->getIdentifierQuoteString(  );
308 	sSql += ::dbtools::quoteName(sQuote,_rColName);
309 	sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" SET DEFAULT '")) + _sNewDefault;
310 	sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("'"));
311 
312 	executeStatement(sSql);
313 }
314 // -----------------------------------------------------------------------------
315 void OHSQLTable::dropDefaultValue(const ::rtl::OUString& _rColName)
316 {
317 	::rtl::OUString sSql = getAlterTableColumnPart();
318 	sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" ALTER COLUMN "));
319 
320 	const ::rtl::OUString sQuote = getMetaData()->getIdentifierQuoteString(  );
321 	sSql += ::dbtools::quoteName(sQuote,_rColName);
322 	sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" DROP DEFAULT"));
323 
324 	executeStatement(sSql);
325 }
326 // -----------------------------------------------------------------------------
327 ::rtl::OUString OHSQLTable::getAlterTableColumnPart()
328 {
329 	::rtl::OUString sSql = ::rtl::OUString::createFromAscii("ALTER TABLE ");
330 	const ::rtl::OUString sQuote = getMetaData()->getIdentifierQuoteString(  );
331 
332 	::rtl::OUString sComposedName( ::dbtools::composeTableName( getMetaData(), m_CatalogName, m_SchemaName, m_Name, sal_True, ::dbtools::eInTableDefinitions ) );
333 	sSql += sComposedName;
334 
335 	return sSql;
336 }
337 // -----------------------------------------------------------------------------
338 void OHSQLTable::executeStatement(const ::rtl::OUString& _rStatement )
339 {
340 	::rtl::OUString sSQL = _rStatement;
341 	if(sSQL.lastIndexOf(',') == (sSQL.getLength()-1))
342 		sSQL = sSQL.replaceAt(sSQL.getLength()-1,1,::rtl::OUString::createFromAscii(")"));
343 
344 	Reference< XStatement > xStmt = getConnection()->createStatement(  );
345 	if ( xStmt.is() )
346 	{
347         try { xStmt->execute(sSQL); }
348         catch( const Exception& )
349         {
350 		    ::comphelper::disposeComponent(xStmt);
351             throw;
352         }
353 		::comphelper::disposeComponent(xStmt);
354 	}
355 }
356 // -----------------------------------------------------------------------------
357 Sequence< Type > SAL_CALL OHSQLTable::getTypes(  ) throw(RuntimeException)
358 {
359 	if ( ! m_Type.compareToAscii("VIEW") )
360 	{
361 		Sequence< Type > aTypes = OTableHelper::getTypes();
362 		::std::vector<Type> aOwnTypes;
363 		aOwnTypes.reserve(aTypes.getLength());
364 		const Type* pIter = aTypes.getConstArray();
365 		const Type* pEnd = pIter + aTypes.getLength();
366 		for(;pIter != pEnd;++pIter)
367 		{
368 			if( *pIter != ::getCppuType((const Reference<XRename>*)0) )
369 			{
370 				aOwnTypes.push_back(*pIter);
371 			}
372 		}
373 		Type *pTypes = aOwnTypes.empty() ? 0 : &aOwnTypes[0];
374 		return Sequence< Type >(pTypes, aOwnTypes.size());
375 	}
376 	return OTableHelper::getTypes();
377 }
378 // -------------------------------------------------------------------------
379 // XRename
380 void SAL_CALL OHSQLTable::rename( const ::rtl::OUString& newName ) throw(SQLException, ElementExistException, RuntimeException)
381 {
382 	::osl::MutexGuard aGuard(m_aMutex);
383 	checkDisposed(
384 #ifdef GCC
385 		::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
386 #else
387 		rBHelper.bDisposed
388 #endif
389 		);
390 
391 	if(!isNew())
392 	{
393 		::rtl::OUString sSql = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ALTER "));
394 		if ( m_Type == ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("VIEW")) )
395 			sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" VIEW "));
396 		else
397 			sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" TABLE "));
398 
399 		::rtl::OUString sQuote = getMetaData()->getIdentifierQuoteString(  );
400 
401 		::rtl::OUString sCatalog,sSchema,sTable;
402 		::dbtools::qualifiedNameComponents(getMetaData(),newName,sCatalog,sSchema,sTable,::dbtools::eInDataManipulation);
403 
404 		::rtl::OUString sComposedName(
405             ::dbtools::composeTableName( getMetaData(), m_CatalogName, m_SchemaName, m_Name, sal_True, ::dbtools::eInDataManipulation ) );
406 		sSql += sComposedName
407             + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" RENAME TO "));
408 		sSql += ::dbtools::composeTableName( getMetaData(), sCatalog, sSchema, sTable, sal_True, ::dbtools::eInDataManipulation );
409 
410         executeStatement(sSql);
411 
412 		::connectivity::OTable_TYPEDEF::rename(newName);
413 	}
414 	else
415 		::dbtools::qualifiedNameComponents(getMetaData(),newName,m_CatalogName,m_SchemaName,m_Name,::dbtools::eInTableDefinitions);
416 }
417 
418 // -------------------------------------------------------------------------
419 Any SAL_CALL OHSQLTable::queryInterface( const Type & rType ) throw(RuntimeException)
420 {
421 	if( !m_Type.compareToAscii("VIEW") && rType == ::getCppuType((const Reference<XRename>*)0) )
422 		return Any();
423 
424 	return OTableHelper::queryInterface(rType);
425 }
426 // -------------------------------------------------------------------------
427 
428