1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "objectnames.hxx"
22 #include "module_sdbt.hxx"
23 #include "sdbt_resource.hrc"
25 #include <com/sun/star/lang/NullPointerException.hpp>
26 #include <com/sun/star/sdb/CommandType.hpp>
27 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
28 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
29 #include <com/sun/star/sdb/ErrorCondition.hpp>
31 #include <connectivity/dbmetadata.hxx>
32 #include <connectivity/dbtools.hxx>
33 #include <connectivity/sqlerror.hxx>
34 #include <cppuhelper/exc_hlp.hxx>
35 #include <rtl/ustrbuf.hxx>
37 #include <boost/noncopyable.hpp>
38 #include <boost/shared_ptr.hpp>
43 using ::com::sun::star::uno::Reference
;
44 using ::com::sun::star::sdbc::XConnection
;
45 using ::com::sun::star::lang::NullPointerException
;
46 using ::com::sun::star::lang::IllegalArgumentException
;
47 using ::com::sun::star::uno::RuntimeException
;
48 using ::com::sun::star::sdbc::SQLException
;
49 using ::com::sun::star::sdbc::XDatabaseMetaData
;
50 using ::com::sun::star::uno::XInterface
;
51 using ::com::sun::star::container::XNameAccess
;
52 using ::com::sun::star::uno::UNO_QUERY_THROW
;
53 using ::com::sun::star::sdbcx::XTablesSupplier
;
54 using ::com::sun::star::sdb::XQueriesSupplier
;
55 using ::com::sun::star::uno::Exception
;
56 using ::com::sun::star::uno::makeAny
;
57 using ::com::sun::star::uno::Any
;
58 using ::com::sun::star::uno::XComponentContext
;
60 namespace CommandType
= ::com::sun::star::sdb::CommandType
;
61 namespace ErrorCondition
= ::com::sun::star::sdb::ErrorCondition
;
67 virtual bool validateName( const OUString
& _rName
) = 0;
68 virtual void validateName_throw( const OUString
& _rName
) = 0;
70 virtual ~INameValidation() { }
72 typedef ::boost::shared_ptr
< INameValidation
> PNameValidation
;
74 // PlainExistenceCheck
75 class PlainExistenceCheck
: public INameValidation
78 const Reference
<XComponentContext
> m_aContext
;
79 Reference
< XConnection
> m_xConnection
;
80 Reference
< XNameAccess
> m_xContainer
;
83 PlainExistenceCheck( const Reference
<XComponentContext
>& _rContext
, const Reference
< XConnection
>& _rxConnection
, const Reference
< XNameAccess
>& _rxContainer
)
84 :m_aContext( _rContext
)
85 ,m_xConnection( _rxConnection
)
86 ,m_xContainer( _rxContainer
)
88 OSL_ENSURE( m_xContainer
.is(), "PlainExistenceCheck::PlainExistenceCheck: this will crash!" );
92 virtual bool validateName( const OUString
& _rName
) SAL_OVERRIDE
94 return !m_xContainer
->hasByName( _rName
);
97 virtual void validateName_throw( const OUString
& _rName
) SAL_OVERRIDE
99 if ( validateName( _rName
) )
102 ::connectivity::SQLError
aErrors( m_aContext
);
103 SQLException
aError( aErrors
.getSQLException( ErrorCondition::DB_OBJECT_NAME_IS_USED
, m_xConnection
, _rName
) );
105 ::dbtools::DatabaseMetaData
aMeta( m_xConnection
);
106 if ( aMeta
.supportsSubqueriesInFrom() )
108 OUString
sNeedDistinctNames( SdbtRes( STR_QUERY_AND_TABLE_DISTINCT_NAMES
) );
109 aError
.NextException
<<= SQLException( sNeedDistinctNames
, m_xConnection
, OUString(), 0, Any() );
116 // TableValidityCheck
117 class TableValidityCheck
: public INameValidation
119 const Reference
<XComponentContext
> m_aContext
;
120 const Reference
< XConnection
> m_xConnection
;
123 TableValidityCheck( const Reference
<XComponentContext
>& _rContext
, const Reference
< XConnection
>& _rxConnection
)
124 :m_aContext( _rContext
)
125 ,m_xConnection( _rxConnection
)
129 virtual bool validateName( const OUString
& _rName
) SAL_OVERRIDE
131 ::dbtools::DatabaseMetaData
aMeta( m_xConnection
);
132 if ( !aMeta
.restrictIdentifiersToSQL92() )
135 OUString sCatalog
, sSchema
, sName
;
136 ::dbtools::qualifiedNameComponents(
137 m_xConnection
->getMetaData(), _rName
, sCatalog
, sSchema
, sName
, ::dbtools::eInTableDefinitions
);
139 OUString
sExtraNameCharacters( m_xConnection
->getMetaData()->getExtraNameCharacters() );
140 if ( ( !sCatalog
.isEmpty() && !::dbtools::isValidSQLName( sCatalog
, sExtraNameCharacters
) )
141 || ( !sSchema
.isEmpty() && !::dbtools::isValidSQLName( sSchema
, sExtraNameCharacters
) )
142 || ( !sName
.isEmpty() && !::dbtools::isValidSQLName( sName
, sExtraNameCharacters
) )
149 virtual void validateName_throw( const OUString
& _rName
) SAL_OVERRIDE
151 if ( validateName( _rName
) )
154 ::connectivity::SQLError
aErrors( m_aContext
);
155 aErrors
.raiseException( ErrorCondition::DB_INVALID_SQL_NAME
, m_xConnection
, _rName
);
159 // QueryValidityCheck
160 class QueryValidityCheck
: public INameValidation
162 const Reference
<XComponentContext
> m_aContext
;
163 const Reference
< XConnection
> m_xConnection
;
166 QueryValidityCheck( const Reference
<XComponentContext
>& _rContext
, const Reference
< XConnection
>& _rxConnection
)
167 :m_aContext( _rContext
)
168 ,m_xConnection( _rxConnection
)
172 static inline ::connectivity::ErrorCondition
validateName_getErrorCondition( const OUString
& _rName
)
174 if ( ( _rName
.indexOf( (sal_Unicode
)34 ) >= 0 ) // "
175 || ( _rName
.indexOf( (sal_Unicode
)39 ) >= 0 ) // '
176 || ( _rName
.indexOf( (sal_Unicode
)96 ) >= 0 )
177 || ( _rName
.indexOf( (sal_Unicode
)145 ) >= 0 )
178 || ( _rName
.indexOf( (sal_Unicode
)146 ) >= 0 )
179 || ( _rName
.indexOf( (sal_Unicode
)180 ) >= 0 ) // removed unparsable chars
181 return ErrorCondition::DB_QUERY_NAME_WITH_QUOTES
;
183 if ( _rName
.indexOf( '/') >= 0 )
184 return ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES
;
189 virtual bool validateName( const OUString
& _rName
) SAL_OVERRIDE
191 if ( validateName_getErrorCondition( _rName
) != 0 )
196 virtual void validateName_throw( const OUString
& _rName
) SAL_OVERRIDE
198 ::connectivity::ErrorCondition nErrorCondition
= validateName_getErrorCondition( _rName
);
199 if ( nErrorCondition
!= 0 )
201 ::connectivity::SQLError
aErrors( m_aContext
);
202 aErrors
.raiseException( nErrorCondition
, m_xConnection
);
208 class CombinedNameCheck
: public INameValidation
211 PNameValidation m_pPrimary
;
212 PNameValidation m_pSecondary
;
215 CombinedNameCheck( PNameValidation _pPrimary
, PNameValidation _pSecondary
)
216 :m_pPrimary( _pPrimary
)
217 ,m_pSecondary( _pSecondary
)
219 OSL_ENSURE( m_pPrimary
.get() && m_pSecondary
.get(), "CombinedNameCheck::CombinedNameCheck: this will crash!" );
223 virtual bool validateName( const OUString
& _rName
) SAL_OVERRIDE
225 return m_pPrimary
->validateName( _rName
) && m_pSecondary
->validateName( _rName
);
228 virtual void validateName_throw( const OUString
& _rName
) SAL_OVERRIDE
230 m_pPrimary
->validateName_throw( _rName
);
231 m_pSecondary
->validateName_throw( _rName
);
236 class NameCheckFactory
: private boost::noncopyable
239 /** creates an INameValidation instance which can be used to check the existence of query or table names
242 the component's context
245 the type of objects (CommandType::TABLE or CommandType::QUERY) of which names shall be checked for existence
248 the connection relative to which the names are to be checked. Must be an SDB-level connection
250 @throws IllegalArgumentException
251 if the given connection is no SDB-level connection
253 @throws IllegalArgumentException
254 if the given command type is neither CommandType::TABLE or CommandType::QUERY
256 static PNameValidation
createExistenceCheck(
257 const Reference
<XComponentContext
>& _rContext
,
258 sal_Int32 _nCommandType
,
259 const Reference
< XConnection
>& _rxConnection
262 /** creates an INameValidation instance which can be used to check the validity of a query or table name
265 the component's context
268 the type of objects (CommandType::TABLE or CommandType::QUERY) of which names shall be validated
271 the connection relative to which the names are to be checked. Must be an SDB-level connection
273 @throws IllegalArgumentException
274 if the given connection is no SDB-level connection
276 @throws IllegalArgumentException
277 if the given command type is neither CommandType::TABLE or CommandType::QUERY
279 static PNameValidation
createValidityCheck(
280 const Reference
<XComponentContext
>& _rContext
,
281 const sal_Int32 _nCommandType
,
282 const Reference
< XConnection
>& _rxConnection
286 static void verifyCommandType( sal_Int32 _nCommandType
);
289 void NameCheckFactory::verifyCommandType( sal_Int32 _nCommandType
)
291 if ( ( _nCommandType
!= CommandType::TABLE
)
292 && ( _nCommandType
!= CommandType::QUERY
)
294 throw IllegalArgumentException(
295 OUString( SdbtRes( STR_INVALID_COMMAND_TYPE
) ),
301 PNameValidation
NameCheckFactory::createExistenceCheck( const Reference
<XComponentContext
>& _rContext
, sal_Int32 _nCommandType
, const Reference
< XConnection
>& _rxConnection
)
303 verifyCommandType( _nCommandType
);
305 ::dbtools::DatabaseMetaData
aMeta( _rxConnection
);
307 Reference
< XNameAccess
> xTables
, xQueries
;
310 Reference
< XTablesSupplier
> xSuppTables( _rxConnection
, UNO_QUERY_THROW
);
311 Reference
< XQueriesSupplier
> xQueriesSupplier( _rxConnection
, UNO_QUERY_THROW
);
312 xTables
.set( xSuppTables
->getTables(), UNO_QUERY_THROW
);
313 xQueries
.set( xQueriesSupplier
->getQueries(), UNO_QUERY_THROW
);
315 catch( const Exception
& )
317 throw IllegalArgumentException(
318 OUString( SdbtRes( STR_CONN_WITHOUT_QUERIES_OR_TABLES
) ),
324 PNameValidation
pTableCheck( new PlainExistenceCheck( _rContext
, _rxConnection
, xTables
) );
325 PNameValidation
pQueryCheck( new PlainExistenceCheck( _rContext
, _rxConnection
, xQueries
) );
326 PNameValidation pReturn
;
328 if ( aMeta
.supportsSubqueriesInFrom() )
329 pReturn
.reset( new CombinedNameCheck( pTableCheck
, pQueryCheck
) );
330 else if ( _nCommandType
== CommandType::TABLE
)
331 pReturn
= pTableCheck
;
333 pReturn
= pQueryCheck
;
337 PNameValidation
NameCheckFactory::createValidityCheck( const Reference
<XComponentContext
>& _rContext
, sal_Int32 _nCommandType
, const Reference
< XConnection
>& _rxConnection
)
339 verifyCommandType( _nCommandType
);
341 Reference
< XDatabaseMetaData
> xMeta
;
344 xMeta
.set( _rxConnection
->getMetaData(), UNO_QUERY_THROW
);
346 catch( const Exception
& )
348 throw IllegalArgumentException(
349 "The connection could not provide its database's meta data.",
355 if ( _nCommandType
== CommandType::TABLE
)
356 return PNameValidation( new TableValidityCheck( _rContext
, _rxConnection
) );
357 return PNameValidation( new QueryValidityCheck( _rContext
, _rxConnection
) );
361 struct ObjectNames_Impl
363 SdbtClient m_aModuleClient
; // keep the module alive as long as this instance lives
367 ObjectNames::ObjectNames( const Reference
<XComponentContext
>& _rContext
, const Reference
< XConnection
>& _rxConnection
)
368 :ConnectionDependentComponent( _rContext
)
369 ,m_pImpl( new ObjectNames_Impl
)
371 setWeakConnection( _rxConnection
);
374 ObjectNames::~ObjectNames()
378 OUString SAL_CALL
ObjectNames::suggestName( ::sal_Int32 _CommandType
, const OUString
& _BaseName
) throw (IllegalArgumentException
, SQLException
, RuntimeException
, std::exception
)
380 EntryGuard
aGuard( *this );
382 PNameValidation
pNameCheck( NameCheckFactory::createExistenceCheck( getContext(), _CommandType
, getConnection() ) );
384 OUString
sBaseName( _BaseName
);
385 if ( sBaseName
.isEmpty() )
387 if ( _CommandType
== CommandType::TABLE
)
388 sBaseName
= OUString( SdbtRes( STR_BASENAME_TABLE
) );
390 sBaseName
= OUString( SdbtRes( STR_BASENAME_QUERY
) );
392 else if( _CommandType
== CommandType::QUERY
)
394 sBaseName
=sBaseName
.replace('/', '_');
397 OUString
sName( sBaseName
);
399 while ( !pNameCheck
->validateName( sName
) )
401 sName
= sBaseName
+ " " + OUString::number(++i
);
407 OUString SAL_CALL
ObjectNames::convertToSQLName( const OUString
& Name
) throw (RuntimeException
, std::exception
)
409 EntryGuard
aGuard( *this );
410 Reference
< XDatabaseMetaData
> xMeta( getConnection()->getMetaData(), UNO_QUERY_THROW
);
411 return ::dbtools::convertName2SQLName( Name
, xMeta
->getExtraNameCharacters() );
414 sal_Bool SAL_CALL
ObjectNames::isNameUsed( ::sal_Int32 _CommandType
, const OUString
& _Name
) throw (IllegalArgumentException
, SQLException
, RuntimeException
, std::exception
)
416 EntryGuard
aGuard( *this );
418 PNameValidation
pNameCheck( NameCheckFactory::createExistenceCheck( getContext(), _CommandType
, getConnection()) );
419 return !pNameCheck
->validateName( _Name
);
422 sal_Bool SAL_CALL
ObjectNames::isNameValid( ::sal_Int32 _CommandType
, const OUString
& _Name
) throw (IllegalArgumentException
, RuntimeException
, std::exception
)
424 EntryGuard
aGuard( *this );
426 PNameValidation
pNameCheck( NameCheckFactory::createValidityCheck( getContext(), _CommandType
, getConnection()) );
427 return pNameCheck
->validateName( _Name
);
430 void SAL_CALL
ObjectNames::checkNameForCreate( ::sal_Int32 _CommandType
, const OUString
& _Name
) throw (SQLException
, RuntimeException
, std::exception
)
432 EntryGuard
aGuard( *this );
434 PNameValidation
pNameCheck( NameCheckFactory::createExistenceCheck( getContext(), _CommandType
, getConnection() ) );
435 pNameCheck
->validateName_throw( _Name
);
437 pNameCheck
= NameCheckFactory::createValidityCheck( getContext(), _CommandType
, getConnection() );
438 pNameCheck
->validateName_throw( _Name
);
441 } // namespace sdbtools
443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */