]> granicus.if.org Git - icinga2/commitdiff
Fix grouping for Livestatus queries with 'Stats' 5503/head
authorGunnar Beutner <gunnar.beutner@icinga.com>
Mon, 14 Aug 2017 13:30:06 +0000 (15:30 +0200)
committerGunnar Beutner <gunnar.beutner@icinga.com>
Mon, 14 Aug 2017 13:30:06 +0000 (15:30 +0200)
refs #5078

19 files changed:
lib/livestatus/aggregator.cpp
lib/livestatus/aggregator.hpp
lib/livestatus/avgaggregator.cpp
lib/livestatus/avgaggregator.hpp
lib/livestatus/countaggregator.cpp
lib/livestatus/countaggregator.hpp
lib/livestatus/invavgaggregator.cpp
lib/livestatus/invavgaggregator.hpp
lib/livestatus/invsumaggregator.cpp
lib/livestatus/invsumaggregator.hpp
lib/livestatus/livestatusquery.cpp
lib/livestatus/maxaggregator.cpp
lib/livestatus/maxaggregator.hpp
lib/livestatus/minaggregator.cpp
lib/livestatus/minaggregator.hpp
lib/livestatus/stdaggregator.cpp
lib/livestatus/stdaggregator.hpp
lib/livestatus/sumaggregator.cpp
lib/livestatus/sumaggregator.hpp

index 4976b0f64fcde2adb36c5e7f15f92cf481c67d36..4c9e31fa82a690b480a86847032fe91fbbf8c8fa 100644 (file)
@@ -33,3 +33,6 @@ Filter::Ptr Aggregator::GetFilter(void) const
 {
        return m_Filter;
 }
+
+AggregatorState::~AggregatorState(void)
+{ }
index 9006d528b6c39094dc2081614f0385e3233dcc0d..36fb792fe020c07fbd7cbf1aaa7e5ee3931c4599 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct I2_LIVESTATUS_API AggregatorState
+{
+       virtual ~AggregatorState(void);
+};
+
 /**
  * @ingroup livestatus
  */
@@ -35,8 +43,8 @@ class I2_LIVESTATUS_API Aggregator : public Object
 public:
        DECLARE_PTR_TYPEDEFS(Aggregator);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) = 0;
-       virtual double GetResult(void) const = 0;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) = 0;
+       virtual double GetResultAndFreeState(AggregatorState *state) const = 0;
        void SetFilter(const Filter::Ptr& filter);
 
 protected:
index 270150e1c4c353e578ec7b776a9722f29069a899..bfe5e909518a569e7a2a0885d28147efdf27f46f 100644 (file)
 using namespace icinga;
 
 AvgAggregator::AvgAggregator(const String& attr)
-    : m_Avg(0), m_AvgCount(0), m_AvgAttr(attr)
+    : m_AvgAttr(attr)
 { }
 
-void AvgAggregator::Apply(const Table::Ptr& table, const Value& row)
+AvgAggregatorState *AvgAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new AvgAggregatorState();
+
+       return static_cast<AvgAggregatorState *>(*state);
+}
+
+void AvgAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
        Column column = table->GetColumn(m_AvgAttr);
 
        Value value = column.ExtractValue(row);
 
-       m_Avg += value;
-       m_AvgCount++;
+       AvgAggregatorState *pstate = EnsureState(state);
+
+       pstate->Avg += value;
+       pstate->AvgCount++;
 }
 
-double AvgAggregator::GetResult(void) const
+double AvgAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       return (m_Avg / m_AvgCount);
+       AvgAggregatorState *pstate = EnsureState(&state);
+       double result = pstate->Avg / pstate->AvgCount;
+       delete pstate;
+
+       return result;
 }
index 4f19512c38f878836e526c9888ef62f1606dd2d3..a16d3cd58190531e4a0b3fe18e973c6d8fbe8c5e 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct AvgAggregatorState : public AggregatorState
+{
+       AvgAggregatorState(void)
+           : Avg(0), AvgCount(0)
+       { }
+
+       double Avg;
+       double AvgCount;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -36,13 +49,13 @@ public:
 
        AvgAggregator(const String& attr);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
 private:
-       double m_Avg;
-       double m_AvgCount;
        String m_AvgAttr;
+
+       static AvgAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }
index 469b44195b8660a2e672301a4d9aaf1f052de9a0..616a7366a0308a847905a2fb30fa7f15b676b60c 100644 (file)
 
 using namespace icinga;
 
-CountAggregator::CountAggregator(void)
-    : m_Count(0)
-{ }
+CountAggregatorState *CountAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new CountAggregatorState();
+
+       return static_cast<CountAggregatorState *>(*state);
+}
 
-void CountAggregator::Apply(const Table::Ptr& table, const Value& row)
+void CountAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
+       CountAggregatorState *pstate = EnsureState(state);
+
        if (GetFilter()->Apply(table, row))
-               m_Count++;
+               pstate->Count++;
 }
 
-double CountAggregator::GetResult(void) const
+double CountAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       return m_Count;
+       CountAggregatorState *pstate = EnsureState(&state);
+       double result = pstate->Count;
+       delete pstate;
+
+       return result;
 }
index b03c86d32eac28fb70e323d49bce111626a5fd7f..df022e8aa44282e6a4ec0cab097463c946009111 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct CountAggregatorState : public AggregatorState
+{
+       CountAggregatorState(void)
+           : Count(0)
+       { }
+
+       int Count;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -34,13 +46,11 @@ class I2_LIVESTATUS_API CountAggregator : public Aggregator
 public:
        DECLARE_PTR_TYPEDEFS(CountAggregator);
 
-       CountAggregator(void);
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
-       
 private:
-       int m_Count;
+       static CountAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }
index 1798e2c7b3d1b01c1fbba199c0b0007b9f939ad5..30f3b5df59de0a1ef08de2236c860359345b7716 100644 (file)
 using namespace icinga;
 
 InvAvgAggregator::InvAvgAggregator(const String& attr)
-    : m_InvAvg(0), m_InvAvgCount(0), m_InvAvgAttr(attr)
+    : m_InvAvgAttr(attr)
 { }
 
-void InvAvgAggregator::Apply(const Table::Ptr& table, const Value& row)
+InvAvgAggregatorState *InvAvgAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new InvAvgAggregatorState();
+
+       return static_cast<InvAvgAggregatorState *>(*state);
+}
+
+void InvAvgAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
        Column column = table->GetColumn(m_InvAvgAttr);
 
        Value value = column.ExtractValue(row);
 
-       m_InvAvg += (1.0 / value);
-       m_InvAvgCount++;
+       InvAvgAggregatorState *pstate = EnsureState(state);
+
+       pstate->InvAvg += (1.0 / value);
+       pstate->InvAvgCount++;
 }
 
-double InvAvgAggregator::GetResult(void) const
+double InvAvgAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       return (m_InvAvg / m_InvAvgCount);
+       InvAvgAggregatorState *pstate = EnsureState(&state);
+       double result = pstate->InvAvg / pstate->InvAvgCount;
+       delete pstate;
+
+       return result;
 }
index 8e76b374dae97ca77808cb1e18df24581e65b8db..ff78e1500cbdf9ae97765d9fa7272bfd6af0c116 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct InvAvgAggregatorState : public AggregatorState
+{
+       InvAvgAggregatorState(void)
+           : InvAvg(0), InvAvgCount(0)
+       { }
+
+       double InvAvg;
+       double InvAvgCount;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -36,13 +49,13 @@ public:
 
        InvAvgAggregator(const String& attr);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
 private:
-       double m_InvAvg;
-       double m_InvAvgCount;
        String m_InvAvgAttr;
+
+       static InvAvgAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }
index e64b944b9023e991082e4d46fef943fed0467029..d8daddb049a5d66270835932ad6394503b38844c 100644 (file)
 using namespace icinga;
 
 InvSumAggregator::InvSumAggregator(const String& attr)
-    : m_InvSum(0), m_InvSumAttr(attr)
+    : m_InvSumAttr(attr)
 { }
 
-void InvSumAggregator::Apply(const Table::Ptr& table, const Value& row)
+InvSumAggregatorState *InvSumAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new InvSumAggregatorState();
+
+       return static_cast<InvSumAggregatorState *>(*state);
+}
+
+void InvSumAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
        Column column = table->GetColumn(m_InvSumAttr);
 
        Value value = column.ExtractValue(row);
 
-       m_InvSum += (1.0 / value);
+       InvSumAggregatorState *pstate = EnsureState(state);
+
+       pstate->InvSum += (1.0 / value);
 }
 
-double InvSumAggregator::GetResult(void) const
+double InvSumAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       return m_InvSum;
+       InvSumAggregatorState *pstate = EnsureState(&state);
+       double result = pstate->InvSum;
+       delete pstate;
+
+       return result;
 }
index 53669c87dcd885fe3e4882093726827f3958e760..166108a6db97cea4208c4a29508cee5778ac928b 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct I2_LIVESTATUS_API InvSumAggregatorState : public AggregatorState
+{
+       InvSumAggregatorState(void)
+           : InvSum(0)
+       { }
+
+       double InvSum;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -36,12 +48,13 @@ public:
 
        InvSumAggregator(const String& attr);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
 private:
-       double m_InvSum;
        String m_InvSumAttr;
+
+       static InvSumAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }
index ed92f959cfb0e73fe1fdfb6074d343ef43378f5c..2def592aed50811ba35846d2185d5ea4cf532477 100644 (file)
@@ -512,17 +512,34 @@ void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
                        AppendResultRow(result, row, first_row);
                }
        } else {
-               std::vector<double> stats(m_Aggregators.size(), 0);
-               int index = 0;
+               std::map<std::vector<Value>, std::vector<AggregatorState *> > allStats;
 
                /* add aggregated stats */
-               for (const Aggregator::Ptr aggregator : m_Aggregators) {
-                       for (const LivestatusRowValue& object : objects) {
-                               aggregator->Apply(table, object.Row);
+               for (const LivestatusRowValue& object : objects) {
+                       Column column = table->GetColumn(m_Columns[0]);
+
+                       std::vector<Value> statsKey;
+
+                       for (const String& columnName : m_Columns) {
+                               Column column = table->GetColumn(columnName);
+                               statsKey.push_back(column.ExtractValue(object.Row, object.GroupByType, object.GroupByObject));
                        }
 
-                       stats[index] = aggregator->GetResult();
-                       index++;
+                       auto it = allStats.find(statsKey);
+
+                       if (it == allStats.end()) {
+                               std::vector<AggregatorState *> newStats(m_Aggregators.size(), NULL);
+                               it = allStats.insert(std::make_pair(statsKey, newStats)).first;
+                       }
+
+                       auto& stats = it->second;
+
+                       int index = 0;
+
+                       for (const Aggregator::Ptr aggregator : m_Aggregators) {
+                               aggregator->Apply(table, object.Row, &stats[index]);
+                               index++;
+                       }
                }
 
                /* add column headers both for raw and aggregated data */
@@ -540,28 +557,22 @@ void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
                        AppendResultRow(result, header, first_row);
                }
 
-               Array::Ptr row = new Array();
-
-               row->Reserve(m_Columns.size() + m_Aggregators.size());
-
-               /*
-                * add selected columns next to stats
-                * may not be accurate for grouping!
-                */
-               if (objects.size() > 0 && m_Columns.size() > 0) {
-                       for (const String& columnName : m_Columns) {
-                               Column column = table->GetColumn(columnName);
+               for (const auto& kv : allStats) {
+                       Array::Ptr row = new Array();
 
-                               LivestatusRowValue object = objects[0]; //first object wins
+                       row->Reserve(m_Columns.size() + m_Aggregators.size());
 
-                               row->Add(column.ExtractValue(object.Row, object.GroupByType, object.GroupByObject));
+                       for (const Value& keyPart : kv.first) {
+                               row->Add(keyPart);
                        }
-               }
 
-               for (size_t i = 0; i < m_Aggregators.size(); i++)
-                       row->Add(stats[i]);
+                       auto& stats = kv.second;
+
+                       for (size_t i = 0; i < m_Aggregators.size(); i++)
+                               row->Add(m_Aggregators[i]->GetResultAndFreeState(stats[i]));
 
-               AppendResultRow(result, row, first_row);
+                       AppendResultRow(result, row, first_row);
+               }
        }
 
        EndResultSet(result);
index 6cf557aae6f55ee7a0a5b533323b74fabdd3f3d8..e3c6f1e65558233243d8fd37ce31b1a16aed3c13 100644 (file)
 using namespace icinga;
 
 MaxAggregator::MaxAggregator(const String& attr)
-    : m_Max(0), m_MaxAttr(attr)
+    : m_MaxAttr(attr)
 { }
 
-void MaxAggregator::Apply(const Table::Ptr& table, const Value& row)
+MaxAggregatorState *MaxAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new MaxAggregatorState();
+
+       return static_cast<MaxAggregatorState *>(*state);
+}
+
+void MaxAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
        Column column = table->GetColumn(m_MaxAttr);
 
        Value value = column.ExtractValue(row);
 
-       if (value > m_Max)
-               m_Max = value;
+       MaxAggregatorState *pstate = EnsureState(state);
+
+       if (value > pstate->Max)
+               pstate->Max = value;
 }
 
-double MaxAggregator::GetResult(void) const
+double MaxAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       return m_Max;
+       MaxAggregatorState *pstate = EnsureState(&state);
+       double result = pstate->Max;
+       delete pstate;
+
+       return result;
 }
index 3e12980d43be7d109edbd5cac600a2738c435490..fb9188886e08835809549015e74ad3c9127a5785 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct I2_LIVESTATUS_API MaxAggregatorState : public AggregatorState
+{
+       MaxAggregatorState(void)
+           : Max(0)
+       { }
+
+       double Max;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -36,12 +48,13 @@ public:
 
        MaxAggregator(const String& attr);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
 private:
-       double m_Max;
        String m_MaxAttr;
+
+       static MaxAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }
index 69fc38bad2bee13ef4ccdbcdfbc42375ff51fc9a..a2e91748a1c7a72bc2693e345402ebad4b96df26 100644 (file)
 using namespace icinga;
 
 MinAggregator::MinAggregator(const String& attr)
-    : m_Min(DBL_MAX), m_MinAttr(attr)
+    : m_MinAttr(attr)
 { }
 
-void MinAggregator::Apply(const Table::Ptr& table, const Value& row)
+MinAggregatorState *MinAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new MinAggregatorState();
+
+       return static_cast<MinAggregatorState *>(*state);
+}
+
+void MinAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
        Column column = table->GetColumn(m_MinAttr);
 
        Value value = column.ExtractValue(row);
 
-       if (value < m_Min)
-               m_Min = value;
+       MinAggregatorState *pstate = EnsureState(state);
+
+       if (value < pstate->Min)
+               pstate->Min = value;
 }
 
-double MinAggregator::GetResult(void) const
+double MinAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       if (m_Min == DBL_MAX)
-               return 0;
+       MinAggregatorState *pstate = EnsureState(&state);
+
+       double result;
+
+       if (pstate->Min == DBL_MAX)
+               result = 0;
        else
-               return m_Min;
+               result = pstate->Min;
+
+       delete pstate;
+
+       return result;
 }
index 6e5c07cb07246704c7bfa3f4c83d03a897d090f3..2ab8fd94a51d9ba8258666c289375110c3a4658c 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct I2_LIVESTATUS_API MinAggregatorState : public AggregatorState
+{
+       MinAggregatorState(void)
+           : Min(DBL_MAX)
+       { }
+
+       double Min;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -37,12 +49,13 @@ public:
 
        MinAggregator(const String& attr);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
 private:
-       double m_Min;
        String m_MinAttr;
+
+       static MinAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }
index ebc8ccf011eca6ed960318763de6e6075fa2de21..f7c6ad5679ce473a5a519e51b670ae9493502b1f 100644 (file)
 using namespace icinga;
 
 StdAggregator::StdAggregator(const String& attr)
-    : m_StdSum(0), m_StdQSum(0), m_StdCount(0), m_StdAttr(attr)
+    : m_StdAttr(attr)
 { }
 
-void StdAggregator::Apply(const Table::Ptr& table, const Value& row)
+StdAggregatorState *StdAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new StdAggregatorState();
+
+       return static_cast<StdAggregatorState *>(*state);
+}
+
+void StdAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
        Column column = table->GetColumn(m_StdAttr);
 
        Value value = column.ExtractValue(row);
 
-       m_StdSum += value;
-       m_StdQSum += pow(value, 2);
-       m_StdCount++;
+       StdAggregatorState *pstate = EnsureState(state);
+
+       pstate->StdSum += value;
+       pstate->StdQSum += pow(value, 2);
+       pstate->StdCount++;
 }
 
-double StdAggregator::GetResult(void) const
+double StdAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       return sqrt((m_StdQSum - (1 / m_StdCount) * pow(m_StdSum, 2)) / (m_StdCount - 1));
+       StdAggregatorState *pstate = EnsureState(&state);
+       double result = sqrt((pstate->StdQSum - (1 / pstate->StdCount) * pow(pstate->StdSum, 2)) / (pstate->StdCount - 1));
+       delete pstate;
+
+       return result;
 }
index 56ff36f696dbb21264be9ad4c9d7294cd8b7484e..b6ae7ce5530e9ca0059e984f90af259389998573 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct I2_LIVESTATUS_API StdAggregatorState : public AggregatorState
+{
+       StdAggregatorState(void)
+           : StdSum(0), StdQSum(0), StdCount(0)
+       { }
+
+       double StdSum;
+       double StdQSum;
+       double StdCount;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -36,14 +50,13 @@ public:
 
        StdAggregator(const String& attr);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
 private:
-       double m_StdSum;
-       double m_StdQSum;
-       double m_StdCount;
        String m_StdAttr;
+
+       static StdAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }
index 2ee1f502ffb8969b4528f6a7f52c3208e945f0b3..533daae3de9640df88c77f7ab15501b042430bc7 100644 (file)
 using namespace icinga;
 
 SumAggregator::SumAggregator(const String& attr)
-    : m_Sum(0), m_SumAttr(attr)
+    : m_SumAttr(attr)
 { }
 
-void SumAggregator::Apply(const Table::Ptr& table, const Value& row)
+SumAggregatorState *SumAggregator::EnsureState(AggregatorState **state)
+{
+       if (!*state)
+               *state = new SumAggregatorState();
+
+       return static_cast<SumAggregatorState *>(*state);
+}
+
+void SumAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state)
 {
        Column column = table->GetColumn(m_SumAttr);
 
        Value value = column.ExtractValue(row);
 
-       m_Sum += value;
+       SumAggregatorState *pstate = EnsureState(state);
+
+       pstate->Sum += value;
 }
 
-double SumAggregator::GetResult(void) const
+double SumAggregator::GetResultAndFreeState(AggregatorState *state) const
 {
-       return m_Sum;
+       SumAggregatorState *pstate = EnsureState(&state);
+       double result = pstate->Sum;
+       delete pstate;
+
+       return result;
 }
index ecbaaee3b66226ae9782d352feec6fc5c036d2c2..42500c0509444c6ce349597963eda3ec23418c27 100644 (file)
 namespace icinga
 {
 
+/**
+ * @ingroup livestatus
+ */
+struct I2_LIVESTATUS_API SumAggregatorState : public AggregatorState
+{
+       SumAggregatorState(void)
+           : Sum(0)
+       { }
+
+       double Sum;
+};
+
 /**
  * @ingroup livestatus
  */
@@ -36,12 +48,13 @@ public:
 
        SumAggregator(const String& attr);
 
-       virtual void Apply(const Table::Ptr& table, const Value& row) override;
-       virtual double GetResult(void) const override;
+       virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override;
+       virtual double GetResultAndFreeState(AggregatorState *state) const override;
 
 private:
-       double m_Sum;
        String m_SumAttr;
+
+       static SumAggregatorState *EnsureState(AggregatorState **state);
 };
 
 }