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