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 "objectnames.hxx"
21 :
22 : #include "module_sdbt.hxx"
23 : #include "sdbt_resource.hrc"
24 :
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>
30 :
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>
36 :
37 : #include <boost/shared_ptr.hpp>
38 :
39 : namespace sdbtools
40 : {
41 :
42 : using ::com::sun::star::uno::Reference;
43 : using ::com::sun::star::sdbc::XConnection;
44 : using ::com::sun::star::lang::NullPointerException;
45 : using ::com::sun::star::lang::IllegalArgumentException;
46 : using ::com::sun::star::uno::RuntimeException;
47 : using ::com::sun::star::sdbc::SQLException;
48 : using ::com::sun::star::sdbc::XDatabaseMetaData;
49 : using ::com::sun::star::uno::XInterface;
50 : using ::com::sun::star::container::XNameAccess;
51 : using ::com::sun::star::uno::UNO_QUERY_THROW;
52 : using ::com::sun::star::sdbcx::XTablesSupplier;
53 : using ::com::sun::star::sdb::XQueriesSupplier;
54 : using ::com::sun::star::uno::Exception;
55 : using ::com::sun::star::uno::makeAny;
56 : using ::com::sun::star::uno::Any;
57 : using ::com::sun::star::uno::XComponentContext;
58 :
59 : namespace CommandType = ::com::sun::star::sdb::CommandType;
60 : namespace ErrorCondition = ::com::sun::star::sdb::ErrorCondition;
61 :
62 : // INameValidation
63 0 : class INameValidation
64 : {
65 : public:
66 : virtual bool validateName( const OUString& _rName ) = 0;
67 : virtual void validateName_throw( const OUString& _rName ) = 0;
68 :
69 0 : virtual ~INameValidation() { }
70 : };
71 : typedef ::boost::shared_ptr< INameValidation > PNameValidation;
72 :
73 : // PlainExistenceCheck
74 0 : class PlainExistenceCheck : public INameValidation
75 : {
76 : private:
77 : const Reference<XComponentContext> m_aContext;
78 : Reference< XConnection > m_xConnection;
79 : Reference< XNameAccess > m_xContainer;
80 :
81 : public:
82 0 : PlainExistenceCheck( const Reference<XComponentContext>& _rContext, const Reference< XConnection >& _rxConnection, const Reference< XNameAccess >& _rxContainer )
83 : :m_aContext( _rContext )
84 : ,m_xConnection( _rxConnection )
85 0 : ,m_xContainer( _rxContainer )
86 : {
87 : OSL_ENSURE( m_xContainer.is(), "PlainExistenceCheck::PlainExistenceCheck: this will crash!" );
88 0 : }
89 :
90 : // INameValidation
91 0 : virtual bool validateName( const OUString& _rName ) SAL_OVERRIDE
92 : {
93 0 : return !m_xContainer->hasByName( _rName );
94 : }
95 :
96 0 : virtual void validateName_throw( const OUString& _rName ) SAL_OVERRIDE
97 : {
98 0 : if ( validateName( _rName ) )
99 0 : return;
100 :
101 0 : ::connectivity::SQLError aErrors( m_aContext );
102 0 : SQLException aError( aErrors.getSQLException( ErrorCondition::DB_OBJECT_NAME_IS_USED, m_xConnection, _rName ) );
103 :
104 0 : ::dbtools::DatabaseMetaData aMeta( m_xConnection );
105 0 : if ( aMeta.supportsSubqueriesInFrom() )
106 : {
107 0 : OUString sNeedDistinctNames( SdbtRes( STR_QUERY_AND_TABLE_DISTINCT_NAMES ) );
108 0 : aError.NextException <<= SQLException( sNeedDistinctNames, m_xConnection, OUString(), 0, Any() );
109 : }
110 :
111 0 : throw aError;
112 : }
113 : };
114 :
115 : // TableValidityCheck
116 0 : class TableValidityCheck : public INameValidation
117 : {
118 : const Reference<XComponentContext> m_aContext;
119 : const Reference< XConnection > m_xConnection;
120 :
121 : public:
122 0 : TableValidityCheck( const Reference<XComponentContext>& _rContext, const Reference< XConnection >& _rxConnection )
123 : :m_aContext( _rContext )
124 0 : ,m_xConnection( _rxConnection )
125 : {
126 0 : }
127 :
128 0 : virtual bool validateName( const OUString& _rName ) SAL_OVERRIDE
129 : {
130 0 : ::dbtools::DatabaseMetaData aMeta( m_xConnection );
131 0 : if ( !aMeta.restrictIdentifiersToSQL92() )
132 0 : return true;
133 :
134 0 : OUString sCatalog, sSchema, sName;
135 : ::dbtools::qualifiedNameComponents(
136 0 : m_xConnection->getMetaData(), _rName, sCatalog, sSchema, sName, ::dbtools::eInTableDefinitions );
137 :
138 0 : OUString sExtraNameCharacters( m_xConnection->getMetaData()->getExtraNameCharacters() );
139 0 : if ( ( !sCatalog.isEmpty() && !::dbtools::isValidSQLName( sCatalog, sExtraNameCharacters ) )
140 0 : || ( !sSchema.isEmpty() && !::dbtools::isValidSQLName( sSchema, sExtraNameCharacters ) )
141 0 : || ( !sName.isEmpty() && !::dbtools::isValidSQLName( sName, sExtraNameCharacters ) )
142 : )
143 0 : return false;
144 :
145 0 : return true;
146 : }
147 :
148 0 : virtual void validateName_throw( const OUString& _rName ) SAL_OVERRIDE
149 : {
150 0 : if ( validateName( _rName ) )
151 0 : return;
152 :
153 0 : ::connectivity::SQLError aErrors( m_aContext );
154 0 : aErrors.raiseException( ErrorCondition::DB_INVALID_SQL_NAME, m_xConnection, _rName );
155 : }
156 : };
157 :
158 : // QueryValidityCheck
159 0 : class QueryValidityCheck : public INameValidation
160 : {
161 : const Reference<XComponentContext> m_aContext;
162 : const Reference< XConnection > m_xConnection;
163 :
164 : public:
165 0 : QueryValidityCheck( const Reference<XComponentContext>& _rContext, const Reference< XConnection >& _rxConnection )
166 : :m_aContext( _rContext )
167 0 : ,m_xConnection( _rxConnection )
168 : {
169 0 : }
170 :
171 0 : inline ::connectivity::ErrorCondition validateName_getErrorCondition( const OUString& _rName )
172 : {
173 0 : if ( ( _rName.indexOf( (sal_Unicode)34 ) >= 0 ) // "
174 0 : || ( _rName.indexOf( (sal_Unicode)39 ) >= 0 ) // '
175 0 : || ( _rName.indexOf( (sal_Unicode)96 ) >= 0 )
176 0 : || ( _rName.indexOf( (sal_Unicode)145 ) >= 0 )
177 0 : || ( _rName.indexOf( (sal_Unicode)146 ) >= 0 )
178 0 : || ( _rName.indexOf( (sal_Unicode)180 ) >= 0 ) // removed unparsable chars
179 : )
180 0 : return ErrorCondition::DB_QUERY_NAME_WITH_QUOTES;
181 :
182 0 : if ( _rName.indexOf( '/') >= 0 )
183 0 : return ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES;
184 :
185 0 : return 0;
186 : }
187 :
188 0 : virtual bool validateName( const OUString& _rName ) SAL_OVERRIDE
189 : {
190 0 : if ( validateName_getErrorCondition( _rName ) != 0 )
191 0 : return false;
192 0 : return true;
193 : }
194 :
195 0 : virtual void validateName_throw( const OUString& _rName ) SAL_OVERRIDE
196 : {
197 0 : ::connectivity::ErrorCondition nErrorCondition = validateName_getErrorCondition( _rName );
198 0 : if ( nErrorCondition != 0 )
199 : {
200 0 : ::connectivity::SQLError aErrors( m_aContext );
201 0 : aErrors.raiseException( nErrorCondition, m_xConnection );
202 : }
203 0 : }
204 : };
205 :
206 : // CombinedNameCheck
207 0 : class CombinedNameCheck : public INameValidation
208 : {
209 : private:
210 : PNameValidation m_pPrimary;
211 : PNameValidation m_pSecondary;
212 :
213 : public:
214 0 : CombinedNameCheck( PNameValidation _pPrimary, PNameValidation _pSecondary )
215 : :m_pPrimary( _pPrimary )
216 0 : ,m_pSecondary( _pSecondary )
217 : {
218 : OSL_ENSURE( m_pPrimary.get() && m_pSecondary.get(), "CombinedNameCheck::CombinedNameCheck: this will crash!" );
219 0 : }
220 :
221 : // INameValidation
222 0 : virtual bool validateName( const OUString& _rName ) SAL_OVERRIDE
223 : {
224 0 : return m_pPrimary->validateName( _rName ) && m_pSecondary->validateName( _rName );
225 : }
226 :
227 0 : virtual void validateName_throw( const OUString& _rName ) SAL_OVERRIDE
228 : {
229 0 : m_pPrimary->validateName_throw( _rName );
230 0 : m_pSecondary->validateName_throw( _rName );
231 0 : }
232 : };
233 :
234 : // NameCheckFactory
235 : class NameCheckFactory
236 : {
237 : public:
238 : /** creates an INameValidation instance which can be used to check the existence of query or table names
239 :
240 : @param _rContext
241 : the component's context
242 :
243 : @param _nCommandType
244 : the type of objects (CommandType::TABLE or CommandType::QUERY) of which names shall be checked for existence
245 :
246 : @param _rxConnection
247 : the connection relative to which the names are to be checked. Must be an SDB-level connection
248 :
249 : @throws IllegalArgumentException
250 : if the given connection is no SDB-level connection
251 :
252 : @throws IllegalArgumentException
253 : if the given command type is neither CommandType::TABLE or CommandType::QUERY
254 : */
255 : static PNameValidation createExistenceCheck(
256 : const Reference<XComponentContext>& _rContext,
257 : sal_Int32 _nCommandType,
258 : const Reference< XConnection >& _rxConnection
259 : );
260 :
261 : /** creates an INameValidation instance which can be used to check the validity of a query or table name
262 :
263 : @param _rContext
264 : the component's context
265 :
266 : @param _nCommandType
267 : the type of objects (CommandType::TABLE or CommandType::QUERY) of which names shall be validated
268 :
269 : @param _rxConnection
270 : the connection relative to which the names are to be checked. Must be an SDB-level connection
271 :
272 : @throws IllegalArgumentException
273 : if the given connection is no SDB-level connection
274 :
275 : @throws IllegalArgumentException
276 : if the given command type is neither CommandType::TABLE or CommandType::QUERY
277 : */
278 : static PNameValidation createValidityCheck(
279 : const Reference<XComponentContext>& _rContext,
280 : const sal_Int32 _nCommandType,
281 : const Reference< XConnection >& _rxConnection
282 : );
283 :
284 : private:
285 : NameCheckFactory(); // never implemented
286 :
287 : private:
288 : static void verifyCommandType( sal_Int32 _nCommandType );
289 : };
290 :
291 0 : void NameCheckFactory::verifyCommandType( sal_Int32 _nCommandType )
292 : {
293 0 : if ( ( _nCommandType != CommandType::TABLE )
294 0 : && ( _nCommandType != CommandType::QUERY )
295 : )
296 : throw IllegalArgumentException(
297 : OUString( SdbtRes( STR_INVALID_COMMAND_TYPE ) ),
298 : NULL,
299 : 0
300 0 : );
301 0 : }
302 :
303 0 : PNameValidation NameCheckFactory::createExistenceCheck( const Reference<XComponentContext>& _rContext, sal_Int32 _nCommandType, const Reference< XConnection >& _rxConnection )
304 : {
305 0 : verifyCommandType( _nCommandType );
306 :
307 0 : ::dbtools::DatabaseMetaData aMeta( _rxConnection );
308 :
309 0 : Reference< XNameAccess > xTables, xQueries;
310 : try
311 : {
312 0 : Reference< XTablesSupplier > xSuppTables( _rxConnection, UNO_QUERY_THROW );
313 0 : Reference< XQueriesSupplier > xQueriesSupplier( _rxConnection, UNO_QUERY_THROW );
314 0 : xTables.set( xSuppTables->getTables(), UNO_QUERY_THROW );
315 0 : xQueries.set( xQueriesSupplier->getQueries(), UNO_QUERY_THROW );
316 : }
317 0 : catch( const Exception& )
318 : {
319 : throw IllegalArgumentException(
320 : OUString( SdbtRes( STR_CONN_WITHOUT_QUERIES_OR_TABLES ) ),
321 : NULL,
322 : 0
323 0 : );
324 : }
325 :
326 0 : PNameValidation pTableCheck( new PlainExistenceCheck( _rContext, _rxConnection, xTables ) );
327 0 : PNameValidation pQueryCheck( new PlainExistenceCheck( _rContext, _rxConnection, xQueries ) );
328 0 : PNameValidation pReturn;
329 :
330 0 : if ( aMeta.supportsSubqueriesInFrom() )
331 0 : pReturn.reset( new CombinedNameCheck( pTableCheck, pQueryCheck ) );
332 0 : else if ( _nCommandType == CommandType::TABLE )
333 0 : pReturn = pTableCheck;
334 : else
335 0 : pReturn = pQueryCheck;
336 0 : return pReturn;
337 : }
338 :
339 0 : PNameValidation NameCheckFactory::createValidityCheck( const Reference<XComponentContext>& _rContext, sal_Int32 _nCommandType, const Reference< XConnection >& _rxConnection )
340 : {
341 0 : verifyCommandType( _nCommandType );
342 :
343 0 : Reference< XDatabaseMetaData > xMeta;
344 : try
345 : {
346 0 : xMeta.set( _rxConnection->getMetaData(), UNO_QUERY_THROW );
347 : }
348 0 : catch( const Exception& )
349 : {
350 : throw IllegalArgumentException(
351 : OUString( "The connection could not provide its database's meta data." ),
352 : NULL,
353 : 0
354 0 : );
355 : }
356 :
357 0 : if ( _nCommandType == CommandType::TABLE )
358 0 : return PNameValidation( new TableValidityCheck( _rContext, _rxConnection ) );
359 0 : return PNameValidation( new QueryValidityCheck( _rContext, _rxConnection ) );
360 : }
361 :
362 : // ObjectNames_Impl
363 0 : struct ObjectNames_Impl
364 : {
365 : SdbtClient m_aModuleClient; // keep the module alive as long as this instance lives
366 : };
367 :
368 : // ObjectNames
369 0 : ObjectNames::ObjectNames( const Reference<XComponentContext>& _rContext, const Reference< XConnection >& _rxConnection )
370 : :ConnectionDependentComponent( _rContext )
371 0 : ,m_pImpl( new ObjectNames_Impl )
372 : {
373 0 : if ( !_rxConnection.is() )
374 0 : throw NullPointerException();
375 0 : setWeakConnection( _rxConnection );
376 0 : }
377 :
378 0 : ObjectNames::~ObjectNames()
379 : {
380 0 : }
381 :
382 0 : OUString SAL_CALL ObjectNames::suggestName( ::sal_Int32 _CommandType, const OUString& _BaseName ) throw (IllegalArgumentException, RuntimeException, std::exception)
383 : {
384 0 : EntryGuard aGuard( *this );
385 :
386 0 : PNameValidation pNameCheck( NameCheckFactory::createExistenceCheck( getContext(), _CommandType, getConnection() ) );
387 :
388 0 : OUString sBaseName( _BaseName );
389 0 : if ( sBaseName.isEmpty() )
390 : {
391 0 : if ( _CommandType == CommandType::TABLE )
392 0 : sBaseName = OUString( SdbtRes( STR_BASENAME_TABLE ) );
393 : else
394 0 : sBaseName = OUString( SdbtRes( STR_BASENAME_QUERY ) );
395 : }
396 0 : else if( _CommandType == CommandType::QUERY )
397 : {
398 0 : sBaseName=sBaseName.replace('/', '_');
399 : }
400 :
401 0 : OUString sName( sBaseName );
402 0 : sal_Int32 i = 1;
403 0 : while ( !pNameCheck->validateName( sName ) )
404 : {
405 0 : sName = sBaseName + " " + OUString::number(++i);
406 : }
407 :
408 0 : return sName;
409 : }
410 :
411 0 : OUString SAL_CALL ObjectNames::convertToSQLName( const OUString& Name ) throw (RuntimeException, std::exception)
412 : {
413 0 : EntryGuard aGuard( *this );
414 0 : Reference< XDatabaseMetaData > xMeta( getConnection()->getMetaData(), UNO_QUERY_THROW );
415 0 : return ::dbtools::convertName2SQLName( Name, xMeta->getExtraNameCharacters() );
416 : }
417 :
418 0 : sal_Bool SAL_CALL ObjectNames::isNameUsed( ::sal_Int32 _CommandType, const OUString& _Name ) throw (IllegalArgumentException, RuntimeException, std::exception)
419 : {
420 0 : EntryGuard aGuard( *this );
421 :
422 0 : PNameValidation pNameCheck( NameCheckFactory::createExistenceCheck( getContext(), _CommandType, getConnection()) );
423 0 : return !pNameCheck->validateName( _Name );
424 : }
425 :
426 0 : sal_Bool SAL_CALL ObjectNames::isNameValid( ::sal_Int32 _CommandType, const OUString& _Name ) throw (IllegalArgumentException, RuntimeException, std::exception)
427 : {
428 0 : EntryGuard aGuard( *this );
429 :
430 0 : PNameValidation pNameCheck( NameCheckFactory::createValidityCheck( getContext(), _CommandType, getConnection()) );
431 0 : return pNameCheck->validateName( _Name );
432 : }
433 :
434 0 : void SAL_CALL ObjectNames::checkNameForCreate( ::sal_Int32 _CommandType, const OUString& _Name ) throw (SQLException, RuntimeException, std::exception)
435 : {
436 0 : EntryGuard aGuard( *this );
437 :
438 0 : PNameValidation pNameCheck( NameCheckFactory::createExistenceCheck( getContext(), _CommandType, getConnection() ) );
439 0 : pNameCheck->validateName_throw( _Name );
440 :
441 0 : pNameCheck = NameCheckFactory::createValidityCheck( getContext(), _CommandType, getConnection() );
442 0 : pNameCheck->validateName_throw( _Name );
443 0 : }
444 :
445 : } // namespace sdbtools
446 :
447 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|