LibreOffice Module dbaccess (master) 1
FilteredContainer.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 <strings.hxx>
21#include <FilteredContainer.hxx>
22#include <RefreshListener.hxx>
23#include <sdbcoretools.hxx>
24#include <com/sun/star/sdbc/SQLException.hpp>
25#include <com/sun/star/sdbc/XRow.hpp>
26#include <comphelper/types.hxx>
28#include <tools/wldcrd.hxx>
30#include <optional>
31#include <sal/log.hxx>
32
33namespace dbaccess
34{
35 using namespace dbtools;
36 using namespace ::com::sun::star::uno;
37 using namespace ::com::sun::star::lang;
38 using namespace ::com::sun::star::beans;
39 using namespace ::com::sun::star::sdbc;
40 using namespace ::com::sun::star::sdb;
41 using namespace ::com::sun::star::sdbcx;
42 using namespace ::com::sun::star::util;
43 using namespace ::com::sun::star::container;
44 using namespace ::osl;
45 using namespace ::comphelper;
46 using namespace ::cppu;
47 using namespace ::connectivity::sdbcx;
48
51static sal_Int32 createWildCardVector(Sequence< OUString >& _rTableFilter, std::vector< WildCard >& _rOut)
52{
53 // for wildcard search : remove all table filters which are a wildcard expression and build a WildCard
54 // for them
55 OUString* pTableFilters = _rTableFilter.getArray();
56 OUString* pEnd = pTableFilters + _rTableFilter.getLength();
57 sal_Int32 nShiftPos = 0;
58 for (sal_Int32 i=0; pEnd != pTableFilters; ++pTableFilters,++i)
59 {
60 if (pTableFilters->indexOf('%') != -1)
61 {
62 _rOut.emplace_back(pTableFilters->replace('%', '*'));
63 }
64 else
65 {
66 if (nShiftPos != i)
67 {
68 _rTableFilter.getArray()[nShiftPos] = _rTableFilter.getArray()[i];
69 }
70 ++nShiftPos;
71 }
72 }
73 // now aTableFilter contains nShiftPos non-wc-strings and aWCSearch all wc-strings
74 _rTableFilter.realloc(nShiftPos);
75 return nShiftPos;
76}
77
78 static bool lcl_isElementAllowed( const OUString& _rName,
79 const Sequence< OUString >& _rTableFilter,
80 const std::vector< WildCard >& _rWCSearch )
81 {
82 sal_Int32 nTableFilterLen = _rTableFilter.getLength();
83
84 const OUString* tableFilter = _rTableFilter.getConstArray();
85 const OUString* tableFilterEnd = _rTableFilter.getConstArray() + nTableFilterLen;
86 bool bFilterMatch = std::find( tableFilter, tableFilterEnd, _rName ) != tableFilterEnd;
87 // the table is allowed to "pass" if we had no filters at all or any of the non-wildcard filters matches
88 if (!bFilterMatch && !_rWCSearch.empty())
89 { // or if one of the wildcard expression matches
90 for (auto const& elem : _rWCSearch)
91 {
92 bFilterMatch = elem.Matches( _rName );
93 if (bFilterMatch)
94 break;
95 }
96 }
97
98 return bFilterMatch;
99 }
100
101 typedef ::std::optional< OUString > OptionalString;
102
103 namespace {
104
105 struct TableInfo
106 {
112
113 explicit TableInfo( const OUString& _composedName )
114 : sComposedName( _composedName )
115 {
116 }
117
118 TableInfo( const OUString& _catalog, const OUString& _schema, const OUString& _name,
119 const OUString& _type )
120 :sType( _type )
121 ,sCatalog( _catalog )
122 ,sSchema( _schema )
123 ,sName( _name )
124 {
125 }
126 };
127
128 }
129
130 typedef std::vector< TableInfo > TableInfos;
131
132 static void lcl_ensureComposedName( TableInfo& _io_tableInfo, const Reference< XDatabaseMetaData >& _metaData )
133 {
134 if ( !_metaData.is() )
135 throw RuntimeException("lcl_ensureComposedName: _metaData cannot be null!");
136
137 if ( !_io_tableInfo.sComposedName )
138 {
139 OSL_ENSURE( !!_io_tableInfo.sCatalog && !!_io_tableInfo.sSchema && !!_io_tableInfo.sName, "lcl_ensureComposedName: How should I composed the name from nothing!?" );
140
141 _io_tableInfo.sComposedName = OptionalString(
142 composeTableName( _metaData, *_io_tableInfo.sCatalog, *_io_tableInfo.sSchema, *_io_tableInfo.sName,
143 false, ::dbtools::EComposeRule::InDataManipulation )
144 );
145 }
146 }
147
148 static void lcl_ensureType( TableInfo& _io_tableInfo, const Reference< XDatabaseMetaData >& _metaData, const Reference< XNameAccess >& _masterContainer )
149 {
150 if ( !!_io_tableInfo.sType )
151 return;
152
153 lcl_ensureComposedName( _io_tableInfo, _metaData );
154
155 if ( !_masterContainer.is() )
156 throw RuntimeException("lcl_ensureType: _masterContainer cannot be null!");
157
158 OUString sTypeName;
159 try
160 {
161 Reference< XPropertySet > xTable( _masterContainer->getByName( *_io_tableInfo.sComposedName ), UNO_QUERY_THROW );
162 OSL_VERIFY( xTable->getPropertyValue( PROPERTY_TYPE ) >>= sTypeName );
163 }
164 catch( const Exception& )
165 {
166 DBG_UNHANDLED_EXCEPTION("dbaccess");
167 }
168 _io_tableInfo.sType = OptionalString( sTypeName );
169 }
170
171 static ::std::vector< OUString> lcl_filter( TableInfos&& _unfilteredTables,
172 const Sequence< OUString >& _tableFilter, const Sequence< OUString >& _tableTypeFilter,
173 const Reference< XDatabaseMetaData >& _metaData, const Reference< XNameAccess >& _masterContainer )
174 {
175 TableInfos aFilteredTables;
176
177 // first, filter for the table names
178 sal_Int32 nTableFilterCount = _tableFilter.getLength();
179 bool dontFilterTableNames = ( ( nTableFilterCount == 1 ) && _tableFilter[0] == "%" );
180 if( dontFilterTableNames )
181 {
182 aFilteredTables = std::move(_unfilteredTables);
183 }
184 else
185 {
186 // for wildcard search : remove all table filters which are a wildcard expression and build a WildCard
187 // for them
188 std::vector< WildCard > aWildCardTableFilter;
189 Sequence< OUString > aNonWildCardTableFilter = _tableFilter;
190 nTableFilterCount = createWildCardVector( aNonWildCardTableFilter, aWildCardTableFilter );
191
192 TableInfos aUnfilteredTables( std::move(_unfilteredTables) );
193 aUnfilteredTables.reserve( nTableFilterCount + ( aWildCardTableFilter.size() * 10 ) );
194
195 for (auto & unfilteredTable : aUnfilteredTables)
196 {
197 lcl_ensureComposedName(unfilteredTable, _metaData);
198
199 if ( lcl_isElementAllowed( *unfilteredTable.sComposedName, aNonWildCardTableFilter, aWildCardTableFilter ) )
200 aFilteredTables.push_back(unfilteredTable);
201 }
202 }
203
204 // second, filter for the table types
205 sal_Int32 nTableTypeFilterCount = _tableTypeFilter.getLength();
206 bool dontFilterTableTypes = ( ( nTableTypeFilterCount == 1 ) && _tableTypeFilter[0] == "%" );
207 dontFilterTableTypes = dontFilterTableTypes || ( nTableTypeFilterCount == 0 );
208 // (for TableTypeFilter, unlike TableFilter, "empty" means "do not filter at all")
209 if ( !dontFilterTableTypes )
210 {
211 TableInfos aUnfilteredTables;
212 aUnfilteredTables.swap( aFilteredTables );
213
214 const OUString* pTableTypeFilterBegin = _tableTypeFilter.getConstArray();
215 const OUString* pTableTypeFilterEnd = pTableTypeFilterBegin + _tableTypeFilter.getLength();
216
217 for (auto & unfilteredTable : aUnfilteredTables)
218 {
219 // ensure that we know the table type
220 lcl_ensureType( unfilteredTable, _metaData, _masterContainer );
221
222 if ( std::find( pTableTypeFilterBegin, pTableTypeFilterEnd, *unfilteredTable.sType ) != pTableTypeFilterEnd )
223 aFilteredTables.push_back(unfilteredTable);
224 }
225 }
226
227 ::std::vector< OUString> aReturn;
228 for (auto & filteredTable : aFilteredTables)
229 {
230 lcl_ensureComposedName(filteredTable, _metaData);
231 aReturn.push_back(*filteredTable.sComposedName);
232 }
233 return aReturn;
234 }
235
236 // OViewContainer
238 ::osl::Mutex& _rMutex,
239 const Reference< XConnection >& _xCon,
240 bool _bCase,
241 IRefreshListener* _pRefreshListener,
242 std::atomic<std::size_t>& _nInAppend)
243 :OCollection(_rParent,_bCase,_rMutex,std::vector< OUString>())
244 ,m_bConstructed(false)
245 ,m_pRefreshListener(_pRefreshListener)
246 ,m_nInAppend(_nInAppend)
247 ,m_xConnection(_xCon)
248 {
249 }
250
251 void OFilteredContainer::construct(const Reference< XNameAccess >& _rxMasterContainer,
252 const Sequence< OUString >& _rTableFilter,
253 const Sequence< OUString >& _rTableTypeFilter)
254 {
255 try
256 {
257 Reference<XConnection> xCon = m_xConnection;
258 if ( xCon.is() )
259 m_xMetaData = xCon->getMetaData();
260 }
261 catch(SQLException&)
262 {
263 DBG_UNHANDLED_EXCEPTION("dbaccess");
264 }
265
266 m_xMasterContainer = _rxMasterContainer;
267
268 if ( m_xMasterContainer.is() )
269 {
271
272 TableInfos aUnfilteredTables;
273
274 Sequence< OUString > aNames = m_xMasterContainer->getElementNames();
275 const OUString* name = aNames.getConstArray();
276 const OUString* nameEnd = name + aNames.getLength();
277 for ( ; name != nameEnd; ++name )
278 aUnfilteredTables.emplace_back( *name );
279
280 reFill( lcl_filter( std::move(aUnfilteredTables),
281 _rTableFilter, _rTableTypeFilter, m_xMetaData, m_xMasterContainer ) );
282
283 m_bConstructed = true;
284 }
285 else
286 {
287 construct( _rTableFilter, _rTableTypeFilter );
288 }
289 }
290
291 void OFilteredContainer::construct(const Sequence< OUString >& _rTableFilter, const Sequence< OUString >& _rTableTypeFilter)
292 {
293 // build sorted versions of the filter sequences, so the visibility decision is faster
294 Sequence< OUString > aTableFilter(_rTableFilter);
295
296 // for wildcard search : remove all table filters which are a wildcard expression and build a WildCard
297 // for them
298 std::vector< WildCard > aWCSearch;
299 createWildCardVector(aTableFilter,aWCSearch);
300
301 try
302 {
303 Reference< XConnection > xCon( m_xConnection, UNO_SET_THROW );
304 m_xMetaData.set( xCon->getMetaData(), UNO_SET_THROW );
305
306 // create a table filter suitable for the XDatabaseMetaData::getTables call,
307 // taking into account both the externally-provided table type filter, and any
308 // table type restriction which is inherent to the container
309 Sequence< OUString > aTableTypeFilter;
310 OUString sInherentTableTypeRestriction( getTableTypeRestriction() );
311 if ( !sInherentTableTypeRestriction.isEmpty() )
312 {
313 if ( _rTableTypeFilter.hasElements() )
314 {
315 const OUString* tableType = _rTableTypeFilter.getConstArray();
316 const OUString* tableTypeEnd = tableType + _rTableTypeFilter.getLength();
317 for ( ; tableType != tableTypeEnd; ++tableType )
318 {
319 if ( *tableType == sInherentTableTypeRestriction )
320 break;
321 }
322 if ( tableType == tableTypeEnd )
323 { // the only table type which can be part of this container is not allowed
324 // by the externally provided table type filter.
325 m_bConstructed = true;
326 return;
327 }
328 }
329 aTableTypeFilter = { sInherentTableTypeRestriction };
330 }
331 else
332 {
333 // no container-inherent restriction for the table types
334 if ( !_rTableTypeFilter.hasElements() )
335 { // no externally-provided table type filter => use the default filter
336 getAllTableTypeFilter( aTableTypeFilter );
337 }
338 else
339 {
340 aTableTypeFilter = _rTableTypeFilter;
341 }
342 }
343
344 static constexpr OUStringLiteral sAll = u"%";
345 Reference< XResultSet > xTables = m_xMetaData->getTables( Any(), sAll, sAll, aTableTypeFilter );
346 Reference< XRow > xCurrentRow( xTables, UNO_QUERY_THROW );
347
348 TableInfos aUnfilteredTables;
349
350 OUString sCatalog, sSchema, sName, sType;
351 while ( xTables->next() )
352 {
353 sCatalog = xCurrentRow->getString(1);
354 sSchema = xCurrentRow->getString(2);
355 sName = xCurrentRow->getString(3);
356 sType = xCurrentRow->getString(4);
357
358 aUnfilteredTables.emplace_back( sCatalog, sSchema, sName, sType );
359 }
360
361 reFill( lcl_filter( std::move(aUnfilteredTables),
362 _rTableFilter, aTableTypeFilter, m_xMetaData, nullptr ) );
363
364 disposeComponent( xTables );
365 }
366 catch (const Exception&)
367 {
368 DBG_UNHANDLED_EXCEPTION("dbaccess");
369 disposing();
370 return;
371 }
372
373 m_bConstructed = true;
374 }
375
377 {
378 OCollection::disposing();
379
380 if ( m_xMasterContainer.is() )
382
383 m_xMasterContainer = nullptr;
384 m_xMetaData = nullptr;
385 m_pRefreshListener = nullptr;
386 m_bConstructed = false;
387 }
388
390 {
391 if ( m_pRefreshListener )
392 {
393 m_bConstructed = false;
394 Reference<XRefreshable> xRefresh(m_xMasterContainer,UNO_QUERY);
395 if ( xRefresh.is() )
396 xRefresh->refresh();
398 }
399 }
400
402 {
403 OSL_ENSURE( _xObject.is(), "OFilteredContainer::getNameForObject: Object is NULL!" );
404 return ::dbtools::composeTableName( m_xMetaData, _xObject, ::dbtools::EComposeRule::InDataManipulation, false );
405 }
406
407 // multiple to obtain all tables from XDatabaseMetaData::getTables, via passing a particular
408 // table type filter:
409 // adhere to the standard, which requests to pass a NULL table type filter, if
410 // you want to retrieve all tables
411 #define FILTER_MODE_STANDARD 0
412 // only pass %, which is not allowed by the standard, but understood by some drivers
413 #define FILTER_MODE_WILDCARD 1
414 // only pass TABLE and VIEW
415 #define FILTER_MODE_FIXED 2
416 // do the thing which showed to be the safest way, understood by nearly all
417 // drivers, even the ones which do not understand the standard
418 #define FILTER_MODE_MIX_ALL 3
419
420 void OFilteredContainer::getAllTableTypeFilter( Sequence< OUString >& /* [out] */ _rFilter ) const
421 {
422 sal_Int32 nFilterMode = FILTER_MODE_MIX_ALL;
423 // for compatibility reasons, this is the default: we used this way before we
424 // introduced the TableTypeFilterMode setting
425
426 // obtain the data source we belong to, and the TableTypeFilterMode setting
427 Any aFilterModeSetting;
428 if ( getDataSourceSetting( getDataSource( Reference< XInterface >(m_rParent) ), "TableTypeFilterMode", aFilterModeSetting ) )
429 {
430 OSL_VERIFY( aFilterModeSetting >>= nFilterMode );
431 }
432
433 static constexpr OUStringLiteral sAll( u"%" );
434 static constexpr OUStringLiteral sView( u"VIEW" );
435 static constexpr OUStringLiteral sTable( u"TABLE" );
436
437 switch ( nFilterMode )
438 {
439 default:
440 SAL_WARN("dbaccess", "OTableContainer::getAllTableTypeFilter: unknown TableTypeFilterMode!" );
441 [[fallthrough]];
443 _rFilter = { sView, sTable, sAll };
444 break;
446 _rFilter = { sView, sTable };
447 break;
449 _rFilter = { sAll };
450 break;
452 _rFilter.realloc( 0 );
453 break;
454 }
455 }
456
457} // namespace
458
459/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OptionalString sSchema
OptionalString sCatalog
OptionalString sComposedName
#define FILTER_MODE_WILDCARD
OptionalString sType
#define FILTER_MODE_MIX_ALL
OptionalString sName
#define FILTER_MODE_STANDARD
#define FILTER_MODE_FIXED
void reFill(const ::std::vector< OUString > &_rVector)
::cppu::OWeakObject & m_rParent
virtual void refresh(const css::uno::Reference< css::container::XNameAccess > &_rToBeRefreshed)=0
virtual void impl_refresh() override
virtual void removeMasterContainerListener()
IRefreshListener * m_pRefreshListener
css::uno::Reference< css::container::XNameAccess > m_xMasterContainer
void getAllTableTypeFilter(css::uno::Sequence< OUString > &_rFilter) const
retrieve a table type filter to pass to <member scope="css::sdbc">XDatabaseMetaData::getTables</membe...
virtual OUString getNameForObject(const ::connectivity::sdbcx::ObjectType &_xObject) override
virtual void disposing() override
tell the container to free all elements and all additional resources.
css::uno::WeakReference< css::sdbc::XConnection > m_xConnection
css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xMetaData
virtual OUString getTableTypeRestriction() const =0
returns a string denoting the only type of tables allowed in this container, or an empty string if th...
OFilteredContainer(::cppu::OWeakObject &_rParent, ::osl::Mutex &_rMutex, const css::uno::Reference< css::sdbc::XConnection > &_xCon, bool _bCase, IRefreshListener *_pRefreshListener, std::atomic< std::size_t > &_nInAppend)
ctor of the container.
void construct(const css::uno::Sequence< OUString > &_rTableFilter, const css::uno::Sequence< OUString > &_rTableTypeFilter)
late ctor.
#define DBG_UNHANDLED_EXCEPTION(...)
float u
const char * name
#define SAL_WARN(area, stream)
@ Exception
::std::optional< OUString > OptionalString
void disposeComponent(css::uno::Reference< TYPE > &_rxComp)
static bool lcl_isElementAllowed(const OUString &_rName, const Sequence< OUString > &_rTableFilter, const std::vector< WildCard > &_rWCSearch)
static void lcl_ensureComposedName(TableInfo &_io_tableInfo, const Reference< XDatabaseMetaData > &_metaData)
css::uno::Reference< css::uno::XInterface > getDataSource(const css::uno::Reference< css::uno::XInterface > &_rxDependentObject)
static void lcl_ensureType(TableInfo &_io_tableInfo, const Reference< XDatabaseMetaData > &_metaData, const Reference< XNameAccess > &_masterContainer)
::std::optional< OUString > OptionalString
static ::std::vector< OUString > lcl_filter(TableInfos &&_unfilteredTables, const Sequence< OUString > &_tableFilter, const Sequence< OUString > &_tableTypeFilter, const Reference< XDatabaseMetaData > &_metaData, const Reference< XNameAccess > &_masterContainer)
std::vector< TableInfo > TableInfos
static sal_Int32 createWildCardVector(Sequence< OUString > &_rTableFilter, std::vector< WildCard > &_rOut)
creates a vector of WildCards and reduce the _rTableFilter of the length of WildCards
bool getDataSourceSetting(const Reference< XInterface > &_xChild, const OUString &_sAsciiSettingsName, Any &_rSettingsValue)
OUString composeTableName(const Reference< XDatabaseMetaData > &_rxMetaData, const OUString &_rCatalog, const OUString &_rSchema, const OUString &_rName, bool _bQuote, EComposeRule _eComposeRule)
int i
ObjectType
Reference< XConnection > m_xConnection
Definition: objectnames.cxx:79
constexpr OUStringLiteral PROPERTY_TYPE(u"Type")