/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
#include "jliball.hpp"
#include "hql.hpp"
#include "platform.h"
#include "jlib.hpp"
#include "jexcept.hpp"
#include "jmisc.hpp"
#include "javahash.hpp"
#include "eclhelper.hpp"
#include "hqlfunc.hpp"
#include "hqlhtcpp.ipp"
#include "hqlwcpp.hpp"
#include "hqlcpputil.hpp"
#include "hqlcerrors.hpp"
#include "hqlcatom.hpp"
#include "hqlpmap.hpp"
#include "hqlthql.hpp"
#include "hqlcset.ipp"
#include "hqlfold.hpp"
#include "hqltcppc.ipp"
#include "hqlutil.hpp"
#include "hqlcse.ipp"
#include "hqliter.ipp"
#include "hqlinline.hpp"
//===========================================================================
bool isSequenceRoot(IHqlExpression * expr)
{
switch (expr->getOperator())
{
case no_newkeyindex:
case no_table:
case no_select:
return true;
}
return false;
}
bool canBuildSequenceInline(IHqlExpression * expr)
{
loop
{
switch (expr->getOperator())
{
case no_cachealias:
expr = expr->queryChild(1);
break;
case no_newkeyindex:
case no_table:
case no_select:
return true;
case no_sorted:
case no_distributed:
case no_preservemeta:
case no_grouped:
case no_preload:
case no_limit:
case no_keyedlimit:
case no_choosen:
case no_filter:
case no_hqlproject:
case no_newusertable:
case no_compound_diskread:
case no_compound_disknormalize:
case no_compound_diskaggregate:
case no_compound_diskcount:
case no_compound_diskgroupaggregate:
case no_compound_indexread:
case no_compound_indexnormalize:
case no_compound_indexaggregate:
case no_compound_indexcount:
case no_compound_indexgroupaggregate:
case no_compound_childread:
case no_compound_childnormalize:
case no_compound_childaggregate:
case no_compound_childcount:
case no_compound_childgroupaggregate:
case no_compound_fetch:
case no_compound_selectnew:
case no_compound_inline:
case no_alias_scope:
case no_dataset_alias:
expr = expr->queryChild(0);
break;
default:
return false;
}
}
}
//-------------------------------------------------------------------
void TransformSequenceBuilder::buildSequence(BuildCtx & ctx, BuildCtx * declarectx, IHqlExpression * expr)
{
node_operator op = expr->getOperator();
switch (op)
{
case no_cachealias:
buildSequence(ctx, declarectx, expr->queryChild(1));
return;
case no_newkeyindex:
case no_table:
case no_fetch:
case no_select:
case no_anon:
case no_pseudods:
break;
case no_hqlproject:
case no_newusertable:
buildSequence(ctx, NULL, expr->queryChild(0));
break;
default:
buildSequence(ctx, declarectx, expr->queryChild(0));
break;
}
switch (op)
{
case no_filter:
{
HqlExprAttr cond;
ForEachChild(i, expr)
{
IHqlExpression * cur = queryRealChild(expr, i);
if (cur && i != 0)
extendConditionOwn(cond, no_and, LINK(cur));
}
OwnedHqlExpr test = getInverse(cond);
if (translator.queryOptions().foldFilter)
test.setown(foldScopedHqlExpression(expr->queryChild(0)->queryNormalizedSelector(), test));
if (translator.queryOptions().spotCSE)
test.setown(spotScalarCSE(test));
translator.buildFilteredReturn(ctx, test, failedFilterValue);
}
break;
case no_hqlproject:
{
IHqlExpression * dataset = expr->queryChild(0);
OwnedHqlExpr leftSelect = createSelector(no_left, dataset, querySelSeq(expr));
OwnedHqlExpr newSelect = ensureActiveRow(dataset->queryNormalizedSelector());
OwnedHqlExpr transform = replaceSelector(expr->queryChild(1), leftSelect, newSelect);
//MORE: Calculate cses at this point, but we really need to have removed hqlprojects for cse to work...
BuildCtx & createctx = declarectx ? *declarectx : ctx;
Owned tempRow = translator.declareTempAnonRow(createctx, ctx, expr);
BuildCtx subctx(ctx);
translator.associateSkipReturnMarker(subctx, failedFilterValue, NULL);
translator.doInlineTransform(subctx, transform, tempRow);
translator.bindTableCursor(ctx, expr, tempRow->queryBound());
}
break;
case no_newusertable:
{
IHqlExpression * transform = expr->queryChild(2);
BuildCtx & createctx = declarectx ? *declarectx : ctx;
Owned tempRow = translator.declareTempAnonRow(createctx, ctx, expr);
BuildCtx subctx(ctx);
translator.associateSkipReturnMarker(subctx, failedFilterValue, NULL);
translator.doInlineTransform(subctx, transform, tempRow);
translator.bindTableCursor(ctx, expr, tempRow->queryBound());
}
break;
}
}
//------------------------------------------------------------------------------------------
//Gather the different select levels, return the root dataset (if not in scope)
IHqlExpression * gatherSelectorLevels(HqlExprArray & iterators, IHqlExpression * expr)
{
loop
{
IHqlExpression * root = queryRoot(expr);
if (!root || root->getOperator() != no_select)
return expr;
iterators.add(*LINK(expr), 0);
if (!root->hasProperty(newAtom))
return NULL;
expr = root->queryChild(0);
}
}
void CompoundIteratorBuilder::bindParentCursors(BuildCtx & ctx, CursorArray & cursors)
{
OwnedHqlExpr colocal = createQuoted("activity", makeVoidType());
ForEachItemIn(i, cursors)
{
BoundRow & cur = cursors.item(i);
//Very similar to code in the extract builder
OwnedHqlExpr colocalBound = addMemberSelector(cur.queryBound(), colocal);
ctx.associateOwn(*cur.clone(colocalBound));
}
}
void CompoundIteratorBuilder::buildCompoundIterator(BuildCtx & initctx, HqlExprArray & iterators, CursorArray & cursors)
{
StringBuffer s;
declarectx.addQuoted("RtlCompoundIterator iter;");
initctx.addQuoted(s.clear().append("iter.init(").append(iterators.ordinality()).append(");"));
ForEachItemIn(i, iterators)
{
StringBuffer iterName, cursorName;
createSingleLevelIterator(iterName, cursorName, &iterators.item(i), cursors);
initctx.addQuoted(s.clear().append("iter.addIter(").append(i).append(",&").append(iterName).append(",&").append(cursorName).append(");"));
}
}
void CompoundIteratorBuilder::createSingleLevelIterator(StringBuffer & iterName, StringBuffer & cursorName, IHqlExpression * expr, CursorArray & cursors)
{
LinkedHqlExpr cur = expr;
IHqlExpression * root = queryRoot(cur);
assertex(root->getOperator() == no_select);
//First remove any new attributes from selector, since parent cursor is guaranteed to be in scope at this point.
IHqlExpression * normalized = root->queryNormalizedSelector();
if (root != normalized)
cur.setown(replaceExpression(cur, root, normalized));
createSingleIterator(iterName, cur, cursors);
translator.getUniqueId(cursorName.append("row"));
OwnedHqlExpr row = createVariable(cursorName, makeRowReferenceType(cur));
declarectx.addDeclare(row);
cursors.append(*translator.createTableCursor(cur, row, no_none, NULL));
}
void CompoundIteratorBuilder::createSingleIterator(StringBuffer & iterName, IHqlExpression * expr, CursorArray & cursors)
{
StringBuffer s;
translator.getUniqueId(iterName.clear().append("iter"));
//MORE: Nested class/...
BuildCtx classctx(nestedctx);
translator.beginNestedClass(classctx, iterName, "IRtlDatasetSimpleCursor", NULL, NULL);
translator.queryEvalContext(classctx)->ensureHelpersExist();
if (isArrayRowset(expr->queryType()))
{
classctx.addQuoted("byte * * end;");
classctx.addQuoted("byte * * cur;");
}
else
{
classctx.addQuoted("byte * end;");
classctx.addQuoted("byte * cur;");
}
IHqlExpression * root = queryRoot(expr);
if (expr->queryBody() == root)
{
BuildCtx firstctx(classctx);
firstctx.addQuotedCompound("virtual const byte * first()");
createRawFirstFunc(firstctx, expr, cursors);
BuildCtx nextctx(classctx);
nextctx.addQuotedCompound("virtual const byte * next()");
createRawNextFunc(nextctx, expr, cursors);
}
else
{
BuildCtx rawfirstctx(classctx);
rawfirstctx.addQuotedCompound("inline const byte * rawFirst()");
createRawFirstFunc(rawfirstctx, root, cursors);
BuildCtx rawnextctx(classctx);
rawnextctx.addQuotedCompound("virtual const byte * rawNext()");
createRawNextFunc(rawnextctx, root, cursors);
OwnedHqlExpr failValue = createTranslatedOwned(createValue(no_nullptr, makeVoidType()));
TransformSequenceBuilder checkValidBuilder(translator, failValue);
BuildCtx checkctx(classctx);
checkctx.addQuotedCompound("inline const byte * checkValid()");
bindParentCursors(checkctx, cursors);
if (isArrayRowset(expr->queryType()))
translator.bindTableCursor(checkctx, root, "(*cur)");
else
translator.bindTableCursor(checkctx, root, "cur");
checkValidBuilder.buildSequence(checkctx, &classctx, expr);
BoundRow * match = translator.resolveSelectorDataset(checkctx, expr);
assertex(match);
OwnedHqlExpr row = getPointer(match->queryBound());
checkctx.addReturn(row);
BuildCtx firstctx(classctx);
firstctx.addQuotedCompound("virtual const byte * first()");
firstctx.addQuoted("if (!rawFirst()) return NULL;");
firstctx.addQuotedCompound("for (;;)");
firstctx.addQuoted("const byte * valid = checkValid(); if (valid) return valid;");
firstctx.addQuoted("if (!rawNext()) return NULL;");
BuildCtx nextctx(classctx);
nextctx.addQuotedCompound("virtual const byte * next()");
nextctx.addQuotedCompound("for (;;)");
nextctx.addQuoted("if (!rawNext()) return NULL;");
nextctx.addQuoted("const byte * valid = checkValid(); if (valid) return valid;");
}
translator.endNestedClass();
}
void CompoundIteratorBuilder::createRawFirstFunc(BuildCtx & ctx, IHqlExpression * expr, CursorArray & cursors)
{
bool isOutOfLine = isArrayRowset(expr->queryType());
bindParentCursors(ctx, cursors);
CHqlBoundExpr boundDs;
translator.buildDataset(ctx, expr, boundDs, queryNaturalFormat(expr->queryType()));
if (isOutOfLine)
{
StringBuffer s;
s.clear().append("cur = "); // more: should really be const...
translator.generateExprCpp(s, boundDs.expr).append(";");
ctx.addQuoted(s);
OwnedHqlExpr count = translator.getBoundCount(boundDs);
s.clear().append("end = cur+");
translator.generateExprCpp(s, count).append(";");
ctx.addQuoted(s);
ctx.addQuoted("return (cur < end) ? *cur : NULL;");
}
else
{
OwnedHqlExpr address = getPointer(boundDs.expr);
StringBuffer s;
s.clear().append("cur = (byte *)"); // more: should really be const...
translator.generateExprCpp(s, address).append(";");
ctx.addQuoted(s);
OwnedHqlExpr length = translator.getBoundLength(boundDs);
s.clear().append("end = cur+");
translator.generateExprCpp(s, length).append(";");
ctx.addQuoted(s);
ctx.addQuoted("return (cur < end) ? cur : NULL;");
}
}
void CompoundIteratorBuilder::createRawNextFunc(BuildCtx & ctx, IHqlExpression * expr, CursorArray & cursors)
{
if (isArrayRowset(expr->queryType()))
{
ctx.addQuoted("cur++;");
ctx.addQuoted("return (cur < end) ? *cur : NULL;");
}
else
{
bindParentCursors(ctx, cursors);
translator.bindTableCursor(ctx, expr, "cur");
CHqlBoundExpr bound;
translator.getRecordSize(ctx, expr, bound);
StringBuffer s;
s.clear().append("cur+=");
translator.generateExprCpp(s, bound.expr).append(";");
ctx.addQuoted(s);
ctx.addQuoted("return (cur < end) ? cur : NULL;");
}
}
//------------------------------------------------------------------------------------------