diff options
-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 |