LibreOffice Module dbaccess (master) 1
objectnames.cxx
Go to the documentation of this file.
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#include <core_resource.hxx>
22
23#include <strings.hrc>
24
25#include <com/sun/star/sdb/CommandType.hpp>
26#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
27#include <com/sun/star/sdb/XQueriesSupplier.hpp>
28#include <com/sun/star/sdb/ErrorCondition.hpp>
29
33#include <osl/diagnose.h>
34
35#include <memory>
36#include <utility>
37
38namespace sdbtools
39{
40
41 using ::com::sun::star::uno::Reference;
42 using ::com::sun::star::sdbc::XConnection;
43 using ::com::sun::star::lang::IllegalArgumentException;
44 using ::com::sun::star::sdbc::SQLException;
45 using ::com::sun::star::sdbc::XDatabaseMetaData;
46 using ::com::sun::star::container::XNameAccess;
47 using ::com::sun::star::uno::UNO_QUERY_THROW;
48 using ::com::sun::star::sdbcx::XTablesSupplier;
49 using ::com::sun::star::sdb::XQueriesSupplier;
50 using ::com::sun::star::uno::Exception;
51 using ::com::sun::star::uno::Any;
52 using ::com::sun::star::uno::XComponentContext;
53
54 namespace CommandType = ::com::sun::star::sdb::CommandType;
55 namespace ErrorCondition = ::com::sun::star::sdb::ErrorCondition;
56
57 namespace {
58
59 // INameValidation
60 class INameValidation
61 {
62 public:
63 virtual bool validateName( const OUString& _rName ) = 0;
64 virtual void validateName_throw( const OUString& _rName ) = 0;
65
66 virtual ~INameValidation() { }
67 };
68
69 }
70
71 typedef std::shared_ptr< INameValidation > PNameValidation;
72
73 namespace {
74
75 // PlainExistenceCheck
76 class PlainExistenceCheck : public INameValidation
77 {
78 private:
79 Reference< XConnection > m_xConnection;
80 Reference< XNameAccess > m_xContainer;
81
82 public:
83 PlainExistenceCheck( const Reference< XConnection >& _rxConnection, const Reference< XNameAccess >& _rxContainer )
84 :m_xConnection( _rxConnection )
85 ,m_xContainer( _rxContainer )
86 {
87 OSL_ENSURE( m_xContainer.is(), "PlainExistenceCheck::PlainExistenceCheck: this will crash!" );
88 }
89
90 // INameValidation
91 virtual bool validateName( const OUString& _rName ) override
92 {
93 return !m_xContainer->hasByName( _rName );
94 }
95
96 virtual void validateName_throw( const OUString& _rName ) override
97 {
98 if ( validateName( _rName ) )
99 return;
100
102 SQLException aError( aErrors.getSQLException( ErrorCondition::DB_OBJECT_NAME_IS_USED, m_xConnection, _rName ) );
103
105 if ( aMeta.supportsSubqueriesInFrom() )
106 {
107 OUString sNeedDistinctNames( DBA_RES( STR_QUERY_AND_TABLE_DISTINCT_NAMES ) );
108 aError.NextException <<= SQLException( sNeedDistinctNames, m_xConnection, OUString(), 0, Any() );
109 }
110
111 throw aError;
112 }
113 };
114
115 // TableValidityCheck
116 class TableValidityCheck : public INameValidation
117 {
118 const Reference< XConnection > m_xConnection;
119
120 public:
121 TableValidityCheck( const Reference< XConnection >& _rxConnection )
122 :m_xConnection( _rxConnection )
123 {
124 }
125
126 virtual bool validateName( const OUString& _rName ) override
127 {
129 if ( !aMeta.restrictIdentifiersToSQL92() )
130 return true;
131
132 OUString sCatalog, sSchema, sName;
133 ::dbtools::qualifiedNameComponents(
134 m_xConnection->getMetaData(), _rName, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InTableDefinitions );
135
136 OUString sExtraNameCharacters( m_xConnection->getMetaData()->getExtraNameCharacters() );
137 return !( ( !sCatalog.isEmpty() && !::dbtools::isValidSQLName( sCatalog, sExtraNameCharacters ) )
138 || ( !sSchema.isEmpty() && !::dbtools::isValidSQLName( sSchema, sExtraNameCharacters ) )
139 || ( !sName.isEmpty() && !::dbtools::isValidSQLName( sName, sExtraNameCharacters ) ));
140 }
141
142 virtual void validateName_throw( const OUString& _rName ) override
143 {
144 if ( validateName( _rName ) )
145 return;
146
148 aErrors.raiseException( ErrorCondition::DB_INVALID_SQL_NAME, m_xConnection, _rName );
149 }
150 };
151
152 // QueryValidityCheck
153 class QueryValidityCheck : public INameValidation
154 {
155 const Reference< XConnection > m_xConnection;
156
157 public:
158 QueryValidityCheck( const Reference< XConnection >& _rxConnection )
159 :m_xConnection( _rxConnection )
160 {
161 }
162
163 static ::connectivity::ErrorCondition validateName_getErrorCondition( std::u16string_view _rName )
164 {
165 if ( ( _rName.find( u'"' ) != std::u16string_view::npos )
166 || ( _rName.find( u'\'' ) != std::u16string_view::npos )
167 || ( _rName.find( u'`' ) != std::u16string_view::npos )
168 || ( _rName.find( u'\x0091' ) != std::u16string_view::npos )
169 || ( _rName.find( u'\x0092' ) != std::u16string_view::npos )
170 || ( _rName.find( u'\x00B4' ) != std::u16string_view::npos ) // removed unparsable chars
171 )
172 return ErrorCondition::DB_QUERY_NAME_WITH_QUOTES;
173
174 if ( _rName.find( '/') != std::u16string_view::npos )
175 return ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES;
176
177 return 0;
178 }
179
180 virtual bool validateName( const OUString& _rName ) override
181 {
182 return validateName_getErrorCondition( _rName ) == 0;
183 }
184
185 virtual void validateName_throw( const OUString& _rName ) override
186 {
187 ::connectivity::ErrorCondition nErrorCondition = validateName_getErrorCondition( _rName );
188 if ( nErrorCondition != 0 )
189 {
191 aErrors.raiseException( nErrorCondition, m_xConnection );
192 }
193 }
194 };
195
196 // CombinedNameCheck
197 class CombinedNameCheck : public INameValidation
198 {
199 private:
202
203 public:
204 CombinedNameCheck(PNameValidation _pPrimary, PNameValidation _pSecondary)
205 :m_pPrimary(std::move( _pPrimary ))
206 ,m_pSecondary(std::move( _pSecondary ))
207 {
208 OSL_ENSURE( m_pPrimary && m_pSecondary, "CombinedNameCheck::CombinedNameCheck: this will crash!" );
209 }
210
211 // INameValidation
212 virtual bool validateName( const OUString& _rName ) override
213 {
214 return m_pPrimary->validateName( _rName ) && m_pSecondary->validateName( _rName );
215 }
216
217 virtual void validateName_throw( const OUString& _rName ) override
218 {
219 m_pPrimary->validateName_throw( _rName );
220 m_pSecondary->validateName_throw( _rName );
221 }
222 };
223
224 // NameCheckFactory
225 class NameCheckFactory
226 {
227 public:
228 NameCheckFactory(const NameCheckFactory&) = delete;
229 const NameCheckFactory& operator=(const NameCheckFactory&) = delete;
244 static PNameValidation createExistenceCheck(
245 sal_Int32 _nCommandType,
246 const Reference< XConnection >& _rxConnection
247 );
248
263 static PNameValidation createValidityCheck(
264 const sal_Int32 _nCommandType,
265 const Reference< XConnection >& _rxConnection
266 );
267
268 private:
269 static void verifyCommandType( sal_Int32 _nCommandType );
270 };
271
272 }
273
274 void NameCheckFactory::verifyCommandType( sal_Int32 _nCommandType )
275 {
276 if ( ( _nCommandType != CommandType::TABLE )
277 && ( _nCommandType != CommandType::QUERY )
278 )
279 throw IllegalArgumentException(
280 DBA_RES( STR_INVALID_COMMAND_TYPE ),
281 nullptr,
282 0
283 );
284 }
285
286 PNameValidation NameCheckFactory::createExistenceCheck( sal_Int32 _nCommandType, const Reference< XConnection >& _rxConnection )
287 {
288 verifyCommandType( _nCommandType );
289
290 ::dbtools::DatabaseMetaData aMeta( _rxConnection );
291
292 Reference< XNameAccess > xTables, xQueries;
293 try
294 {
295 Reference< XTablesSupplier > xSuppTables( _rxConnection, UNO_QUERY_THROW );
296 Reference< XQueriesSupplier > xQueriesSupplier( _rxConnection, UNO_QUERY_THROW );
297 xTables.set( xSuppTables->getTables(), css::uno::UNO_SET_THROW );
298 xQueries.set( xQueriesSupplier->getQueries(), css::uno::UNO_SET_THROW );
299 }
300 catch( const Exception& )
301 {
302 throw IllegalArgumentException(
303 DBA_RES( STR_CONN_WITHOUT_QUERIES_OR_TABLES ),
304 nullptr,
305 0
306 );
307 }
308
309 PNameValidation pTableCheck = std::make_shared<PlainExistenceCheck>( _rxConnection, xTables );
310 PNameValidation pQueryCheck = std::make_shared<PlainExistenceCheck>( _rxConnection, xQueries );
311 PNameValidation pReturn;
312
313 if ( aMeta.supportsSubqueriesInFrom() )
314 pReturn = std::make_shared<CombinedNameCheck>( pTableCheck, pQueryCheck );
315 else if ( _nCommandType == CommandType::TABLE )
316 pReturn = pTableCheck;
317 else
318 pReturn = pQueryCheck;
319 return pReturn;
320 }
321
322 PNameValidation NameCheckFactory::createValidityCheck( sal_Int32 _nCommandType, const Reference< XConnection >& _rxConnection )
323 {
324 verifyCommandType( _nCommandType );
325
326 Reference< XDatabaseMetaData > xMeta;
327 try
328 {
329 xMeta.set( _rxConnection->getMetaData(), css::uno::UNO_SET_THROW );
330 }
331 catch( const Exception& )
332 {
333 throw IllegalArgumentException(
334 "The connection could not provide its database's meta data.",
335 nullptr,
336 0
337 );
338 }
339
340 if ( _nCommandType == CommandType::TABLE )
341 return std::make_shared<TableValidityCheck>( _rxConnection );
342 return std::make_shared<QueryValidityCheck>( _rxConnection );
343 }
344
345 // ObjectNames
346 ObjectNames::ObjectNames( const Reference<XComponentContext>& _rContext, const Reference< XConnection >& _rxConnection )
347 :ConnectionDependentComponent( _rContext )
348 {
349 setWeakConnection( _rxConnection );
350 }
351
352 ObjectNames::~ObjectNames()
353 {
354 }
355
356 OUString SAL_CALL ObjectNames::suggestName( ::sal_Int32 CommandType, const OUString& BaseName )
357 {
358 EntryGuard aGuard( *this );
359
360 PNameValidation pNameCheck( NameCheckFactory::createExistenceCheck( CommandType, getConnection() ) );
361
362 OUString sBaseName( BaseName );
363 if ( sBaseName.isEmpty() )
364 {
366 sBaseName = DBA_RES(STR_BASENAME_TABLE);
367 else
368 sBaseName = DBA_RES(STR_BASENAME_QUERY);
369 }
370 else if( CommandType == CommandType::QUERY )
371 {
372 sBaseName=sBaseName.replace('/', '_');
373 }
374
375 OUString sName( sBaseName );
376 sal_Int32 i = 1;
377 while ( !pNameCheck->validateName( sName ) )
378 {
379 sName = sBaseName + " " + OUString::number(++i);
380 }
381
382 return sName;
383 }
384
385 OUString SAL_CALL ObjectNames::convertToSQLName( const OUString& Name )
386 {
387 EntryGuard aGuard( *this );
388 Reference< XDatabaseMetaData > xMeta( getConnection()->getMetaData(), css::uno::UNO_SET_THROW );
389 return ::dbtools::convertName2SQLName( Name, xMeta->getExtraNameCharacters() );
390 }
391
392 sal_Bool SAL_CALL ObjectNames::isNameUsed( ::sal_Int32 CommandType, const OUString& Name )
393 {
394 EntryGuard aGuard( *this );
395
396 PNameValidation pNameCheck( NameCheckFactory::createExistenceCheck( CommandType, getConnection()) );
397 return !pNameCheck->validateName( Name );
398 }
399
400 sal_Bool SAL_CALL ObjectNames::isNameValid( ::sal_Int32 CommandType, const OUString& Name )
401 {
402 EntryGuard aGuard( *this );
403
404 PNameValidation pNameCheck( NameCheckFactory::createValidityCheck( CommandType, getConnection()) );
405 return pNameCheck->validateName( Name );
406 }
407
408 void SAL_CALL ObjectNames::checkNameForCreate( ::sal_Int32 CommandType, const OUString& Name )
409 {
410 EntryGuard aGuard( *this );
411
412 PNameValidation pNameCheck( NameCheckFactory::createExistenceCheck( CommandType, getConnection() ) );
413 pNameCheck->validateName_throw( Name );
414
415 pNameCheck = NameCheckFactory::createValidityCheck( CommandType, getConnection() );
416 pNameCheck->validateName_throw( Name );
417 }
418
419} // namespace sdbtools
420
421/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OptionalString sSchema
OptionalString sCatalog
OptionalString sName
void raiseException(const ErrorCondition _eCondition, const css::uno::Reference< css::uno::XInterface > &_rxContext, const std::optional< OUString > &_rParamValue1=std::nullopt, const std::optional< OUString > &_rParamValue2=std::nullopt, const std::optional< OUString > &_rParamValue3=std::nullopt) const
css::sdbc::SQLException getSQLException(const ErrorCondition _eCondition, const css::uno::Reference< css::uno::XInterface > &_rxContext, const std::optional< OUString > &_rParamValue1=std::nullopt, const std::optional< OUString > &_rParamValue2=std::nullopt, const std::optional< OUString > &_rParamValue3=std::nullopt) const
a class for guarding methods of a connection-dependent component
ObjectNames(const css::uno::Reference< css::uno::XComponentContext > &_rContext, const css::uno::Reference< css::sdbc::XConnection > &_rxConnection)
constructs the instance
#define DBA_RES(id)
::sal_Int32 ErrorCondition
Reference< XConnection > getConnection(const Reference< XRowSet > &_rxRowSet)
int i
std::shared_ptr< INameValidation > PNameValidation
Definition: objectnames.cxx:71
PNameValidation m_pPrimary
PNameValidation m_pSecondary
Reference< XNameAccess > m_xContainer
Definition: objectnames.cxx:80
Reference< XConnection > m_xConnection
Definition: objectnames.cxx:79
OUString Name
unsigned char sal_Bool