2009-10-31 20:03:30 +01:00
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Interbase Public
|
|
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy
|
|
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an
|
|
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
|
|
* or implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code was created by Inprise Corporation
|
|
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
|
|
* Copyright (C) Inprise Corporation.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
2010-10-13 12:39:52 +02:00
|
|
|
#include "../common/common.h"
|
2009-10-31 20:03:30 +01:00
|
|
|
#include "../jrd/jrd.h"
|
2010-02-13 21:29:29 +01:00
|
|
|
#include "../dsql/Nodes.h"
|
2010-10-24 02:26:00 +02:00
|
|
|
#include "../dsql/ExprNodes.h"
|
2009-12-09 19:45:44 +01:00
|
|
|
#include "../jrd/cmp_proto.h"
|
2009-10-31 20:03:30 +01:00
|
|
|
#include "../jrd/evl_proto.h"
|
2009-12-09 19:45:44 +01:00
|
|
|
#include "../jrd/exe_proto.h"
|
2009-10-31 20:03:30 +01:00
|
|
|
#include "../jrd/mov_proto.h"
|
|
|
|
#include "../jrd/vio_proto.h"
|
2009-12-25 10:55:05 +01:00
|
|
|
#include "../jrd/Attachment.h"
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
#include "RecordSource.h"
|
|
|
|
|
2009-10-31 20:03:30 +01:00
|
|
|
using namespace Firebird;
|
2009-12-09 19:45:44 +01:00
|
|
|
using namespace Jrd;
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
// ------------------------
|
|
|
|
// Data access: aggregation
|
|
|
|
// ------------------------
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-14 20:08:22 +01:00
|
|
|
// Note that we can have NULL order here, in case of window function with shouldCallWinPass
|
|
|
|
// returning true, with partition, and without order. Example: ROW_NUMBER() OVER (PARTITION BY N).
|
2010-11-21 04:47:29 +01:00
|
|
|
AggregatedStream::AggregatedStream(CompilerScratch* csb, UCHAR stream, const NestValueArray* group,
|
|
|
|
const MapNode* map, BaseBufferedStream* next, const NestValueArray* order)
|
2010-02-14 00:55:48 +01:00
|
|
|
: RecordStream(csb, stream),
|
2010-02-16 01:26:53 +01:00
|
|
|
m_bufferedStream(next),
|
2010-02-14 20:08:22 +01:00
|
|
|
m_next(m_bufferedStream),
|
2010-02-14 00:55:48 +01:00
|
|
|
m_group(group),
|
|
|
|
m_map(map),
|
2010-02-15 01:43:04 +01:00
|
|
|
m_order(order),
|
2010-11-21 04:47:29 +01:00
|
|
|
m_winPassSources(csb->csb_pool),
|
|
|
|
m_winPassTargets(csb->csb_pool)
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
2010-02-15 01:43:04 +01:00
|
|
|
init(csb);
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
AggregatedStream::AggregatedStream(CompilerScratch* csb, UCHAR stream, const NestValueArray* group,
|
2010-10-12 13:36:51 +02:00
|
|
|
const MapNode* map, RecordSource* next)
|
2010-02-14 20:08:22 +01:00
|
|
|
: RecordStream(csb, stream),
|
|
|
|
m_bufferedStream(NULL),
|
|
|
|
m_next(next),
|
|
|
|
m_group(group),
|
|
|
|
m_map(map),
|
2010-02-15 01:43:04 +01:00
|
|
|
m_order(NULL),
|
2010-11-21 04:47:29 +01:00
|
|
|
m_winPassSources(csb->csb_pool),
|
|
|
|
m_winPassTargets(csb->csb_pool)
|
2010-02-14 20:08:22 +01:00
|
|
|
{
|
2010-02-15 01:43:04 +01:00
|
|
|
init(csb);
|
2010-02-14 20:08:22 +01:00
|
|
|
}
|
|
|
|
|
2010-07-05 20:37:35 +02:00
|
|
|
void AggregatedStream::open(thread_db* tdbb) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
jrd_req* const request = tdbb->getRequest();
|
2010-04-05 23:20:08 +02:00
|
|
|
Impure* const impure = request->getImpure<Impure>(m_impure);
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
impure->irsb_flags = irsb_open;
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-10 02:32:47 +01:00
|
|
|
impure->state = STATE_GROUPING;
|
2010-02-14 00:55:48 +01:00
|
|
|
impure->pending = 0;
|
2009-12-09 19:45:44 +01:00
|
|
|
VIO_record(tdbb, &request->req_rpb[m_stream], m_format, tdbb->getDefaultPool());
|
2010-02-14 00:55:48 +01:00
|
|
|
|
2011-02-09 01:29:46 +01:00
|
|
|
unsigned impureCount = m_group ? m_group->getCount() : 0;
|
|
|
|
impureCount += m_order ? m_order->getCount() : 0;
|
|
|
|
|
|
|
|
if (!impure->impureValues)
|
|
|
|
{
|
|
|
|
impure->impureValues = FB_NEW(*tdbb->getDefaultPool()) impure_value[impureCount];
|
|
|
|
memset(impure->impureValues, 0, sizeof(impure_value) * impureCount);
|
|
|
|
}
|
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
m_next->open(tdbb);
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2010-07-05 20:37:35 +02:00
|
|
|
void AggregatedStream::close(thread_db* tdbb) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
jrd_req* const request = tdbb->getRequest();
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
invalidateRecords(request);
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-04-05 23:20:08 +02:00
|
|
|
Impure* const impure = request->getImpure<Impure>(m_impure);
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
if (impure->irsb_flags & irsb_open)
|
|
|
|
{
|
|
|
|
impure->irsb_flags &= ~irsb_open;
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
m_next->close(tdbb);
|
|
|
|
}
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2010-07-05 20:37:35 +02:00
|
|
|
bool AggregatedStream::getRecord(thread_db* tdbb) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
jrd_req* const request = tdbb->getRequest();
|
|
|
|
record_param* const rpb = &request->req_rpb[m_stream];
|
2010-04-05 23:20:08 +02:00
|
|
|
Impure* const impure = request->getImpure<Impure>(m_impure);
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
if (!(impure->irsb_flags & irsb_open))
|
|
|
|
{
|
|
|
|
rpb->rpb_number.setValid(false);
|
|
|
|
return false;
|
|
|
|
}
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-19 02:40:05 +01:00
|
|
|
if (m_bufferedStream) // Is that a window stream?
|
2010-02-14 00:55:48 +01:00
|
|
|
{
|
2010-02-16 01:26:53 +01:00
|
|
|
FB_UINT64 position = m_bufferedStream->getPosition(request);
|
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
if (impure->pending == 0)
|
|
|
|
{
|
|
|
|
if (impure->state == STATE_PENDING)
|
2010-02-14 20:08:22 +01:00
|
|
|
m_bufferedStream->getRecord(tdbb);
|
2010-02-14 00:55:48 +01:00
|
|
|
|
|
|
|
impure->state = evaluateGroup(tdbb, impure->state);
|
|
|
|
|
|
|
|
if (impure->state == STATE_PROCESS_EOF)
|
|
|
|
{
|
|
|
|
rpb->rpb_number.setValid(false);
|
|
|
|
return false;
|
|
|
|
}
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-14 20:08:22 +01:00
|
|
|
impure->pending = m_bufferedStream->getPosition(request) - position -
|
2010-02-14 00:55:48 +01:00
|
|
|
(impure->state == STATE_EOF_FOUND ? 0 : 1);
|
2010-02-14 20:08:22 +01:00
|
|
|
m_bufferedStream->locate(tdbb, position);
|
2010-02-14 00:55:48 +01:00
|
|
|
}
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
if (m_winPassSources.hasData())
|
2010-02-16 01:26:53 +01:00
|
|
|
{
|
|
|
|
SlidingWindow window(tdbb, m_bufferedStream, m_group, request);
|
|
|
|
dsc* desc;
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* const sourceEnd = m_winPassSources.end();
|
|
|
|
|
|
|
|
for (const NestConst<ValueExprNode>* source = m_winPassSources.begin(),
|
|
|
|
*target = m_winPassTargets.begin();
|
|
|
|
source != sourceEnd;
|
|
|
|
++source, ++target)
|
2010-02-16 01:26:53 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const AggNode* aggNode = (*source)->as<AggNode>();
|
2010-02-16 01:26:53 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
const FieldNode* field = (*target)->as<FieldNode>();
|
2010-11-14 18:25:48 +01:00
|
|
|
const USHORT id = field->fieldId;
|
|
|
|
Record* record = request->req_rpb[field->fieldStream].rpb_record;
|
2010-02-16 01:26:53 +01:00
|
|
|
|
|
|
|
desc = aggNode->winPass(tdbb, request, &window);
|
|
|
|
|
|
|
|
if (!desc)
|
|
|
|
SET_NULL(record, id);
|
|
|
|
else
|
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target));
|
2010-02-16 01:26:53 +01:00
|
|
|
CLEAR_NULL(record, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
if (impure->pending > 0)
|
|
|
|
--impure->pending;
|
2010-02-14 20:08:22 +01:00
|
|
|
|
2010-02-16 01:26:53 +01:00
|
|
|
m_bufferedStream->getRecord(tdbb);
|
2010-02-14 20:08:22 +01:00
|
|
|
|
2010-02-16 01:26:53 +01:00
|
|
|
// If there is no group, we should reassign the map items.
|
|
|
|
if (!m_group)
|
2010-02-14 20:08:22 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
|
|
|
*target = m_map->targetList.begin();
|
|
|
|
source != sourceEnd;
|
|
|
|
++source, ++target)
|
2010-02-14 20:08:22 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const AggNode* aggNode = (*source)->as<AggNode>();
|
2010-02-16 01:26:53 +01:00
|
|
|
|
|
|
|
if (!aggNode)
|
2010-11-21 04:47:29 +01:00
|
|
|
EXE_assignment(tdbb, *source, *target);
|
2010-02-14 20:08:22 +01:00
|
|
|
}
|
|
|
|
}
|
2010-02-14 00:55:48 +01:00
|
|
|
}
|
|
|
|
else
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
2010-02-14 00:55:48 +01:00
|
|
|
impure->state = evaluateGroup(tdbb, impure->state);
|
|
|
|
|
|
|
|
if (impure->state == STATE_PROCESS_EOF)
|
|
|
|
{
|
|
|
|
rpb->rpb_number.setValid(false);
|
|
|
|
return false;
|
|
|
|
}
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
rpb->rpb_number.setValid(true);
|
|
|
|
return true;
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2010-07-05 20:37:35 +02:00
|
|
|
bool AggregatedStream::refetchRecord(thread_db* tdbb) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
return m_next->refetchRecord(tdbb);
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
bool AggregatedStream::lockRecord(thread_db* /*tdbb*/) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
|
|
|
return false; // compiler silencer
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2011-02-02 12:31:04 +01:00
|
|
|
void AggregatedStream::print(thread_db* tdbb, string& plan,
|
|
|
|
bool detailed, unsigned level) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2011-02-02 12:31:04 +01:00
|
|
|
if (detailed)
|
|
|
|
{
|
|
|
|
plan += printIndent(++level) + "Aggregate";
|
|
|
|
}
|
2009-11-01 11:58:16 +01:00
|
|
|
|
2011-02-02 12:31:04 +01:00
|
|
|
m_next->print(tdbb, plan, detailed, level);
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
void AggregatedStream::markRecursive()
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
m_next->markRecursive();
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2010-07-05 20:37:35 +02:00
|
|
|
void AggregatedStream::invalidateRecords(jrd_req* request) const
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
|
|
|
m_next->invalidateRecords(request);
|
|
|
|
}
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2011-02-20 16:34:08 +01:00
|
|
|
void AggregatedStream::findUsedStreams(StreamList& streams) const
|
2010-02-14 00:55:48 +01:00
|
|
|
{
|
|
|
|
RecordStream::findUsedStreams(streams);
|
2010-02-15 01:43:04 +01:00
|
|
|
if (m_bufferedStream)
|
|
|
|
m_bufferedStream->findUsedStreams(streams);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AggregatedStream::init(CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
fb_assert(m_map && m_next);
|
|
|
|
m_impure = CMP_impure(csb, sizeof(Impure));
|
|
|
|
|
|
|
|
// Separate nodes that requires the winPass call.
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
|
|
|
*target = m_map->targetList.begin();
|
|
|
|
source != sourceEnd;
|
|
|
|
++source, ++target)
|
2010-02-15 01:43:04 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const AggNode* aggNode = (*source)->as<AggNode>();
|
2010-02-15 01:43:04 +01:00
|
|
|
|
|
|
|
if (aggNode && aggNode->shouldCallWinPass())
|
2010-11-21 04:47:29 +01:00
|
|
|
{
|
|
|
|
m_winPassSources.add(*source);
|
|
|
|
m_winPassTargets.add(*target);
|
|
|
|
}
|
2010-02-15 01:43:04 +01:00
|
|
|
}
|
2010-02-14 00:55:48 +01:00
|
|
|
}
|
|
|
|
|
2009-10-31 20:03:30 +01:00
|
|
|
// Compute the next aggregated record of a value group. evlGroup is driven by, and returns, a state
|
2009-12-10 02:32:47 +01:00
|
|
|
// variable.
|
2010-07-05 20:37:35 +02:00
|
|
|
AggregatedStream::State AggregatedStream::evaluateGroup(thread_db* tdbb, AggregatedStream::State state) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
jrd_req* const request = tdbb->getRequest();
|
2009-10-31 20:03:30 +01:00
|
|
|
|
|
|
|
if (--tdbb->tdbb_quantum < 0)
|
|
|
|
JRD_reschedule(tdbb, 0, true);
|
|
|
|
|
2011-02-09 01:29:46 +01:00
|
|
|
Impure* const impure = request->getImpure<Impure>(m_impure);
|
2009-10-31 20:03:30 +01:00
|
|
|
|
|
|
|
// if we found the last record last time, we're all done
|
|
|
|
|
2009-12-10 02:32:47 +01:00
|
|
|
if (state == STATE_EOF_FOUND)
|
|
|
|
return STATE_PROCESS_EOF;
|
2009-10-31 20:03:30 +01:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
// If there isn't a record pending, open the stream and get one
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
if (!m_order || state == STATE_PROCESS_EOF || state == STATE_GROUPING)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-02-14 00:55:48 +01:00
|
|
|
// Initialize the aggregate record
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
|
|
|
*target = m_map->targetList.begin();
|
|
|
|
source != sourceEnd;
|
|
|
|
++source, ++target)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const AggNode* aggNode = (*source)->as<AggNode>();
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
if (aggNode)
|
|
|
|
aggNode->aggInit(tdbb, request);
|
|
|
|
else if ((*source)->is<LiteralNode>())
|
|
|
|
EXE_assignment(tdbb, *source, *target);
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-10 02:32:47 +01:00
|
|
|
if (state == STATE_PROCESS_EOF || state == STATE_GROUPING)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
if (!m_next->getRecord(tdbb))
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-01-18 22:37:47 +01:00
|
|
|
if (m_group)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
finiDistinct(tdbb, request);
|
2009-12-10 02:32:47 +01:00
|
|
|
return STATE_PROCESS_EOF;
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
2010-02-14 00:55:48 +01:00
|
|
|
|
2009-12-10 02:32:47 +01:00
|
|
|
state = STATE_EOF_FOUND;
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-09 01:29:46 +01:00
|
|
|
unsigned impureOffset = 0;
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* ptrValue, *endValue;
|
2009-10-31 20:03:30 +01:00
|
|
|
dsc* desc;
|
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
if (m_group)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2011-02-09 01:29:46 +01:00
|
|
|
for (ptrValue = m_group->begin(), endValue = m_group->end();
|
|
|
|
ptrValue != endValue;
|
|
|
|
++ptrValue, ++impureOffset)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const ValueExprNode* from = *ptrValue;
|
2011-02-09 01:29:46 +01:00
|
|
|
impure_value* target = &impure->impureValues[impureOffset];
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
desc = EVL_expr(tdbb, request, from);
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2009-10-31 20:03:30 +01:00
|
|
|
if (request->req_flags & req_null)
|
2011-02-09 01:29:46 +01:00
|
|
|
target->vlu_desc.dsc_address = NULL;
|
2009-10-31 20:03:30 +01:00
|
|
|
else
|
2011-02-09 01:29:46 +01:00
|
|
|
EVL_make_value(tdbb, desc, target);
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
if (m_order)
|
|
|
|
{
|
2011-02-09 01:29:46 +01:00
|
|
|
for (ptrValue = m_order->begin(), endValue = m_order->end();
|
|
|
|
ptrValue != endValue;
|
|
|
|
++ptrValue, ++impureOffset)
|
2010-02-14 00:55:48 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const ValueExprNode* from = *ptrValue;
|
2011-02-09 01:29:46 +01:00
|
|
|
impure_value* target = &impure->impureValues[impureOffset];
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
desc = EVL_expr(tdbb, request, from);
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
if (request->req_flags & req_null)
|
2011-02-09 01:29:46 +01:00
|
|
|
target->vlu_desc.dsc_address = NULL;
|
2010-02-14 00:55:48 +01:00
|
|
|
else
|
2011-02-09 01:29:46 +01:00
|
|
|
EVL_make_value(tdbb, desc, target);
|
2010-02-14 00:55:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-31 20:03:30 +01:00
|
|
|
// Loop thru records until either a value change or EOF
|
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
|
2009-12-10 02:32:47 +01:00
|
|
|
while (state != STATE_EOF_FOUND)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2011-02-09 01:29:46 +01:00
|
|
|
impureOffset = 0;
|
2009-12-10 02:32:47 +01:00
|
|
|
state = STATE_PENDING;
|
2009-10-31 20:03:30 +01:00
|
|
|
|
|
|
|
if (first)
|
|
|
|
first = false;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// In the case of a group by, look for a change in value of any of
|
|
|
|
// the columns; if we find one, stop aggregating and return what we have.
|
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
if (m_group)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
for (ptrValue = m_group->begin(), endValue = m_group->end();
|
|
|
|
ptrValue != endValue;
|
2011-02-09 01:29:46 +01:00
|
|
|
++ptrValue, ++impureOffset)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const ValueExprNode* from = *ptrValue;
|
2011-02-09 01:29:46 +01:00
|
|
|
impure_value* vtemp = &impure->impureValues[impureOffset];
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
desc = EVL_expr(tdbb, request, from);
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-14 00:55:48 +01:00
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
2011-02-09 01:29:46 +01:00
|
|
|
if (vtemp->vlu_desc.dsc_address)
|
2010-02-14 00:55:48 +01:00
|
|
|
{
|
|
|
|
if (m_order)
|
|
|
|
state = STATE_GROUPING;
|
|
|
|
goto break_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-02-09 01:29:46 +01:00
|
|
|
if (!vtemp->vlu_desc.dsc_address || MOV_compare(&vtemp->vlu_desc, desc) != 0)
|
2010-02-14 00:55:48 +01:00
|
|
|
{
|
|
|
|
if (m_order)
|
|
|
|
state = STATE_GROUPING;
|
|
|
|
goto break_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_order)
|
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
for (ptrValue = m_order->begin(), endValue = m_order->end();
|
|
|
|
ptrValue != endValue;
|
2011-02-09 01:29:46 +01:00
|
|
|
++ptrValue, ++impureOffset)
|
2010-02-14 00:55:48 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const ValueExprNode* from = *ptrValue;
|
2011-02-09 01:29:46 +01:00
|
|
|
impure_value* vtemp = &impure->impureValues[impureOffset];
|
2010-02-14 00:55:48 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
desc = EVL_expr(tdbb, request, from);
|
2010-02-14 00:55:48 +01:00
|
|
|
|
2009-10-31 20:03:30 +01:00
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
2011-02-09 01:29:46 +01:00
|
|
|
if (vtemp->vlu_desc.dsc_address)
|
2009-10-31 20:03:30 +01:00
|
|
|
goto break_out;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-02-09 01:29:46 +01:00
|
|
|
if (!vtemp->vlu_desc.dsc_address || MOV_compare(&vtemp->vlu_desc, desc) != 0)
|
2009-10-31 20:03:30 +01:00
|
|
|
goto break_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// go through and compute all the aggregates on this record
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
|
|
|
*target = m_map->targetList.begin();
|
|
|
|
source != sourceEnd;
|
|
|
|
++source, ++target)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const AggNode* aggNode = (*source)->as<AggNode>();
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
if (aggNode)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
aggNode->aggPass(tdbb, request);
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
// If a max or min has been mapped to an index, then the first record is the EOF.
|
|
|
|
if (aggNode->indexed)
|
|
|
|
state = STATE_EOF_FOUND;
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
2010-02-13 21:29:29 +01:00
|
|
|
else
|
2010-11-21 04:47:29 +01:00
|
|
|
EXE_assignment(tdbb, *source, *target);
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
2009-12-10 02:32:47 +01:00
|
|
|
if (state == STATE_EOF_FOUND)
|
2009-10-31 20:03:30 +01:00
|
|
|
break;
|
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
if (!m_next->getRecord(tdbb))
|
2009-12-10 02:32:47 +01:00
|
|
|
state = STATE_EOF_FOUND;
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
break_out:
|
|
|
|
|
|
|
|
// Finish up any residual computations and get out
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
|
|
|
*target = m_map->targetList.begin();
|
|
|
|
source != sourceEnd;
|
|
|
|
++source, ++target)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const AggNode* aggNode = (*source)->as<AggNode>();
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
if (aggNode)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const FieldNode* field = (*target)->as<FieldNode>();
|
2010-11-14 18:25:48 +01:00
|
|
|
const USHORT id = field->fieldId;
|
|
|
|
Record* record = request->req_rpb[field->fieldStream].rpb_record;
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
desc = aggNode->execute(tdbb, request);
|
|
|
|
if (!desc || !desc->dsc_dtype)
|
2009-10-31 20:03:30 +01:00
|
|
|
SET_NULL(record, id);
|
|
|
|
else
|
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target));
|
2009-10-31 20:03:30 +01:00
|
|
|
CLEAR_NULL(record, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-12-09 19:45:44 +01:00
|
|
|
catch (const Exception&)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
finiDistinct(tdbb, request);
|
2009-12-09 19:45:44 +01:00
|
|
|
throw;
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
// Finalize a sort for distinct aggregate
|
2010-07-05 20:37:35 +02:00
|
|
|
void AggregatedStream::finiDistinct(thread_db* tdbb, jrd_req* request) const
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin();
|
|
|
|
source != sourceEnd;
|
|
|
|
++source)
|
2009-10-31 20:03:30 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const AggNode* aggNode = (*source)->as<AggNode>();
|
2009-10-31 20:03:30 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
if (aggNode)
|
|
|
|
aggNode->aggFinish(tdbb, request);
|
2009-10-31 20:03:30 +01:00
|
|
|
}
|
|
|
|
}
|
2010-02-16 01:26:53 +01:00
|
|
|
|
|
|
|
|
2010-08-09 17:48:51 +02:00
|
|
|
SlidingWindow::SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream,
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestValueArray* aGroup, jrd_req* aRequest)
|
2010-02-16 01:26:53 +01:00
|
|
|
: tdbb(aTdbb), // Note: instanciate the class only as local variable
|
|
|
|
stream(aStream),
|
|
|
|
group(aGroup),
|
|
|
|
request(aRequest)
|
|
|
|
{
|
|
|
|
savedPosition = stream->getPosition(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
SlidingWindow::~SlidingWindow()
|
|
|
|
{
|
|
|
|
if (!moved)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (impure_value* impure = partitionKeys.begin(); impure != partitionKeys.end(); ++impure)
|
|
|
|
delete impure->vlu_string;
|
|
|
|
|
|
|
|
// Position the stream where we received it.
|
|
|
|
stream->locate(tdbb, savedPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move in the window without pass partition boundaries.
|
|
|
|
bool SlidingWindow::move(SINT64 delta)
|
|
|
|
{
|
2010-02-16 09:53:31 +01:00
|
|
|
const SINT64 newPosition = SINT64(savedPosition) + delta;
|
2010-02-16 01:26:53 +01:00
|
|
|
|
|
|
|
// If we try to go out of bounds, no need to check the partition.
|
|
|
|
if (newPosition < 0 || newPosition >= (SINT64) stream->getCount(request))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!group)
|
|
|
|
{
|
|
|
|
// No partition, we may go everywhere.
|
|
|
|
|
|
|
|
moved = true;
|
|
|
|
|
|
|
|
stream->locate(tdbb, newPosition);
|
|
|
|
|
|
|
|
if (!stream->getRecord(tdbb))
|
|
|
|
{
|
|
|
|
fb_assert(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!moved)
|
|
|
|
{
|
|
|
|
// This is our first move. We should cache the partition values, so subsequente moves didn't
|
|
|
|
// need to evaluate them again.
|
|
|
|
|
|
|
|
if (!stream->getRecord(tdbb))
|
|
|
|
{
|
|
|
|
fb_assert(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-02-16 22:16:21 +01:00
|
|
|
try
|
|
|
|
{
|
2010-10-12 13:36:51 +02:00
|
|
|
impure_value* impure = partitionKeys.getBuffer(group->getCount());
|
|
|
|
memset(impure, 0, sizeof(impure_value) * group->getCount());
|
2010-02-16 01:26:53 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* const end = group->end();
|
2010-02-16 22:16:21 +01:00
|
|
|
dsc* desc;
|
2010-02-16 01:26:53 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr < end; ++ptr, ++impure)
|
2010-02-16 22:16:21 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const ValueExprNode* from = *ptr;
|
|
|
|
desc = EVL_expr(tdbb, request, from);
|
2010-10-12 13:36:51 +02:00
|
|
|
|
2010-02-16 22:16:21 +01:00
|
|
|
if (request->req_flags & req_null)
|
|
|
|
impure->vlu_desc.dsc_address = NULL;
|
|
|
|
else
|
|
|
|
EVL_make_value(tdbb, desc, impure);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const Exception&)
|
2010-02-16 01:26:53 +01:00
|
|
|
{
|
2010-02-16 22:16:21 +01:00
|
|
|
stream->locate(tdbb, savedPosition); // Reposition for a new try.
|
|
|
|
throw;
|
2010-02-16 01:26:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
moved = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->locate(tdbb, newPosition);
|
|
|
|
|
|
|
|
if (!stream->getRecord(tdbb))
|
|
|
|
{
|
|
|
|
fb_assert(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify if we're still inside the same partition.
|
|
|
|
|
|
|
|
impure_value* impure = partitionKeys.begin();
|
|
|
|
dsc* desc;
|
2010-11-21 04:47:29 +01:00
|
|
|
const NestConst<ValueExprNode>* const end = group->end();
|
2010-02-16 01:26:53 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr != end; ++ptr, ++impure)
|
2010-02-16 01:26:53 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
const ValueExprNode* from = *ptr;
|
|
|
|
desc = EVL_expr(tdbb, request, from);
|
2010-02-16 01:26:53 +01:00
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
|
|
|
if (impure->vlu_desc.dsc_address)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!impure->vlu_desc.dsc_address || MOV_compare(&impure->vlu_desc, desc) != 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|