diff --git a/components/livestatus/query.cpp b/components/livestatus/query.cpp index 7c90b0470..38762468a 100644 --- a/components/livestatus/query.cpp +++ b/components/livestatus/query.cpp @@ -65,7 +65,7 @@ Query::Query(const vector& lines) m_Columns = params.Split(is_any_of(" ")); else if (header == "ColumnHeaders") m_ColumnHeaders = (params == "on"); - else if (header == "Filter") { + else if (header == "Filter" || header == "Stats") { vector tokens = params.Split(is_any_of(" ")); if (tokens.size() < 3) { @@ -97,29 +97,46 @@ Query::Query(const vector& lines) if (negate) filter = boost::make_shared(filter); - filters.push_back(filter); + deque& deq = (header == "Filter") ? filters : stats; + deq.push_back(filter); } else if (header == "Or" || header == "And") { + deque& deq = (header == "Or" || header == "And") ? filters : stats; + int num = Convert::ToLong(params); CombinerFilter::Ptr filter; - if (header == "Or") + if (header == "Or" || header == "StatsOr") filter = boost::make_shared(); else filter = boost::make_shared(); - while (!filters.empty() && num--) { - filter->AddSubFilter(filters.back()); - filters.pop_back(); + if (num > deq.size()) { + m_Verb = "ERROR"; + m_ErrorCode = 451; + m_ErrorMessage = "Or/StatsOr is referencing " + Convert::ToString(num) + " filters; stack only contains " + Convert::ToString(deq.size()) + " filters"; + return; } - filters.push_back(filter); - } else if (header == "Negate") { - if (!filters.empty()) { - Filter::Ptr filter = filters.back(); - filters.pop_back(); - - filters.push_back(boost::make_shared(filter)); + while (num--) { + filter->AddSubFilter(deq.back()); + deq.pop_back(); } + + deq.push_back(filter); + } else if (header == "Negate" || header == "StatsNegate") { + deque& deq = (header == "Negate") ? filters : stats; + + if (deq.empty()) { + m_Verb = "ERROR"; + m_ErrorCode = 451; + m_ErrorMessage = "Negate/StatsNegate used, however the filter stack is empty"; + return; + } + + Filter::Ptr filter = deq.back(); + filters.pop_back(); + + deq.push_back(boost::make_shared(filter)); } } @@ -196,22 +213,44 @@ void Query::ExecuteGetHelper(const Stream::Ptr& stream) Array::Ptr rs = boost::make_shared(); - BOOST_FOREACH(const Object::Ptr& object, objects) { - Array::Ptr row = boost::make_shared(); + if (m_Stats.empty()) { + BOOST_FOREACH(const Object::Ptr& object, objects) { + Array::Ptr row = boost::make_shared(); - BOOST_FOREACH(const String& column, columns) { - Table::ColumnAccessor accessor = table->GetColumn(column); + BOOST_FOREACH(const String& column, columns) { + Table::ColumnAccessor accessor = table->GetColumn(column); - if (accessor.empty()) { - SendResponse(stream, 450, "Column '" + column + "' does not exist."); + if (accessor.empty()) { + SendResponse(stream, 450, "Column '" + column + "' does not exist."); - return; + return; + } + + row->Add(accessor(object)); } - row->Add(accessor(object)); + rs->Add(row); + } + } else { + vector stats(m_Stats.size(), 0); + + BOOST_FOREACH(const Object::Ptr& object, objects) { + int index = 0; + BOOST_FOREACH(const Filter::Ptr filter, m_Stats) { + if (filter->Apply(table, object)) + stats[index]++; + + index++; + } } + Array::Ptr row = boost::make_shared(); + for (int i = 0; i < m_Stats.size(); i++) + row->Add(stats[i]); + rs->Add(row); + + m_ColumnHeaders = false; } stringstream result; @@ -229,7 +268,7 @@ void Query::ExecuteCommandHelper(const Stream::Ptr& stream) void Query::ExecuteErrorHelper(const Stream::Ptr& stream) { - PrintFixed16(stream, m_ErrorCode, m_ErrorMessage); + SendResponse(stream, m_ErrorCode, m_ErrorMessage); } void Query::SendResponse(const Stream::Ptr& stream, int code, const String& data)