diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 466fe9784c..6e0f0bdd97 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -9148,14 +9148,13 @@ WindowClause* WindowClause::dsqlPass(DsqlCompilerScratch* dsqlScratch) doDsqlPass(dsqlScratch, extent ? extent : window->extent), exclusion ? exclusion : window->exclusion); - if (node->order && node->extent && node->extent->unit == FrameExtent::Unit::RANGE && + if (node->extent && node->extent->unit == FrameExtent::Unit::RANGE && (node->extent->frame1->value || (node->extent->frame2 && node->extent->frame2->value))) { - if (node->order->items.getCount() > 1) - { - status_exception::raise( - Arg::Gds(isc_dsql_window_range_multi_key)); - } + if (!node->order) + status_exception::raise(Arg::Gds(isc_dsql_window_range_inv_key_type)); + else if (node->order->items.getCount() > 1) + status_exception::raise(Arg::Gds(isc_dsql_window_range_multi_key)); else { OrderNode* key = nodeAs(node->order->items[0]); @@ -9165,10 +9164,7 @@ WindowClause* WindowClause::dsqlPass(DsqlCompilerScratch* dsqlScratch) DsqlDescMaker::fromNode(dsqlScratch, &desc, key->value); if (!desc.isDateTime() && !desc.isNumeric()) - { - status_exception::raise( - Arg::Gds(isc_dsql_window_range_inv_key_type)); - } + status_exception::raise(Arg::Gds(isc_dsql_window_range_inv_key_type)); } } diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index b3fe0cb0a1..8272bfa62f 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -822,7 +822,7 @@ Data source : @4"}, /* eds_statement */ {335545115, "Cannot open cursor for non-SELECT statement"}, /* no_cursor */ {335545116, "If specifies @1, then shall not specify @2"}, /* dsql_window_incompat_frames */ {335545117, "RANGE based window with {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value"}, /* dsql_window_range_multi_key */ - {335545118, "RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types"}, /* dsql_window_range_inv_key_type */ + {335545118, "RANGE based window with PRECEDING/FOLLOWING must have a single ORDER BY key of numerical, date, time or timestamp types"}, /* dsql_window_range_inv_key_type */ {335545119, "Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type"}, /* dsql_window_frame_value_inv_type */ {335545120, "Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative"}, /* window_frame_value_invalid */ {335545121, "Window @1 not found"}, /* dsql_window_not_found */ diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 7c4897822b..ff59d6f275 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -790,6 +790,10 @@ namespace Jrd class WindowedStream : public RecordSource { public: + using Frame = WindowClause::Frame; + using FrameExtent = WindowClause::FrameExtent; + using Exclusion = WindowClause::Exclusion; + class WindowStream : public BaseAggWinStream { private: @@ -839,8 +843,8 @@ namespace Jrd WindowStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream, const NestValueArray* group, BaseBufferedStream* next, SortNode* order, MapNode* windowMap, - WindowClause::FrameExtent* frameExtent, - WindowClause::Exclusion exclusion); + FrameExtent* frameExtent, + Exclusion exclusion); public: void open(thread_db* tdbb) const; @@ -860,19 +864,19 @@ namespace Jrd private: const void getFrameValue(thread_db* tdbb, jrd_req* request, - const WindowClause::Frame* frame, impure_value_ex* impureValue) const; + const Frame* frame, impure_value_ex* impureValue) const; SINT64 locateFrameRange(thread_db* tdbb, jrd_req* request, Impure* impure, - const WindowClause::Frame* frame, const dsc* offsetDesc, SINT64 position) const; + const Frame* frame, const dsc* offsetDesc, SINT64 position) const; private: NestConst m_order; const MapNode* m_windowMap; - NestConst m_frameExtent; + NestConst m_frameExtent; Firebird::Array > m_arithNodes; NestValueArray m_aggSources, m_aggTargets; NestValueArray m_winPassSources, m_winPassTargets; - WindowClause::Exclusion m_exclusion; + Exclusion m_exclusion; UCHAR m_invariantOffsets; // 0x1 | 0x2 bitmask }; diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index d4e9412c03..02c9ef3b99 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -191,35 +191,27 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, // Process the unpartioned and unordered map, if existent. - for (ObjectsArray::iterator window = windows.begin(); - window != windows.end(); - ++window) + for (auto& window : windows) { // While here, verify not supported functions/clauses. - if (window->order) + if (window.order || window.frameExtent->unit == FrameExtent::Unit::ROWS) { - const NestConst* source = window->map->sourceList.begin(); - - for (const NestConst* const end = window->map->sourceList.end(); - source != end; - ++source) + for (const auto& source : window.map->sourceList) { - const AggNode* aggNode = nodeAs(*source); - - if (aggNode) + if (const auto aggNode = nodeAs(source)) { - if (aggNode->distinct) - { - status_exception::raise( - Arg::Gds(isc_wish_list) << - Arg::Gds(isc_random) << "DISTINCT is not supported in ordered windows"); - } + const char* arg = nullptr; - if (!(aggNode->getCapabilities() & AggNode::CAP_SUPPORTS_WINDOW_FRAME)) + if (aggNode->distinct) + arg = "DISTINCT"; + else if (!(aggNode->getCapabilities() & AggNode::CAP_SUPPORTS_WINDOW_FRAME)) + arg = aggNode->aggInfo.name; + + if (arg) { string msg; - msg.printf("%s is not supported in ordered windows", aggNode->aggInfo.name); + msg.printf("%s is not supported in windows with ORDER BY or frame by ROWS clauses", arg); status_exception::raise( Arg::Gds(isc_wish_list) << @@ -229,15 +221,22 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, } } - if (!window->group && !window->order) + if (!window.group && !window.order) { - fb_assert(!m_joinedStream); + if (!m_joinedStream) + { + m_joinedStream = FB_NEW_POOL(csb->csb_pool) WindowStream(tdbb, csb, window.stream, + nullptr, FB_NEW_POOL(csb->csb_pool) BufferedStreamWindow(csb, m_next), + nullptr, window.map, window.frameExtent, window.exclusion); + } + else + { + m_joinedStream = FB_NEW_POOL(csb->csb_pool) WindowStream(tdbb, csb, window.stream, + nullptr, FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, m_joinedStream), + nullptr, window.map, window.frameExtent, window.exclusion); + } - m_joinedStream = FB_NEW_POOL(csb->csb_pool) WindowStream(tdbb, csb, window->stream, - NULL, FB_NEW_POOL(csb->csb_pool) BufferedStreamWindow(csb, m_next), - NULL, window->map, NULL, window->exclusion); - - OPT_gen_aggregate_distincts(tdbb, csb, window->map); + OPT_gen_aggregate_distincts(tdbb, csb, window.map); } } @@ -248,14 +247,8 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, StreamList streams; - for (ObjectsArray::iterator window = windows.begin(); - window != windows.end(); - ++window) + for (auto& window : windows) { - // Refresh the stream list based on the last m_joinedStream. - streams.clear(); - m_joinedStream->findUsedStreams(streams); - #if 0 //// FIXME: This causes problems, for example with FIRST_VALUE. //// I think it can be fixed with the help of SlidingWindow. @@ -263,10 +256,10 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, // between !{ following || unbounded preceding} and unbounded following if (window->frameExtent && - window->frameExtent->frame2->bound == WindowClause::Frame::Bound::FOLLOWING && + window->frameExtent->frame2->bound == Frame::Bound::FOLLOWING && !window->frameExtent->frame2->value && - !(window->frameExtent->frame1->bound == WindowClause::Frame::Bound::FOLLOWING || - (window->frameExtent->frame1->bound == WindowClause::Frame::Bound::PRECEDING && + !(window->frameExtent->frame1->bound == Frame::Bound::FOLLOWING || + (window->frameExtent->frame1->bound == Frame::Bound::PRECEDING && !window->frameExtent->frame1->value))) { if (window->order) @@ -286,14 +279,14 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, } } - WindowClause::Frame* temp = window->frameExtent->frame1; + Frame* temp = window->frameExtent->frame1; window->frameExtent->frame1 = window->frameExtent->frame2; window->frameExtent->frame2 = temp; - window->frameExtent->frame1->bound = WindowClause::Frame::Bound::PRECEDING; + window->frameExtent->frame1->bound = Frame::Bound::PRECEDING; - if (window->frameExtent->frame2->bound == WindowClause::Frame::Bound::PRECEDING) - window->frameExtent->frame2->bound = WindowClause::Frame::Bound::FOLLOWING; + if (window->frameExtent->frame2->bound == Frame::Bound::PRECEDING) + window->frameExtent->frame2->bound = Frame::Bound::FOLLOWING; } #endif @@ -301,34 +294,38 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, SortNode* windowOrder; - if (window->group) + if (window.group) { windowOrder = FB_NEW_POOL(csb->csb_pool) SortNode(csb->csb_pool); - windowOrder->expressions.join(window->group->expressions); - windowOrder->direction.join(window->group->direction); - windowOrder->nullOrder.join(window->group->nullOrder); + windowOrder->expressions.join(window.group->expressions); + windowOrder->direction.join(window.group->direction); + windowOrder->nullOrder.join(window.group->nullOrder); - if (window->order) + if (window.order) { - windowOrder->expressions.join(window->order->expressions); - windowOrder->direction.join(window->order->direction); - windowOrder->nullOrder.join(window->order->nullOrder); + windowOrder->expressions.join(window.order->expressions); + windowOrder->direction.join(window.order->direction); + windowOrder->nullOrder.join(window.order->nullOrder); } } else - windowOrder = window->order; + windowOrder = window.order; if (windowOrder) { - SortedStream* sortedStream = OPT_gen_sort(tdbb, csb, streams, NULL, + // Refresh the stream list based on the last m_joinedStream. + streams.clear(); + m_joinedStream->findUsedStreams(streams); + + SortedStream* sortedStream = OPT_gen_sort(tdbb, csb, streams, nullptr, m_joinedStream, windowOrder, false, false); - m_joinedStream = FB_NEW_POOL(csb->csb_pool) WindowStream(tdbb, csb, window->stream, - (window->group ? &window->group->expressions : NULL), + m_joinedStream = FB_NEW_POOL(csb->csb_pool) WindowStream(tdbb, csb, window.stream, + (window.group ? &window.group->expressions : nullptr), FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, sortedStream), - window->order, window->map, window->frameExtent, window->exclusion); + window.order, window.map, window.frameExtent, window.exclusion); - OPT_gen_aggregate_distincts(tdbb, csb, window->map); + OPT_gen_aggregate_distincts(tdbb, csb, window.map); } } } @@ -419,8 +416,8 @@ void WindowedStream::nullRecords(thread_db* tdbb) const WindowedStream::WindowStream::WindowStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream, const NestValueArray* group, BaseBufferedStream* next, SortNode* order, MapNode* windowMap, - WindowClause::FrameExtent* frameExtent, - WindowClause::Exclusion exclusion) + FrameExtent* frameExtent, + Exclusion exclusion) : BaseAggWinStream(tdbb, csb, stream, group, NULL, false, next), m_order(order), m_windowMap(windowMap), @@ -470,12 +467,12 @@ WindowedStream::WindowStream::WindowStream(thread_db* tdbb, CompilerScratch* csb for (unsigned i = 0; i < 2; ++i) { - WindowClause::Frame* frame = i == 0 ? + Frame* frame = i == 0 ? m_frameExtent->frame1 : m_frameExtent->frame2; - if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::RANGE && frame->value) + if (m_frameExtent->unit == FrameExtent::Unit::RANGE && frame->value) { - int direction = frame->bound == WindowClause::Frame::Bound::FOLLOWING ? 1 : -1; + int direction = frame->bound == Frame::Bound::FOLLOWING ? 1 : -1; if (m_order->direction[0] == ORDER_DESC) direction *= -1; @@ -605,37 +602,36 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const // Find the window start. - if (m_order && m_frameExtent->frame1->value && !(m_invariantOffsets & 0x1)) + if (m_frameExtent->frame1->value && !(m_invariantOffsets & 0x1)) getFrameValue(tdbb, request, m_frameExtent->frame1, &impure->startOffset); - if (!m_order) - impure->windowBlock.startPosition = impure->partitionBlock.startPosition; // {range | rows} between unbounded preceding and ... - else if (m_frameExtent->frame1->bound == WindowClause::Frame::Bound::PRECEDING && - !m_frameExtent->frame1->value) + // (no order by) range + if ((m_frameExtent->frame1->bound == Frame::Bound::PRECEDING && !m_frameExtent->frame1->value) || + (!m_order && m_frameExtent->unit == FrameExtent::Unit::RANGE)) { impure->windowBlock.startPosition = impure->partitionBlock.startPosition; } // rows between current row and ... - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::ROWS && - m_frameExtent->frame1->bound == WindowClause::Frame::Bound::CURRENT_ROW) + else if (m_frameExtent->unit == FrameExtent::Unit::ROWS && + m_frameExtent->frame1->bound == Frame::Bound::CURRENT_ROW) { impure->windowBlock.startPosition = position; } // rows between {preceding | following} and ... - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::ROWS && + else if (m_frameExtent->unit == FrameExtent::Unit::ROWS && m_frameExtent->frame1->value) { impure->windowBlock.startPosition = position + impure->startOffset.vlux_count; } // range between current row and ... - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::RANGE && - m_frameExtent->frame1->bound == WindowClause::Frame::Bound::CURRENT_ROW) + else if (m_frameExtent->unit == FrameExtent::Unit::RANGE && + m_frameExtent->frame1->bound == Frame::Bound::CURRENT_ROW) { impure->windowBlock.startPosition = position; } // range between {preceding | following} and ... - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::RANGE && + else if (m_frameExtent->unit == FrameExtent::Unit::RANGE && m_frameExtent->frame1->value) { impure->windowBlock.startPosition = locateFrameRange(tdbb, request, impure, @@ -649,32 +645,31 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const // Find the window end. - if (m_order && m_frameExtent->frame2->value && !(m_invariantOffsets & 0x2)) + if (m_frameExtent->frame2->value && !(m_invariantOffsets & 0x2)) getFrameValue(tdbb, request, m_frameExtent->frame2, &impure->endOffset); - if (!m_order) - impure->windowBlock.endPosition = impure->partitionBlock.endPosition; // {range | rows} between ... and unbounded following - else if (m_frameExtent->frame2->bound == WindowClause::Frame::Bound::FOLLOWING && - !m_frameExtent->frame2->value) + // (no order by) range + if ((m_frameExtent->frame2->bound == Frame::Bound::FOLLOWING && !m_frameExtent->frame2->value) || + (!m_order && m_frameExtent->unit == FrameExtent::Unit::RANGE)) { impure->windowBlock.endPosition = impure->partitionBlock.endPosition; } // rows between ... and current row - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::ROWS && - m_frameExtent->frame2->bound == WindowClause::Frame::Bound::CURRENT_ROW) + else if (m_frameExtent->unit == FrameExtent::Unit::ROWS && + m_frameExtent->frame2->bound == Frame::Bound::CURRENT_ROW) { impure->windowBlock.endPosition = position; } // rows between ... and {preceding | following} - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::ROWS && + else if (m_frameExtent->unit == FrameExtent::Unit::ROWS && m_frameExtent->frame2->value) { impure->windowBlock.endPosition = position + impure->endOffset.vlux_count; } // range between ... and current row - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::RANGE && - m_frameExtent->frame2->bound == WindowClause::Frame::Bound::CURRENT_ROW) + else if (m_frameExtent->unit == FrameExtent::Unit::RANGE && + m_frameExtent->frame2->bound == Frame::Bound::CURRENT_ROW) { SINT64 rangePos = position; cacheValues(tdbb, request, &m_order->expressions, impure->orderValues, @@ -700,7 +695,7 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const fb_assert(false); } // range between ... and {preceding | following} - else if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::RANGE && + else if (m_frameExtent->unit == FrameExtent::Unit::RANGE && m_frameExtent->frame2->value) { impure->windowBlock.endPosition = locateFrameRange(tdbb, request, impure, @@ -712,43 +707,37 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const return false; } - if (!m_order) - impure->rangePending = MAX(0, impure->windowBlock.endPosition - position); - else if (m_order && m_frameExtent->unit == WindowClause::FrameExtent::Unit::RANGE) + if ((m_frameExtent->frame1->bound == Frame::Bound::PRECEDING && !m_frameExtent->frame1->value && + m_frameExtent->frame2->bound == Frame::Bound::FOLLOWING && !m_frameExtent->frame2->value) || + (m_frameExtent->unit == FrameExtent::Unit::RANGE && !m_order)) { - if (m_frameExtent->frame1->bound == WindowClause::Frame::Bound::PRECEDING && - !m_frameExtent->frame1->value && - m_frameExtent->frame2->bound == WindowClause::Frame::Bound::FOLLOWING && - !m_frameExtent->frame2->value) - { - impure->rangePending = MAX(0, impure->windowBlock.endPosition - position); - } - else - { - SINT64 rangePos = position; - cacheValues(tdbb, request, &m_order->expressions, impure->orderValues, - DummyAdjustFunctor()); - - while (++rangePos <= impure->partitionBlock.endPosition) - { - if (!m_next->getRecord(tdbb)) - fb_assert(false); - - if (lookForChange(tdbb, request, &m_order->expressions, m_order, - impure->orderValues)) - { - break; - } - } - - impure->rangePending = rangePos - position - 1; - } - - m_next->locate(tdbb, position); - - if (!m_next->getRecord(tdbb)) - fb_assert(false); + impure->rangePending = MAX(0, impure->windowBlock.endPosition - position); } + else if (m_frameExtent->unit == FrameExtent::Unit::RANGE) + { + SINT64 rangePos = position; + cacheValues(tdbb, request, &m_order->expressions, impure->orderValues, + DummyAdjustFunctor()); + + while (++rangePos <= impure->partitionBlock.endPosition) + { + if (!m_next->getRecord(tdbb)) + fb_assert(false); + + if (lookForChange(tdbb, request, &m_order->expressions, m_order, + impure->orderValues)) + { + break; + } + } + + impure->rangePending = rangePos - position - 1; + } + + m_next->locate(tdbb, position); + + if (!m_next->getRecord(tdbb)) + fb_assert(false); //// TODO: There is no need to pass record by record when m_aggSources.isEmpty() @@ -902,7 +891,7 @@ void WindowedStream::WindowStream::nullRecords(thread_db* tdbb) const } const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* request, - const WindowClause::Frame* frame, impure_value_ex* impureValue) const + const Frame* frame, impure_value_ex* impureValue) const { dsc* desc = EVL_expr(tdbb, request, frame->value); bool error = false; @@ -911,7 +900,7 @@ const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* error = true; else { - if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::ROWS) + if (m_frameExtent->unit == FrameExtent::Unit::ROWS) { // Purposedly used 32-bit here. So long distance will complicate things for no gain. impureValue->vlux_count = MOV_get_long(tdbb, desc, 0); @@ -919,7 +908,7 @@ const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* if (impureValue->vlux_count < 0) error = true; - if (frame->bound == WindowClause::Frame::Bound::PRECEDING) + if (frame->bound == Frame::Bound::PRECEDING) impureValue->vlux_count = -impureValue->vlux_count; } else if (MOV_compare(tdbb, desc, &zeroDsc) < 0) @@ -937,7 +926,7 @@ const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* } SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, jrd_req* request, Impure* impure, - const WindowClause::Frame* frame, const dsc* offsetDesc, SINT64 position) const + const Frame* frame, const dsc* offsetDesc, SINT64 position) const { if (m_order->expressions.getCount() != 1) { @@ -983,7 +972,7 @@ SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, jrd_req* --rangePos; } } - else if (frame->bound == WindowClause::Frame::Bound::FOLLOWING) + else if (frame->bound == Frame::Bound::FOLLOWING) { const int bound = frame == m_frameExtent->frame1 ? 0 : 1; diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index f81158037e..1ede54479c 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -905,7 +905,7 @@ Data source : @4', NULL, NULL) ('no_cursor', 'DSQL_open', 'dsql.cpp', NULL, 0, 795, NULL, 'Cannot open cursor for non-SELECT statement', NULL, NULL); ('dsql_window_incompat_frames', NULL, 'ExprNodes.cpp', NULL, 0, 796, NULL, 'If specifies @1, then shall not specify @2', NULL, NULL); ('dsql_window_range_multi_key', NULL, 'ExprNodes.cpp', NULL, 0, 797, NULL, 'RANGE based window with {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value', NULL, NULL); -('dsql_window_range_inv_key_type', NULL, 'ExprNodes.cpp', NULL, 0, 798, NULL, 'RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types', NULL, NULL); +('dsql_window_range_inv_key_type', NULL, 'ExprNodes.cpp', NULL, 0, 798, NULL, 'RANGE based window with PRECEDING/FOLLOWING must have a single ORDER BY key of numerical, date, time or timestamp types', NULL, NULL); ('dsql_window_frame_value_inv_type', NULL, 'ExprNodes.cpp', NULL, 0, 799, NULL, 'Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type', NULL, NULL); ('window_frame_value_invalid', NULL, 'ExprNodes.cpp', NULL, 0, 800, NULL, 'Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative', NULL, NULL); ('dsql_window_not_found', NULL, 'ExprNodes.cpp', NULL, 0, 801, NULL, 'Window @1 not found', NULL, NULL);