diff options
author | Lionel Elie Mamane <lionel@mamane.lu> | 2017-07-30 17:57:14 +0200 |
---|---|---|
committer | Lionel Elie Mamane <lionel@mamane.lu> | 2017-07-30 20:23:09 +0200 |
commit | 2b1d6f0d3b0b025148c81986ba7f109659d838af (patch) | |
tree | 475feb3567e60c37b5bc6f669711c1b9a65c33ad | |
parent | aba73077851a744c06e72b3bddf5a0bae85d7c28 (diff) |
tdf#96370 rework filtering to be aware of WHERE vs HAVING clause
Several bugs (AFAIK not filed into tdf bugzilla) fixed.
Remaining problems:
When some filter clauses go into WHERE and others in HAVING,
they are always logically ANDed (it cannot be any other way),
but that is not communicated to the user in the UI.
Some things left undone:
* DatabaseDataProvider (and its users?) needs to be updated
to be HAVING-aware, too.
* Form-based filter (.uno:FormFilter) not HAVING-aware
it reads the current filter in function
svxform::FormController::setFilter
in
svx/source/form/formcontrollers.cxx
That's one place that needs to be updated.
The other place is the one that applies the filter.
Change-Id: I0e9d30a1927b6739a16ae7627e8d0dae8823b376
-rw-r--r-- | connectivity/source/commontools/dbtools2.cxx | 35 | ||||
-rw-r--r-- | connectivity/source/commontools/filtermanager.cxx | 108 | ||||
-rw-r--r-- | connectivity/source/commontools/parameters.cxx | 37 | ||||
-rw-r--r-- | dbaccess/source/core/misc/DatabaseDataProvider.cxx | 2 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/brwctrlr.cxx | 16 | ||||
-rw-r--r-- | dbaccess/source/ui/dlg/queryfilter.cxx | 14 | ||||
-rw-r--r-- | dbaccess/source/ui/inc/queryfilter.hxx | 4 | ||||
-rw-r--r-- | forms/source/component/DatabaseForm.cxx | 42 | ||||
-rw-r--r-- | forms/source/inc/frm_strings.hxx | 1 | ||||
-rw-r--r-- | forms/source/inc/property.hxx | 2 | ||||
-rw-r--r-- | forms/source/runtime/formoperations.cxx | 51 | ||||
-rw-r--r-- | forms/source/runtime/formoperations.hxx | 14 | ||||
-rw-r--r-- | include/connectivity/dbtools.hxx | 39 | ||||
-rw-r--r-- | include/connectivity/filtermanager.hxx | 12 | ||||
-rw-r--r-- | include/connectivity/parameters.hxx | 8 |
15 files changed, 329 insertions, 56 deletions
diff --git a/connectivity/source/commontools/dbtools2.cxx b/connectivity/source/commontools/dbtools2.cxx index 3db5b431bd35..43f4aaef2774 100644 --- a/connectivity/source/commontools/dbtools2.cxx +++ b/connectivity/source/commontools/dbtools2.cxx @@ -52,6 +52,7 @@ namespace dbtools using namespace ::com::sun::star::uno; using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::sdb; using namespace ::com::sun::star::sdbc; using namespace ::com::sun::star::sdbcx; using namespace ::com::sun::star::lang; @@ -1007,6 +1008,40 @@ OUString getDefaultReportEngineServiceName(const Reference< XComponentContext >& return OUString(); } +bool isAggregateColumn(const Reference< XSingleSelectQueryComposer > &_xParser, const Reference< XPropertySet > &_xField, bool whenNotFound) +{ + OUString sName; + _xField->getPropertyValue("Name") >>= sName; + Reference< XColumnsSupplier > xColumnsSupplier(_xParser, UNO_QUERY); + Reference< css::container::XNameAccess > xCols; + if (xColumnsSupplier.is()) + xCols = xColumnsSupplier->getColumns(); + + return isAggregateColumn(xCols, sName, whenNotFound); +} + +bool isAggregateColumn(const Reference< XNameAccess > &_xColumns, const OUString &_sName, bool whenNotFound) +{ + if ( _xColumns.is() && _xColumns->hasByName(_sName) ) + { + Reference<XPropertySet> xProp(_xColumns->getByName(_sName),UNO_QUERY); + assert(xProp.is()); + return isAggregateColumn( xProp ); + } + return whenNotFound; +} + +bool isAggregateColumn( const Reference< XPropertySet > &_xColumn ) +{ + bool bAgg(false); + + static const char sAgg[] = "AggregateFunction"; + if ( _xColumn->getPropertySetInfo()->hasPropertyByName(sAgg) ) + _xColumn->getPropertyValue(sAgg) >>= bAgg; + + return bAgg; +} + } // namespace dbtools diff --git a/connectivity/source/commontools/filtermanager.cxx b/connectivity/source/commontools/filtermanager.cxx index ba2d2df20878..5894872625fa 100644 --- a/connectivity/source/commontools/filtermanager.cxx +++ b/connectivity/source/commontools/filtermanager.cxx @@ -62,20 +62,64 @@ namespace dbtools const OUString& FilterManager::getFilterComponent( FilterComponent _eWhich ) const { - return _eWhich == FilterComponent::PublicFilter ? m_aPublicFilterComponent : m_aLinkFilterComponent; + switch (_eWhich) + { + case FilterComponent::PublicFilter: + return m_aPublicFilterComponent; + case FilterComponent::PublicHaving: + return m_aPublicHavingComponent; + case FilterComponent::LinkFilter: + return m_aLinkFilterComponent; + case FilterComponent::LinkHaving: + return m_aLinkHavingComponent; + } + assert(false); + + static OUString sErr("#FilterManager::getFilterComponent unknown component#"); + return sErr; } void FilterManager::setFilterComponent( FilterComponent _eWhich, const OUString& _rComponent ) { - if (_eWhich == FilterComponent::PublicFilter) + switch (_eWhich) + { + case FilterComponent::PublicFilter: m_aPublicFilterComponent = _rComponent; - else + break; + case FilterComponent::PublicHaving: + m_aPublicHavingComponent = _rComponent; + break; + case FilterComponent::LinkFilter: m_aLinkFilterComponent = _rComponent; + break; + case FilterComponent::LinkHaving: + m_aLinkHavingComponent = _rComponent; + break; + } try { - if ( m_xComponentAggregate.is() && (( _eWhich != FilterComponent::PublicFilter ) || m_bApplyPublicFilter ) ) - m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FILTER), makeAny( getComposedFilter() ) ); + if ( m_xComponentAggregate.is() ) + { + bool propagate(true); + switch (_eWhich) + { + case FilterComponent::PublicFilter: + propagate = propagate && m_bApplyPublicFilter; + SAL_FALLTHROUGH; + case FilterComponent::LinkFilter: + if (propagate) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FILTER), makeAny( getComposedFilter() ) ); + break; + case FilterComponent::PublicHaving: + propagate = propagate && m_bApplyPublicFilter; + SAL_FALLTHROUGH; + case FilterComponent::LinkHaving: + if (propagate) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_HAVINGCLAUSE), makeAny( getComposedHaving() ) ); + break; + } + } } catch( const Exception& ) { @@ -93,9 +137,13 @@ namespace dbtools try { - if ( m_xComponentAggregate.is() && !getFilterComponent( FilterComponent::PublicFilter ).isEmpty() ) - { // only if there changed something - m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FILTER), makeAny( getComposedFilter() ) ); + if ( m_xComponentAggregate.is()) + { + // only where/if something changed + if (!getFilterComponent( FilterComponent::PublicFilter ).isEmpty()) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FILTER), makeAny( getComposedFilter() ) ); + if (!getFilterComponent( FilterComponent::PublicHaving ).isEmpty()) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_HAVINGCLAUSE), makeAny( getComposedHaving() ) ); } } catch( const Exception& ) @@ -120,7 +168,7 @@ namespace dbtools } - bool FilterManager::isThereAtMostOneComponent( OUString& o_singleComponent ) const + bool FilterManager::isThereAtMostOneFilterComponent( OUString& o_singleComponent ) const { if (m_bApplyPublicFilter) { if (!m_aPublicFilterComponent.isEmpty() && !m_aLinkFilterComponent.isEmpty()) @@ -143,12 +191,35 @@ namespace dbtools } } + bool FilterManager::isThereAtMostOneHavingComponent( OUString& o_singleComponent ) const + { + if (m_bApplyPublicFilter) { + if (!m_aPublicHavingComponent.isEmpty() && !m_aLinkHavingComponent.isEmpty()) + return false; + if (!m_aPublicHavingComponent.isEmpty()) + o_singleComponent = m_aPublicHavingComponent; + else if (!m_aLinkHavingComponent.isEmpty()) + o_singleComponent = m_aLinkHavingComponent; + else + o_singleComponent.clear(); + return true; + } + else + { + if (m_aLinkHavingComponent.isEmpty()) + o_singleComponent.clear(); + else + o_singleComponent = m_aLinkHavingComponent; + return true; + } + } + OUString FilterManager::getComposedFilter( ) const { // if we have only one non-empty component, then there's no need to compose anything OUString singleComponent; - if ( isThereAtMostOneComponent( singleComponent ) ) + if ( isThereAtMostOneFilterComponent( singleComponent ) ) { return singleComponent; } @@ -161,6 +232,23 @@ namespace dbtools } + OUString FilterManager::getComposedHaving( ) const + { + // if we have only one non-empty component, then there's no need to compose anything + OUString singleComponent; + if ( isThereAtMostOneHavingComponent( singleComponent ) ) + { + return singleComponent; + } + // append the single components + OUStringBuffer aComposedFilter(singleComponent); + if (m_bApplyPublicFilter) + appendFilterComponent( aComposedFilter, m_aPublicHavingComponent ); + appendFilterComponent( aComposedFilter, m_aLinkHavingComponent ); + return aComposedFilter.makeStringAndClear(); + } + + } // namespace dbtools diff --git a/connectivity/source/commontools/parameters.cxx b/connectivity/source/commontools/parameters.cxx index 37f6a6bbd2f6..30311ba22aa4 100644 --- a/connectivity/source/commontools/parameters.cxx +++ b/connectivity/source/commontools/parameters.cxx @@ -245,7 +245,9 @@ namespace dbtools void ParameterManager::classifyLinks( const Reference< XNameAccess >& _rxParentColumns, - const Reference< XNameAccess >& _rxColumns, std::vector< OUString >& _out_rAdditionalFilterComponents ) + const Reference< XNameAccess >& _rxColumns, + std::vector< OUString >& _out_rAdditionalFilterComponents, + std::vector< OUString >& _out_rAdditionalHavingComponents ) { OSL_PRECOND( m_aMasterFields.size() == m_aDetailFields.size(), "ParameterManager::classifyLinks: master and detail fields should have the same length!" ); @@ -309,7 +311,10 @@ namespace dbtools aInsertionPos.first->second.eType = ParameterClassification::LinkedByColumnName; // remember the filter component - _out_rAdditionalFilterComponents.push_back( sFilterCondition ); + if (isAggregateColumn(xDetailField)) + _out_rAdditionalHavingComponents.push_back( sFilterCondition ); + else + _out_rAdditionalFilterComponents.push_back( sFilterCondition ); // remember the new "detail field" for this link aStrippedDetailFields.push_back( sNewParamName ); @@ -381,7 +386,8 @@ namespace dbtools // classify the links - depending on what the detail fields in each link pair denotes std::vector< OUString > aAdditionalFilterComponents; - classifyLinks( xParentColumns, xColumns, aAdditionalFilterComponents ); + std::vector< OUString > aAdditionalHavingComponents; + classifyLinks( xParentColumns, xColumns, aAdditionalFilterComponents, aAdditionalHavingComponents ); // did we find links where the detail field refers to a detail column (instead of a parameter name)? if ( !aAdditionalFilterComponents.empty() ) @@ -401,11 +407,34 @@ namespace dbtools sAdditionalFilter.append(" )"); } - // now set this filter at the 's filter manager + // now set this filter at the filter manager _rFilterManager.setFilterComponent( FilterManager::FilterComponent::LinkFilter, sAdditionalFilter.makeStringAndClear() ); _rColumnsInLinkDetails = true; } + + if ( !aAdditionalHavingComponents.empty() ) + { + // build a conjunction of all the filter components + OUStringBuffer sAdditionalHaving; + for ( std::vector< OUString >::const_iterator aComponent = aAdditionalHavingComponents.begin(); + aComponent != aAdditionalHavingComponents.end(); + ++aComponent + ) + { + if ( !sAdditionalHaving.isEmpty() ) + sAdditionalHaving.append(" AND "); + + sAdditionalHaving.append("( "); + sAdditionalHaving.append(*aComponent); + sAdditionalHaving.append(" )"); + } + + // now set this having clause at the filter manager + _rFilterManager.setFilterComponent( FilterManager::FilterComponent::LinkHaving, sAdditionalHaving.makeStringAndClear() ); + + _rColumnsInLinkDetails = true; + } } catch( const Exception& ) { diff --git a/dbaccess/source/core/misc/DatabaseDataProvider.cxx b/dbaccess/source/core/misc/DatabaseDataProvider.cxx index 0d8b9805c033..d3a823b93a80 100644 --- a/dbaccess/source/core/misc/DatabaseDataProvider.cxx +++ b/dbaccess/source/core/misc/DatabaseDataProvider.cxx @@ -47,6 +47,8 @@ #include <vector> #include <list> +// TODO: update for new HavingClause-aware FilterManager + namespace dbaccess { using namespace ::com::sun::star; diff --git a/dbaccess/source/ui/browser/brwctrlr.cxx b/dbaccess/source/ui/browser/brwctrlr.cxx index af094e97620d..4cc6913b0866 100644 --- a/dbaccess/source/ui/browser/brwctrlr.cxx +++ b/dbaccess/source/ui/browser/brwctrlr.cxx @@ -2006,18 +2006,7 @@ void SbaXDataBrowserController::Execute(sal_uInt16 nId, const Sequence< Property break; // check if the column is a aggregate function - bool bHaving = false; - OUString sName; - xField->getPropertyValue(PROPERTY_NAME) >>= sName; - Reference< XColumnsSupplier > xColumnsSupplier(m_xParser, UNO_QUERY); - Reference< css::container::XNameAccess > xCols = xColumnsSupplier.is() ? xColumnsSupplier->getColumns() : Reference< css::container::XNameAccess > (); - if ( xCols.is() && xCols->hasByName(sName) ) - { - Reference<XPropertySet> xProp(xCols->getByName(sName),UNO_QUERY); - static const char sAgg[] = "AggregateFunction"; - if ( xProp->getPropertySetInfo()->hasPropertyByName(sAgg) ) - xProp->getPropertyValue(sAgg) >>= bHaving; - } + const bool bHaving(isAggregateColumn(m_xParser, xField)); Reference< XSingleSelectQueryComposer > xParser = createParser_nothrow(); const OUString sOldFilter = xParser->getFilter(); @@ -2029,7 +2018,8 @@ void SbaXDataBrowserController::Execute(sal_uInt16 nId, const Sequence< Property // -> completely overwrite it, else append one if (!bApplied) { - DO_SAFE( (bHaving ? xParser->setHavingClause(OUString()) : xParser->setFilter(::OUString())), "SbaXDataBrowserController::Execute : caught an exception while resetting the new filter !" ); + DO_SAFE( xParser->setFilter( OUString()), "SbaXDataBrowserController::Execute : caught an exception while resetting unapplied filter !" ); + DO_SAFE( xParser->setHavingClause(OUString()), "SbaXDataBrowserController::Execute : caught an exception while resetting unapplied HAVING clause !" ); } bool bParserSuccess = false; diff --git a/dbaccess/source/ui/dlg/queryfilter.cxx b/dbaccess/source/ui/dlg/queryfilter.cxx index c9678ac2bfda..f7d04f0f0a4b 100644 --- a/dbaccess/source/ui/dlg/queryfilter.cxx +++ b/dbaccess/source/ui/dlg/queryfilter.cxx @@ -169,9 +169,10 @@ DlgFilterCrit::DlgFilterCrit(vcl::Window * pParent, // insert the criteria into the dialog Sequence<Sequence<PropertyValue > > aValues = m_xQueryComposer->getStructuredFilter(); - fillLines(aValues); + int i(0); + fillLines(i, aValues); aValues = m_xQueryComposer->getStructuredHavingClause(); - fillLines(aValues); + fillLines(i, aValues); EnableLines(); @@ -467,7 +468,7 @@ IMPL_LINK( DlgFilterCrit, PredicateLoseFocus, Control&, rControl, void ) } } -void DlgFilterCrit::SetLine( sal_uInt16 nIdx,const PropertyValue& _rItem,bool _bOr ) +void DlgFilterCrit::SetLine( int nIdx, const PropertyValue& _rItem, bool _bOr ) { OUString aStr; _rItem.Value >>= aStr; @@ -785,13 +786,13 @@ void DlgFilterCrit::BuildWherePart() } } -void DlgFilterCrit::fillLines(const Sequence<Sequence<PropertyValue > >& _aValues) +void DlgFilterCrit::fillLines(int &i, const Sequence< Sequence< PropertyValue > >& _aValues) { const Sequence<PropertyValue >* pOrIter = _aValues.getConstArray(); const Sequence<PropertyValue >* pOrEnd = pOrIter + _aValues.getLength(); - for(sal_uInt16 i=0;pOrIter != pOrEnd; ++pOrIter) + bool bOr(i != 0); // WHERE clause and HAVING clause are always ANDed, nor ORed + for(; pOrIter != pOrEnd; ++pOrIter) { - bool bOr = true; const PropertyValue* pAndIter = pOrIter->getConstArray(); const PropertyValue* pAndEnd = pAndIter + pOrIter->getLength(); for(;pAndIter != pAndEnd; ++pAndIter) @@ -799,6 +800,7 @@ void DlgFilterCrit::fillLines(const Sequence<Sequence<PropertyValue > >& _aValue SetLine( i++,*pAndIter,bOr); bOr = false; } + bOr=true; } } diff --git a/dbaccess/source/ui/inc/queryfilter.hxx b/dbaccess/source/ui/inc/queryfilter.hxx index f7253f1fa76a..5dd72f1e40ef 100644 --- a/dbaccess/source/ui/inc/queryfilter.hxx +++ b/dbaccess/source/ui/inc/queryfilter.hxx @@ -94,12 +94,12 @@ namespace dbaui DECL_LINK( ListSelectHdl, ListBox&, void ); DECL_LINK( ListSelectCompHdl, ListBox&, void ); - void SetLine( sal_uInt16 nIdx,const css::beans::PropertyValue& _rItem,bool _bOr ); + void SetLine( int nIdx, const css::beans::PropertyValue& _rItem, bool _bOr ); void EnableLines(); sal_Int32 GetOSQLPredicateType( const OUString& _rSelectedPredicate ) const; static sal_Int32 GetSelectionPos(sal_Int32 eType,const ListBox& rListBox); bool getCondition(const ListBox& _rField,const ListBox& _rComp,const Edit& _rValue,css::beans::PropertyValue& _rFilter) const; - void fillLines(const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& _aValues); + void fillLines(int &i, const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& _aValues); css::uno::Reference< css::beans::XPropertySet > getMatchingColumn( const Edit& _rValueInput ) const; css::uno::Reference< css::beans::XPropertySet > getColumn( const OUString& _rFieldName ) const; diff --git a/forms/source/component/DatabaseForm.cxx b/forms/source/component/DatabaseForm.cxx index 925d8a8fa3f2..1aae5d88aa39 100644 --- a/forms/source/component/DatabaseForm.cxx +++ b/forms/source/component/DatabaseForm.cxx @@ -1288,7 +1288,7 @@ void ODatabaseForm::describeFixedAndAggregateProperties( Sequence< Property >& _rProps, Sequence< Property >& _rAggregateProps ) const { - _rProps.realloc( 22 ); + _rProps.realloc( 23 ); css::beans::Property* pProperties = _rProps.getArray(); if (m_xAggregateSet.is()) @@ -1312,6 +1312,7 @@ void ODatabaseForm::describeFixedAndAggregateProperties( // (e.g. the ones which result from linking master fields to detail fields // via column names instead of parameters) RemoveProperty( _rAggregateProps, PROPERTY_FILTER ); + RemoveProperty( _rAggregateProps, PROPERTY_HAVINGCLAUSE ); RemoveProperty( _rAggregateProps, PROPERTY_APPLYFILTER ); DECL_IFACE_PROP4( ACTIVE_CONNECTION,XConnection, BOUND, TRANSIENT, MAYBEVOID, CONSTRAINED); @@ -1322,6 +1323,7 @@ void ODatabaseForm::describeFixedAndAggregateProperties( DECL_PROP2 ( DATASOURCE, OUString, BOUND, CONSTRAINED ); DECL_PROP3 ( CYCLE, TabulatorCycle, BOUND, MAYBEVOID, MAYBEDEFAULT ); DECL_PROP2 ( FILTER, OUString, BOUND, MAYBEDEFAULT ); + DECL_PROP2 ( HAVINGCLAUSE, OUString, BOUND, MAYBEDEFAULT ); DECL_BOOL_PROP2 ( INSERTONLY, BOUND, MAYBEDEFAULT ); DECL_PROP1 ( NAVIGATION, NavigationBarMode, BOUND ); DECL_BOOL_PROP1 ( ALLOWADDITIONS, BOUND ); @@ -1464,6 +1466,10 @@ void ODatabaseForm::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const rValue <<= m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter ); break; + case PROPERTY_ID_HAVINGCLAUSE: + rValue <<= m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving ); + break; + case PROPERTY_ID_APPLYFILTER: rValue <<= m_aFilterManager.isApplyPublicFilter(); break; @@ -1546,6 +1552,10 @@ sal_Bool ODatabaseForm::convertFastPropertyValue( Any& rConvertedValue, Any& rOl bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter ) ); break; + case PROPERTY_ID_HAVINGCLAUSE: + bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving ) ); + break; + case PROPERTY_ID_APPLYFILTER: bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.isApplyPublicFilter() ); break; @@ -1635,6 +1645,14 @@ void ODatabaseForm::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const A } break; + case PROPERTY_ID_HAVINGCLAUSE: + { + OUString sNewFilter; + rValue >>= sNewFilter; + m_aFilterManager.setFilterComponent( FilterManager::FilterComponent::PublicHaving, sNewFilter ); + } + break; + case PROPERTY_ID_APPLYFILTER: { bool bApply = true; @@ -1780,6 +1798,13 @@ PropertyState ODatabaseForm::getPropertyStateByHandle(sal_Int32 nHandle) eState = PropertyState_DIRECT_VALUE; break; + case PROPERTY_ID_HAVINGCLAUSE: + if ( m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving ).isEmpty() ) + eState = PropertyState_DEFAULT_VALUE; + else + eState = PropertyState_DIRECT_VALUE; + break; + case PROPERTY_ID_APPLYFILTER: eState = m_aFilterManager.isApplyPublicFilter() ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE; break; @@ -1813,6 +1838,7 @@ void ODatabaseForm::setPropertyToDefaultByHandle(sal_Int32 nHandle) { case PROPERTY_ID_INSERTONLY: case PROPERTY_ID_FILTER: + case PROPERTY_ID_HAVINGCLAUSE: case PROPERTY_ID_APPLYFILTER: case PROPERTY_ID_NAVIGATION: case PROPERTY_ID_CYCLE: @@ -1843,6 +1869,10 @@ Any ODatabaseForm::getPropertyDefaultByHandle( sal_Int32 nHandle ) const aReturn <<= OUString(); break; + case PROPERTY_ID_HAVINGCLAUSE: + aReturn <<= OUString(); + break; + case PROPERTY_ID_APPLYFILTER: aReturn <<= true; break; @@ -3741,7 +3771,7 @@ void SAL_CALL ODatabaseForm::write(const Reference<XObjectOutputStream>& _rxOutS OFormComponents::write(_rxOutStream); // version - _rxOutStream->writeShort(0x0003); + _rxOutStream->writeShort(0x0004); // Name _rxOutStream << m_sName; @@ -3819,10 +3849,13 @@ void SAL_CALL ODatabaseForm::write(const Reference<XObjectOutputStream>& _rxOutS _rxOutStream->writeShort((sal_Int16)m_eNavigation); OUString sFilter; + OUString sHaving; OUString sOrder; if (m_xAggregateSet.is()) { m_xAggregateSet->getPropertyValue(PROPERTY_FILTER) >>= sFilter; + // version 4 + m_xAggregateSet->getPropertyValue(PROPERTY_HAVINGCLAUSE) >>= sHaving; m_xAggregateSet->getPropertyValue(PROPERTY_SORT) >>= sOrder; } _rxOutStream << sFilter; @@ -3922,6 +3955,11 @@ void SAL_CALL ODatabaseForm::read(const Reference<XObjectInputStream>& _rxInStre _rxInStream >> sAggregateProp; setPropertyValue(PROPERTY_FILTER, makeAny(sAggregateProp)); + if(nVersion > 3) + { + _rxInStream >> sAggregateProp; + setPropertyValue(PROPERTY_HAVINGCLAUSE, makeAny(sAggregateProp)); + } _rxInStream >> sAggregateProp; if (m_xAggregateSet.is()) diff --git a/forms/source/inc/frm_strings.hxx b/forms/source/inc/frm_strings.hxx index 868772abc744..790f279b1206 100644 --- a/forms/source/inc/frm_strings.hxx +++ b/forms/source/inc/frm_strings.hxx @@ -53,6 +53,7 @@ namespace frm #define PROPERTY_RELEVANT "Relevant" #define PROPERTY_ISREADONLY "IsReadOnly" #define PROPERTY_FILTER "Filter" + #define PROPERTY_HAVINGCLAUSE "HavingClause" #define PROPERTY_WIDTH "Width" #define PROPERTY_SEARCHABLE "IsSearchable" #define PROPERTY_MULTILINE "MultiLine" diff --git a/forms/source/inc/property.hxx b/forms/source/inc/property.hxx index b5d879a9e7a6..ea2ffca31e5d 100644 --- a/forms/source/inc/property.hxx +++ b/forms/source/inc/property.hxx @@ -173,7 +173,7 @@ namespace frm #define PROPERTY_ID_AUTOINCREMENT (PROPERTY_ID_START +133) // UINT16 // free #define PROPERTY_ID_FILTER (PROPERTY_ID_START +135) // ::rtl::OUString - // free +#define PROPERTY_ID_HAVINGCLAUSE (PROPERTY_ID_START +136) // ::rtl::OUString #define PROPERTY_ID_QUERY (PROPERTY_ID_START +137) // ::rtl::OUString #define PROPERTY_ID_DEFAULT_LONG_VALUE (PROPERTY_ID_START +138) // Double #define PROPERTY_ID_DEFAULT_DATE (PROPERTY_ID_START +139) // UINT32 diff --git a/forms/source/runtime/formoperations.cxx b/forms/source/runtime/formoperations.cxx index 340b4518f7d9..e5f9df54af20 100644 --- a/forms/source/runtime/formoperations.cxx +++ b/forms/source/runtime/formoperations.cxx @@ -307,8 +307,10 @@ namespace frm case FormFeature::ToggleApplyFilter: { OUString sFilter; - m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sFilter; - if ( !sFilter.isEmpty() ) + OUString sHaving; + m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sFilter; + m_xCursorProperties->getPropertyValue( PROPERTY_HAVINGCLAUSE ) >>= sHaving; + if ( ! (sFilter.isEmpty() && sHaving.isEmpty()) ) { aState.State = m_xCursorProperties->getPropertyValue( PROPERTY_APPLYFILTER ); aState.Enabled = !impl_isInsertOnlyForm_throw(); @@ -718,13 +720,15 @@ namespace frm OSL_ENSURE( xProperties.is(), "FormOperations::execute: no multi property access!" ); if ( xProperties.is() ) { - Sequence< OUString > aNames( 2 ); + Sequence< OUString > aNames( 3 ); aNames[0] = PROPERTY_FILTER; - aNames[1] = PROPERTY_SORT; + aNames[1] = PROPERTY_HAVINGCLAUSE; + aNames[2] = PROPERTY_SORT; - Sequence< Any> aValues( 2 ); + Sequence< Any> aValues( 3 ); aValues[0] <<= OUString(); aValues[1] <<= OUString(); + aValues[2] <<= OUString(); WaitObject aWO( nullptr ); xProperties->setPropertyValues( aNames, aValues ); @@ -1034,6 +1038,11 @@ namespace frm if ( m_xParser->getFilter() != sNewValue ) m_xParser->setFilter( sNewValue ); } + else if ( _rEvent.PropertyName == PROPERTY_HAVINGCLAUSE ) + { + if ( m_xParser->getHavingClause() != sNewValue ) + m_xParser->setHavingClause( sNewValue ); + } else if ( _rEvent.PropertyName == PROPERTY_SORT ) { _rEvent.NewValue >>= sNewValue; @@ -1221,14 +1230,17 @@ namespace frm { OUString sStatement; OUString sFilter; + OUString sHaving; OUString sSort; m_xCursorProperties->getPropertyValue( PROPERTY_ACTIVECOMMAND ) >>= sStatement; m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sFilter; + m_xCursorProperties->getPropertyValue( PROPERTY_HAVINGCLAUSE ) >>= sHaving; m_xCursorProperties->getPropertyValue( PROPERTY_SORT ) >>= sSort; m_xParser->setElementaryQuery( sStatement ); m_xParser->setFilter ( sFilter ); + m_xParser->setHavingClause ( sHaving ); m_xParser->setOrder ( sSort ); } @@ -1236,6 +1248,7 @@ namespace frm // we can keep our parser in sync m_xCursorProperties->addPropertyChangeListener( PROPERTY_ACTIVECOMMAND, this ); m_xCursorProperties->addPropertyChangeListener( PROPERTY_FILTER, this ); + m_xCursorProperties->addPropertyChangeListener( PROPERTY_HAVINGCLAUSE, this ); m_xCursorProperties->addPropertyChangeListener( PROPERTY_SORT, this ); } } @@ -1257,6 +1270,7 @@ namespace frm if ( m_xParser.is() && m_xCursorProperties.is() ) { m_xCursorProperties->removePropertyChangeListener( PROPERTY_FILTER, this ); + m_xCursorProperties->removePropertyChangeListener( PROPERTY_HAVINGCLAUSE, this ); m_xCursorProperties->removePropertyChangeListener( PROPERTY_ACTIVECOMMAND, this ); m_xCursorProperties->removePropertyChangeListener( PROPERTY_SORT, this ); } @@ -1337,7 +1351,9 @@ namespace frm bool FormOperations::impl_hasFilterOrOrder_throw() const { - return impl_isParseable_throw() && ( !m_xParser->getFilter().isEmpty() || !m_xParser->getOrder().isEmpty() ); + return impl_isParseable_throw() && ( !m_xParser->getFilter().isEmpty() || + !m_xParser->getHavingClause().isEmpty() || + !m_xParser->getOrder().isEmpty() ); } @@ -1600,21 +1616,27 @@ namespace frm return; OUString sOriginalFilter; - m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sOriginalFilter; + OUString sOriginalHaving; + m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sOriginalFilter; + m_xCursorProperties->getPropertyValue( PROPERTY_HAVINGCLAUSE ) >>= sOriginalHaving; bool bApplied = true; m_xCursorProperties->getPropertyValue( PROPERTY_APPLYFILTER ) >>= bApplied; // if we have a filter, but it's not applied, then we have to overwrite it, else append one if ( !bApplied ) + { m_xParser->setFilter( OUString() ); + m_xParser->setHavingClause( OUString() ); + } - impl_appendFilterByColumn_throw aAction(this, xBoundField); + impl_appendFilterByColumn_throw aAction(this, m_xParser, xBoundField); impl_doActionInSQLContext_throw( aAction, RID_STR_COULD_NOT_SET_FILTER ); WaitObject aWO( nullptr ); try { - m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, makeAny( m_xParser->getFilter() ) ); + m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, makeAny( m_xParser->getFilter() ) ); + m_xCursorProperties->setPropertyValue( PROPERTY_HAVINGCLAUSE, makeAny( m_xParser->getHavingClause() ) ); m_xCursorProperties->setPropertyValue( PROPERTY_APPLYFILTER, makeAny( true ) ); m_xLoadableForm->reload(); @@ -1629,9 +1651,11 @@ namespace frm { // something went wrong -> restore the original state try { - m_xParser->setOrder( sOriginalFilter ); + m_xParser->setFilter ( sOriginalFilter ); + m_xParser->setHavingClause( sOriginalHaving ); m_xCursorProperties->setPropertyValue( PROPERTY_APPLYFILTER, makeAny( bApplied ) ); - m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, makeAny( m_xParser->getFilter() ) ); + m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, makeAny( m_xParser->getFilter() ) ); + m_xCursorProperties->setPropertyValue( PROPERTY_HAVINGCLAUSE, makeAny( m_xParser->getHavingClause() ) ); m_xLoadableForm->reload(); } catch( const Exception& ) @@ -1678,7 +1702,10 @@ namespace frm { WaitObject aWO( nullptr ); if ( _bFilter ) - m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, makeAny( m_xParser->getFilter() ) ); + { + m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, makeAny( m_xParser->getFilter() ) ); + m_xCursorProperties->setPropertyValue( PROPERTY_HAVINGCLAUSE, makeAny( m_xParser->getHavingClause() ) ); + } else m_xCursorProperties->setPropertyValue( PROPERTY_SORT, makeAny( m_xParser->getOrder() ) ); m_xLoadableForm->reload(); diff --git a/forms/source/runtime/formoperations.hxx b/forms/source/runtime/formoperations.hxx index 425e789f8419..ad1494fecf64 100644 --- a/forms/source/runtime/formoperations.hxx +++ b/forms/source/runtime/formoperations.hxx @@ -25,8 +25,10 @@ #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/form/XForm.hpp> #include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> #include <com/sun/star/form/XLoadable.hpp> #include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> #include <com/sun/star/util/XModifyListener.hpp> #include <com/sun/star/container/XIndexAccess.hpp> #include <com/sun/star/lang/XInitialization.hpp> @@ -35,7 +37,7 @@ #include <cppuhelper/basemutex.hxx> #include <cppuhelper/compbase.hxx> - +#include <connectivity/dbtools.hxx> namespace frm { @@ -320,14 +322,22 @@ namespace frm { public: impl_appendFilterByColumn_throw(const FormOperations *pFO, + css::uno::Reference< css::sdb::XSingleSelectQueryComposer > const & xParser, css::uno::Reference< css::beans::XPropertySet > const & xField) : m_pFO(pFO) + , m_xParser(xParser) , m_xField(xField) {}; - void operator()() { m_pFO->m_xParser->appendFilterByColumn( m_xField, true, css::sdb::SQLFilterOperator::EQUAL ); } + void operator()() { + if (dbtools::isAggregateColumn( m_xParser, m_xField )) + m_pFO->m_xParser->appendHavingClauseByColumn( m_xField, true, css::sdb::SQLFilterOperator::EQUAL ); + else + m_pFO->m_xParser->appendFilterByColumn( m_xField, true, css::sdb::SQLFilterOperator::EQUAL ); + } private: const FormOperations *m_pFO; + css::uno::Reference< css::sdb::XSingleSelectQueryComposer > m_xParser; css::uno::Reference< css::beans::XPropertySet > m_xField; }; diff --git a/include/connectivity/dbtools.hxx b/include/connectivity/dbtools.hxx index f771ae9f3fbe..e0bf5ca8f022 100644 --- a/include/connectivity/dbtools.hxx +++ b/include/connectivity/dbtools.hxx @@ -20,6 +20,7 @@ #ifndef INCLUDED_CONNECTIVITY_DBTOOLS_HXX #define INCLUDED_CONNECTIVITY_DBTOOLS_HXX +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> #include <connectivity/dbexception.hxx> #include <comphelper/types.hxx> #include <com/sun/star/sdbc/DataType.hpp> @@ -787,6 +788,44 @@ namespace dbtools OUStringBuffer& _out_rSQLPredicate ); + /** is this field an aggregate? + + @param _xComposer + a query composer that knows the field by name + @param _xField + the field + @param whenNotFound + value returned when _sName is not known by _xComposer + */ + OOO_DLLPUBLIC_DBTOOLS bool isAggregateColumn( + const css::uno::Reference< css::sdb::XSingleSelectQueryComposer > &_xComposer, + const css::uno::Reference< css::beans::XPropertySet > &_xField, + bool whenNotFound = false + ); + + /** is this column an aggregate? + + @param _xColumns collection of columns + look for column sName in there + @param _sName + name of the column + @param whenNotFound + value returned when _sName is not in _xColumns + */ + OOO_DLLPUBLIC_DBTOOLS bool isAggregateColumn( + const css::uno::Reference< css::container::XNameAccess > &_xColumns, + const OUString &_sName, + bool whenNotFound = false + ); + + /** is this column an aggregate? + + @param _xColumn + */ + OOO_DLLPUBLIC_DBTOOLS bool isAggregateColumn( + const css::uno::Reference< css::beans::XPropertySet > &_xColumn + ); + } // namespace dbtools namespace connectivity diff --git a/include/connectivity/filtermanager.hxx b/include/connectivity/filtermanager.hxx index 41df1c61b9f0..45b58a6183f9 100644 --- a/include/connectivity/filtermanager.hxx +++ b/include/connectivity/filtermanager.hxx @@ -61,14 +61,18 @@ namespace dbtools enum class FilterComponent { PublicFilter, // The filter which is to be published as "Filter" property of the database component. - LinkFilter // The filter part which is implicitly created for a database component when connecting + LinkFilter, // The filter part which is implicitly created for a database component when connecting // master and detail database components via column names. + PublicHaving, // the same, but should go in HAVING clause instead of WHERE clause + LinkHaving }; private: css::uno::Reference< css::beans::XPropertySet > m_xComponentAggregate; OUString m_aPublicFilterComponent; + OUString m_aPublicHavingComponent; OUString m_aLinkFilterComponent; + OUString m_aLinkHavingComponent; bool m_bApplyPublicFilter; public: @@ -85,19 +89,21 @@ namespace dbtools void setFilterComponent( FilterComponent _eWhich, const OUString& _rComponent ); bool isApplyPublicFilter( ) const { return m_bApplyPublicFilter; } - void setApplyPublicFilter( bool _bApply ); + void setApplyPublicFilter( bool _bApply ); private: /** retrieves a filter which is a conjunction of all single filter components */ OUString getComposedFilter( ) const; + OUString getComposedHaving( ) const; /** appends one filter component to the statement in our composer */ static void appendFilterComponent( OUStringBuffer& io_appendTo, const OUString& i_component ); /// checks whether there is only one (or even no) non-empty filter component - bool isThereAtMostOneComponent( OUString& o_singleComponent ) const; + bool isThereAtMostOneFilterComponent( OUString& o_singleComponent ) const; + bool isThereAtMostOneHavingComponent( OUString& o_singleComponent ) const; }; diff --git a/include/connectivity/parameters.hxx b/include/connectivity/parameters.hxx index 49eebb93665a..e5ba530bfcc4 100644 --- a/include/connectivity/parameters.hxx +++ b/include/connectivity/parameters.hxx @@ -296,13 +296,19 @@ namespace dbtools the detail part denotes a column name. In such a case, an additional filter needs to be created, containing a new parameter. + @param _out_rAdditionalHavingComponents + the additional having clause components which are required for master-detail relationships where + the detail part denotes a column name. In such a case, an additional filter needs to be created, + containing a new parameter. + @precond <member>m_aMasterFields</member> and <member>m_aDetailFields</member> have the same length */ void classifyLinks( const css::uno::Reference< css::container::XNameAccess >& _rxParentColumns, const css::uno::Reference< css::container::XNameAccess >& _rxColumns, - ::std::vector< OUString >& _out_rAdditionalFilterComponents + ::std::vector< OUString >& _out_rAdditionalFilterComponents, + ::std::vector< OUString >& _out_rAdditionalHavingComponents ); /** finalizes our <member>m_pOuterParameters</member> so that it can be used for |