/*############################################################################## 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 "jmisc.hpp" #include "jstream.ipp" #include "jdebug.hpp" #include "hql.hpp" #include "hqlthql.hpp" #include "hqlhtcpp.ipp" #include "hqlttcpp.ipp" #include "hqlutil.hpp" #include "hqlthql.hpp" #include "hqlpmap.hpp" #include "hqlattr.hpp" #include "hqlwcpp.hpp" #include "hqlcpputil.hpp" #include "hqltcppc.ipp" #include "hqlopt.hpp" #include "hqlfold.hpp" #include "hqlcerrors.hpp" #include "hqlcatom.hpp" #include "hqlresource.hpp" #include "hqlregex.ipp" #include "hqlsource.ipp" #include "hqlcse.ipp" #include "hqlgraph.ipp" #include "hqlccommon.hpp" #include "hqliter.ipp" #include "hqlinline.hpp" #include "hqlusage.hpp" #include "hqlcppds.hpp" #define MAX_FIXED_SIZE_RAW 1024 #define INLINE_TABLE_EXPAND_LIMIT 4 void addGraphIdAttribute(ActivityInstance * instance, BuildCtx & ctx, IHqlExpression * graphId) { SubGraphInfo * match = matchActiveGraph(ctx, graphId); assertex(match); instance->addAttributeInt("_graphId", match->graphId); } //=========================================================================== void HqlCppTranslator::doBuildRowIfBranch(BuildCtx & initctx, BuildCtx & ctx, BoundRow * targetRow, IHqlExpression * branchExpr) { IHqlExpression * targetRowExpr = targetRow->queryBound(); Owned rowSelector = buildNewRow(ctx, branchExpr); Owned boundRow = rowSelector->getRow(ctx); OwnedHqlExpr rowExpr = getPointer(boundRow->queryBound()); OwnedHqlExpr castRow = createValue(no_implicitcast, targetRowExpr->getType(), LINK(rowExpr)); ctx.addAssign(targetRowExpr, castRow); if (rowSelector->isConditional()) targetRow->setConditional(true); } IReferenceSelector * HqlCppTranslator::doBuildRowIf(BuildCtx & ctx, IHqlExpression * expr) { OwnedHqlExpr foldedCond = foldHqlExpression(expr->queryChild(0)); if (foldedCond->queryValue()) { unsigned branch = (foldedCond->queryValue()->getBoolValue()) ? 1 : 2; return buildNewRow(ctx, expr->queryChild(branch)); } IHqlExpression * trueBranch = expr->queryChild(1); IHqlExpression * falseBranch = expr->queryChild(2); //Ideally should have a constant modifier on the following row... Owned rowType = makeReferenceModifier(expr->getType()); OwnedHqlExpr rowExpr = ctx.getTempDeclare(rowType, NULL); Owned row = createBoundRow(expr->queryBody(), rowExpr); //MORE: Need casts because cursor may be (probably are) constant, but temporary isn't //should find out by looking at the a const modifier on the cursor. BuildCtx condctx(ctx); IHqlStmt * cond = buildFilterViaExpr(condctx, foldedCond); //Mark the context as conditional after the filter test, so any temporaries from the filter aren't affected. condctx.associateExpr(queryConditionalRowMarker(), rowExpr); doBuildRowIfBranch(ctx, condctx, row, trueBranch); condctx.selectElse(cond); condctx.associateExpr(queryConditionalRowMarker(), rowExpr); doBuildRowIfBranch(ctx, condctx, row, falseBranch); ctx.associate(*row); return createReferenceSelector(row); } IReferenceSelector * HqlCppTranslator::doBuildRowDeserializeRow(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * srcRow = expr->queryChild(0); IHqlExpression * record = expr->queryRecord(); Owned tempRow = declareLinkedRow(ctx, expr, false); CHqlBoundTarget target; target.expr.set(tempRow->queryBound()); HqlExprArray args; args.append(*createRowAllocator(ctx, record)); args.append(*createRowSerializer(ctx, record, deserializerAtom)); args.append(*LINK(srcRow)); Owned resultType = makeReferenceModifier(makeAttributeModifier(makeRowType(record->getType()), getLinkCountedAttr())); OwnedHqlExpr call = bindFunctionCall(rtlDeserializeRowAtom, args, resultType); buildExprAssign(ctx, target, call); ctx.associate(*tempRow); return createReferenceSelector(tempRow); } void HqlCppTranslator::buildConstRow(IHqlExpression * record, IHqlExpression * rowData, CHqlBoundExpr & bound) { OwnedHqlExpr marker = createAttribute(rowAtom, LINK(record), LINK(rowData)); BuildCtx declareCtx(*code, literalAtom); if (declareCtx.getMatchExpr(marker, bound)) return; //MORE: This probably needs to go in the header as well... Owned rowType = makeConstantModifier(makeRowType(record->getType())); StringBuffer rowName; getUniqueId(rowName.append("r")); //Generate two variables to cope with the different ways the data is interpreted. //Would prefer it to be cleaner... row value would need an associated size unsigned dataSize = rowData->queryType()->getSize(); OwnedITypeInfo declareType; OwnedHqlExpr initializer; if (options.staticRowsUseStringInitializer) { //Generates smaller code (and probably more efficient representation in the c++ compiler //const byte[5+1] = "Hello"; need an extra byte for the implicit \0 declareType.setown(makeDataType(dataSize+1)); initializer.set(rowData); } else { //Following is strictly correct, but much larger. //const byte[5] = { 'H','e','l','l','o' }; declareType.set(rowData->queryType()); initializer.setown(createValue(no_create_initializer, rowData->getType(), LINK(rowData))); } //MORE: Currently these are marked as const rows, but not generated as such OwnedHqlExpr boundDeclare = createVariable(rowName, makeConstantModifier(LINK(declareType))); OwnedHqlExpr boundRow = createVariable(rowName, LINK(rowType)); declareCtx.addDeclare(boundDeclare, initializer); if (options.spanMultipleCpp) { BuildCtx protoctx(*code, mainprototypesAtom); protoctx.addDeclareExternal(boundDeclare); } bound.length.setown(getSizetConstant(dataSize)); bound.expr.set(boundRow); declareCtx.associateExpr(marker, bound); } bool HqlCppTranslator::doBuildRowConstantTransform(IHqlExpression * transform, CHqlBoundExpr & bound) { if (!transform->isConstant() || !options.generateStaticInlineTables) return false; OwnedHqlExpr constRow = createConstantRowExpr(transform); if (!constRow || !canGenerateStringInline(constRow->queryType()->getSize())) return false; buildConstRow(transform->queryRecord(), constRow, bound); return true; } IReferenceSelector * HqlCppTranslator::doBuildRowCreateRow(BuildCtx & ctx, IHqlExpression * expr) { CHqlBoundExpr bound; if (!doBuildRowConstantTransform(expr->queryChild(0), bound)) return doBuildRowViaTemp(ctx, expr); BoundRow * row = bindConstantRow(ctx, expr, bound); return createReferenceSelector(row); } BoundRow * HqlCppTranslator::bindConstantRow(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & bound) { BoundRow * row = bindRow(ctx, expr, bound.expr); //MORE: This should be done more cleanly OwnedHqlExpr sizeOfRow = createSizeof(row->querySelector()); ctx.associateExpr(sizeOfRow, bound.length); return row; } bool HqlCppTranslator::doBuildRowConstantNull(IHqlExpression * expr, CHqlBoundExpr & bound) { if (!options.generateStaticInlineTables) return false; IHqlExpression * record = expr->queryRecord(); OwnedHqlExpr constRow = createConstantNullRowExpr(record); if (!constRow) return false; buildConstRow(record, constRow, bound); return true; } IReferenceSelector * HqlCppTranslator::doBuildRowNull(BuildCtx & ctx, IHqlExpression * expr) { CHqlBoundExpr bound; if (!doBuildRowConstantNull(expr, bound)) return doBuildRowViaTemp(ctx, expr); BoundRow * row = bindRow(ctx, expr, bound.expr); return createReferenceSelector(row); } IReferenceSelector * HqlCppTranslator::doBuildRowViaTemp(BuildCtx & ctx, IHqlExpression * expr) { HqlExprAssociation * match; if (expr->isDataset()) match = ctx.queryAssociation(expr->queryNormalizedSelector(), AssocCursor, NULL); else match = ctx.queryAssociation(expr, AssocRow, NULL); if (match) { BoundRow * row = (BoundRow *)match; return createReferenceSelector(row, expr); } Owned tempRow = declareTempRow(ctx, ctx, expr); Owned rowBuilder = createRowBuilder(ctx, tempRow); Owned createdRef = createReferenceSelector(rowBuilder); buildRowAssign(ctx, createdRef, expr); finalizeTempRow(ctx, tempRow, rowBuilder); ctx.associate(*tempRow); return createReferenceSelector(tempRow); } void HqlCppTranslator::buildDefaultRow(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & bound) { OwnedHqlExpr clearExpr = createRow(no_null, LINK(expr->queryRecord()), createAttribute(clearAtom)); BoundRow * matchedRow = (BoundRow *)ctx.queryAssociation(clearExpr, AssocRow, NULL); if (!matchedRow) { if (doBuildRowConstantNull(expr, bound)) { bindRow(ctx, clearExpr, bound.expr); } else { BuildCtx * declarectx = &ctx; BuildCtx * callCtx = &ctx; getInvariantMemberContext(ctx, &declarectx, &callCtx, true, false); Owned tempRow = declareTempRow(*declarectx, *callCtx, clearExpr); Owned rowBuilder = createRowBuilder(*callCtx, tempRow); OwnedHqlExpr size = createVariable(LINK(sizetType)); OwnedHqlExpr clearCall = createClearRowCall(*callCtx, rowBuilder); callCtx->addDeclare(size, clearCall); OwnedHqlExpr sizeOfRow = createSizeof(rowBuilder->querySelector()); callCtx->associateExpr(sizeOfRow, size); finalizeTempRow(*callCtx, tempRow, rowBuilder); declarectx->associate(*tempRow); bound.expr.set(tempRow->queryBound()); } } else bound.expr.set(matchedRow->queryBound()); //yuk yuk, hack. If called from a const context then need to make the reference unconst. //The real fix is to implement real const tracking throughout the code generator, but that is far from trivial. //rkc39.hql is an example... if (ctx.queryMatchExpr(constantMemberMarkerExpr)) bound.expr.setown(createValue(no_cast, makeReferenceModifier(bound.expr->getType()), getPointer(bound.expr))); } void HqlCppTranslator::buildNullRow(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & bound) { bound.expr.setown(createValue(no_nullptr, makeRowReferenceType(expr))); } IReferenceSelector * HqlCppTranslator::doBuildRowFromXML(BuildCtx & ctx, IHqlExpression * expr) { // assertex(supportsLinkCountedRows); Owned overrideType = setLinkCountedAttr(expr->queryType(), true); Owned utf8Type = makeUtf8Type(UNKNOWN_LENGTH, NULL); IHqlExpression * record = expr->queryRecord(); OwnedHqlExpr ds = createDataset(no_anon, LINK(record)); StringBuffer xmlInstanceName, xmlFactoryName, s; bool usesContents = false; getUniqueId(xmlInstanceName.append("xml")); buildXmlReadTransform(ds, xmlFactoryName, usesContents); OwnedHqlExpr curActivityId = getCurrentActivityId(ctx); s.append("Owned ").append(xmlInstanceName).append(" = ").append(xmlFactoryName).append("(ctx,"); generateExprCpp(s, curActivityId).append(");"); ctx.addQuoted(s); HqlExprArray args; args.append(*createRowAllocator(ctx, record)); args.append(*ensureExprType(expr->queryChild(1), utf8Type)); args.append(*createQuoted(xmlInstanceName, makeBoolType())); args.append(*createConstant(expr->hasProperty(trimAtom))); OwnedHqlExpr function = bindFunctionCall(createRowFromXmlAtom, args, overrideType); CHqlBoundExpr bound; buildExpr(ctx, function, bound); Owned rowType = makeReferenceModifier(LINK(overrideType)); OwnedHqlExpr rowExpr = ctx.getTempDeclare(rowType, NULL); Owned row = createBoundRow(expr, rowExpr); ctx.associate(*row); // associate here because it is compared inside the loop OwnedHqlExpr defaultRowPtr = getPointer(bound.expr); ctx.addAssign(rowExpr, defaultRowPtr); return createReferenceSelector(row); } //NB: If this is a dataset operation, this function assumes that any parent datasets are already in scope // i.e. processed in buildDatasetAssign() // the one exception is aggregate because it needs to treat its input differently. IReferenceSelector * HqlCppTranslator::buildNewOrActiveRow(BuildCtx & ctx, IHqlExpression * expr, bool isNew) { if (isNew) return buildNewRow(ctx, expr); else return buildActiveRow(ctx, expr); } IReferenceSelector * HqlCppTranslator::buildNewRow(BuildCtx & ctx, IHqlExpression * expr) { assertex(!expr->isDataset()); BoundRow * match = static_cast(ctx.queryAssociation(expr, AssocRow, NULL)); if (match) return createReferenceSelector(match, expr); BoundRow * row = NULL; node_operator op = expr->getOperator(); switch (op) { case no_activerow: return buildActiveRow(ctx, expr->queryChild(0)); case no_if: return doBuildRowIf(ctx, expr); case no_id2blob: return doBuildRowIdToBlob(ctx, expr, true); case no_index: case no_selectnth: return buildDatasetIndex(ctx, expr); case no_selectmap: return buildDatasetSelectMap(ctx, expr); case no_left: case no_right: case no_self: case no_top: case no_activetable: return buildActiveRow(ctx, expr); case no_fromxml: return doBuildRowFromXML(ctx, expr); case no_serialize: if (isDummySerializeDeserialize(expr)) return buildNewRow(ctx, expr->queryChild(0)->queryChild(0)); return doBuildRowViaTemp(ctx, expr); case no_deserialize: if (isDummySerializeDeserialize(expr)) return buildNewRow(ctx, expr->queryChild(0)->queryChild(0)); return doBuildRowDeserializeRow(ctx, expr); case no_deref: { //Untested CHqlBoundExpr bound; buildExpr(ctx, expr, bound); row = bindRow(ctx, expr, bound.expr); break; } case no_createrow: return doBuildRowCreateRow(ctx, expr); case no_newusertable: case no_hqlproject: case no_temprow: case no_projectrow: return doBuildRowViaTemp(ctx, expr); case no_null: return doBuildRowNull(ctx, expr); case no_typetransfer: { CHqlBoundExpr bound; IHqlExpression * value = expr->queryChild(1); if (value->isDatarow()) buildAddress(ctx, value, bound); else buildExpr(ctx, value, bound); OwnedHqlExpr cursorExpr = createValue(no_implicitcast, makeReferenceModifier(expr->getType()), LINK(bound.expr)); row = bindRow(ctx, expr, cursorExpr); break; } case no_getresult: { IHqlExpression * seqAttr = expr->queryProperty(sequenceAtom); IHqlExpression * nameAttr = expr->queryProperty(namedAtom); IHqlExpression * record = expr->queryRecord(); OwnedHqlExpr serializedRecord = getSerializedForm(record); OwnedHqlExpr temp = createDatasetF(no_getresult, LINK(serializedRecord), LINK(seqAttr), LINK(nameAttr), NULL); OwnedHqlExpr row = createRow(no_selectnth, LINK(temp), createComma(getSizetConstant(1), createAttribute(noBoundCheckAtom))); row.setown(ensureDeserialized(row, expr->queryType())); return buildNewRow(ctx, row); } case no_matchattr: return doBuildRowMatchAttr(ctx, expr); case no_matchrow: return doBuildRowMatchRow(ctx, expr, true); case no_getgraphresult: case no_call: case no_externalcall: case no_alias: case no_translated: case no_libraryinput: { CHqlBoundExpr bound; buildExpr(ctx, expr, bound); Owned rawType = removeModifier(expr->queryType(), typemod_ref); OwnedHqlExpr cursorExpr = createValue(no_implicitcast, makeReferenceModifier(LINK(rawType)), LINK(bound.expr)); row = bindRow(ctx, expr, cursorExpr); if (bound.length) { OwnedHqlExpr sizeOfRow = createSizeof(row->querySelector()); ctx.associateExpr(sizeOfRow, bound.length); } //We could associate the original expression to allow better cse for child datasets in transforms, but it doesn't actually improve any examples //IHqlExpression * original = queryPropertyChild(expr, _original_Atom, 0); //if (original) // bindRow(ctx, original, cursorExpr)->setResultAlias(); break;//return createReferenceSelector(cursor); } case no_comma: case no_compound: buildStmt(ctx, expr->queryChild(0)); return buildNewRow(ctx, expr->queryChild(1)); case no_select: { #ifdef _DEBUG IHqlExpression * field = expr->queryChild(1); #endif Owned selector; if (isNewSelector(expr)) selector.setown(buildNewRow(ctx, expr->queryChild(0))); else selector.setown(buildActiveRow(ctx, expr->queryChild(0))); return selector->select(ctx, expr); } //If called because known to be a single row. case no_datasetfromrow: case no_nofold: case no_nohoist: case no_section: case no_sectioninput: return buildNewRow(ctx, expr->queryChild(0)); case no_skip: { buildStmt(ctx, expr); OwnedHqlExpr null = createNullExpr(expr); return buildNewRow(ctx, null); } case no_alias_scope: { expandAliasScope(ctx, expr); return buildNewRow(ctx, expr->queryChild(0)); } case no_split: throwUnexpected(); //not at all sure about this..... return buildNewRow(ctx, expr->queryChild(0)); default: { HqlExprAssociation * match; if (expr->isDataset()) match = ctx.queryAssociation(expr->queryNormalizedSelector(), AssocCursor, NULL); else match = ctx.queryAssociation(expr, AssocRow, NULL); if (match) { BoundRow * row = (BoundRow *)match; IReferenceSelector * alias = row->queryAlias(); if (alias) return LINK(alias); return createReferenceSelector(row, expr); } UNIMPLEMENTED_XY("row", getOpString(expr->getOperator())); } } assertex(row); return createReferenceSelector(row); } IReferenceSelector * HqlCppTranslator::buildActiveRow(BuildCtx & ctx, IHqlExpression * expr) { node_operator op = expr->getOperator(); switch (op) { case no_left: case no_right: case no_self: case no_top: case no_activetable: case no_selfref: // shouldn't ever occur... //All selectors should be listed here... break; case no_activerow: return buildActiveRow(ctx, expr->queryChild(0)); default: if (!expr->isDataset() && !expr->isDictionary()) return buildNewRow(ctx, expr); break; } HqlExprAssociation * match = ctx.queryAssociation(expr->queryNormalizedSelector(), AssocCursor, NULL); if (match) { BoundRow * row = (BoundRow *)match; IReferenceSelector * alias = row->queryAlias(); if (alias) return LINK(alias); return createReferenceSelector(row, expr); } switch (op) { case no_select: { #ifdef _DEBUG IHqlExpression * field = expr->queryChild(1); #endif Owned selector = buildNewOrActiveRow(ctx, expr->queryChild(0), isNewSelector(expr)); return selector->select(ctx, expr); } case no_id2blob: return doBuildRowIdToBlob(ctx, expr, false); } StringBuffer tablename; getExprIdentifier(tablename, expr); traceExpression("Dataset not found", expr); RowAssociationIterator iter(ctx); ForEach(iter) { BoundRow & cur = iter.get(); traceExpression("BoundCursor:", cur.querySelector()); } throwError1(HQLERR_DatasetNotActive, tablename.str()); return NULL; //remove warning about control paths } //--------------------------------------------------------------------------- void HqlCppTranslator::doBuildExprAggregate(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt) { OwnedHqlExpr normalized = normalizeAnyDatasetAliases(expr); if (expr != normalized) { buildExpr(ctx, normalized, tgt); return; } node_operator op = expr->getOperator(); ITypeInfo * type = expr->queryType(); ITypeInfo * tempType = op == no_count ? unsignedType : type; LoopInvariantHelper helper; BuildCtx aggctx(ctx); if (options.optimizeLoopInvariant) helper.getBestContext(aggctx, expr); CHqlBoundTarget result; createTempFor(aggctx, tempType, result, typemod_none, FormatNatural); doBuildAssignAggregate(aggctx, result, expr); tgt.setFromTarget(result); if (!isSameBasicType(type, tempType)) tgt.expr.setown(createValue(no_implicitcast, LINK(type), tgt.expr.getClear())); if (expr->isPure()) aggctx.associateExpr(expr, tgt); } void HqlCppTranslator::doBuildAssignAggregateLoop(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr, IHqlExpression * dataset, IHqlExpression * doneFirstVar) { node_operator op = expr->getOperator(); switch (op) { case no_exists: { OwnedHqlExpr optimized = queryOptimizedExists(ctx, expr, dataset); if (optimized) { assignBound(ctx, target, optimized); return; } break; } case no_count: { CHqlBoundExpr temp; if (canBuildOptimizedCount(ctx, dataset, temp)) { OwnedHqlExpr thisCount = temp.getTranslatedExpr(); buildIncrementAssign(ctx, target, thisCount); return; } break; } } switch (dataset->getOperator()) { case no_if: { BuildCtx subctx(ctx); IHqlStmt * stmt = buildFilterViaExpr(subctx, dataset->queryChild(0)); doBuildAssignAggregateLoop(subctx, target, expr, dataset->queryChild(1), doneFirstVar); subctx.selectElse(stmt); doBuildAssignAggregateLoop(subctx, target, expr, dataset->queryChild(2), doneFirstVar); return; } case no_addfiles: { doBuildAssignAggregateLoop(ctx, target, expr, dataset->queryChild(0), doneFirstVar); doBuildAssignAggregateLoop(ctx, target, expr, dataset->queryChild(1), doneFirstVar); return; } case no_null: return; } LinkedHqlExpr arg = expr->queryChild(1); IHqlExpression * oldDataset = expr->queryChild(0); //If no_if or no_addfiles has been optimized above then the selector for the argument will have changed => map it. if (arg && (dataset != oldDataset)) arg.setown(replaceSelector(arg, oldDataset, dataset)); bool needToBreak = (op == no_exists); if (needToBreak) { //if it can have at most one row (fairly strange code!) then don't add a break //unless it was deliberately a choosen to restrict the number of iterations. if (hasNoMoreRowsThan(dataset, 1) && (dataset->getOperator() != no_choosen)) needToBreak = false; } BuildCtx loopctx(ctx); buildDatasetIterate(loopctx, dataset, needToBreak); switch (op) { case no_exists: buildExprAssign(loopctx, target, queryBoolExpr(true)); if (needToBreak) loopctx.addBreak(); break; case no_count: { OwnedHqlExpr inc = createValue(no_postinc, makeVoidType(), LINK(target.expr)); loopctx.addExpr(inc); break; } case no_sum: { OwnedHqlExpr cseArg = options.spotCSE ? spotScalarCSE(arg) : LINK(arg); buildIncrementAssign(loopctx, target, cseArg); break; } case no_min: case no_max: { BuildCtx maxctx(loopctx); OwnedHqlExpr resultExpr = target.getTranslatedExpr(); OwnedHqlExpr cseArg = options.spotCSE ? spotScalarCSE(arg) : LINK(arg); OwnedHqlExpr simpleArg = buildSimplifyExpr(loopctx, cseArg); OwnedHqlExpr test = createBoolExpr((op == no_min) ? no_lt : no_gt, LINK(simpleArg), LINK(resultExpr)); if (doneFirstVar) { IHqlExpression * firstTest = createValue(no_not, makeBoolType(), LINK(doneFirstVar)); test.setown(createBoolExpr(no_or, firstTest, test.getClear())); } buildFilter(maxctx, test); buildExprAssign(maxctx, target, simpleArg); if (doneFirstVar) buildAssignToTemp(maxctx, doneFirstVar, queryBoolExpr(true)); break; } default: assertex(!"unknown aggregate on child datasets"); break; } } bool assignAggregateDirect(const CHqlBoundTarget & target, IHqlExpression * expr) { node_operator op = expr->getOperator(); ITypeInfo * type = expr->queryType(); ITypeInfo * tempType = op == no_count ? unsignedType : type; if (!isSameUnqualifiedType(target.queryType(), tempType)) return false; //For exists/count/sum use a temporary variable, and then assign rather than accumulating directly in the target switch (op) { case no_sum: if (type->getTypeCode() != type_int) break; //fall through case no_exists: case no_count: if (target.expr->getOperator() != no_variable) return false; break; } return true; } static bool isNullValueMinimumValue(ITypeInfo * type) { switch (type->getTypeCode()) { case type_int: case type_swapint: case type_decimal: return !type->isSigned(); case type_data: case type_qstring: return true; } return false; } void HqlCppTranslator::doBuildAssignAggregate(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * _expr) { OwnedHqlExpr expr = normalizeAnyDatasetAliases(_expr); if (assignAggregateDirect(target, expr)) { IHqlExpression * dataset = expr->queryChild(0); OwnedHqlExpr resultExpr = target.getTranslatedExpr(); node_operator op = expr->getOperator(); switch (op) { case no_exists: buildExprAssign(ctx, target, queryBoolExpr(false)); break; default: { OwnedHqlExpr null = createNullExpr(target.queryType()); buildExprAssign(ctx, target, null); break; } } OwnedHqlExpr doneFirstVar; if ((op == no_min) || ((op == no_max) && !isNullValueMinimumValue(target.queryType()))) { doneFirstVar.setown(ctx.getTempDeclare(queryBoolType(), queryBoolExpr(false))); } doBuildAssignAggregateLoop(ctx, target, expr, dataset, doneFirstVar); } else { doBuildExprAssign(ctx, target, expr); } } //--------------------------------------------------------------------------- bool HqlCppTranslator::canBuildOptimizedCount(BuildCtx & ctx, IHqlExpression * dataset, CHqlBoundExpr & tgt) { switch (dataset->getOperator()) { case no_select: { if (isMultiLevelDatasetSelector(dataset, false)) return false; Owned selector = buildReference(ctx, dataset); CHqlBoundExpr temp; selector->get(ctx, temp); tgt.expr.setown(getBoundCount(temp)); return true; } break; default: if (!options.tempDatasetsUseLinkedRows) break; if (!alwaysEvaluatesToBound(dataset)) break; //fall through case no_rows: case no_null: { CHqlBoundExpr temp; buildDataset(ctx, dataset, temp, FormatNatural); tgt.expr.setown(getBoundCount(temp)); return true; } } #if 0 //This is improves a few obscure cases (normally in the global context). I'm not convinced it is worth the extra cycles. //Could also remove the bound.count test. CHqlBoundExpr bound; if (ctx.getMatchExpr(dataset, bound) && bound.count) { tgt.expr.setown(getBoundCount(bound)); return true; } #endif return false; } void HqlCppTranslator::doBuildExprCount(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt) { if (expr->hasProperty(keyedAtom)) throwError(HQLERR_KeyedCountNonKeyable); IHqlExpression * dataset = expr->queryChild(0); CHqlBoundExpr temp; if (canBuildOptimizedCount(ctx, dataset, temp)) { OwnedHqlExpr translated = temp.getTranslatedExpr(); OwnedHqlExpr cast = ensureExprType(translated, expr->queryType()); buildExpr(ctx, cast, tgt); } else doBuildExprAggregate(ctx, expr, tgt); } //--------------------------------------------------------------------------- IHqlExpression * HqlCppTranslator::queryOptimizedExists(BuildCtx & ctx, IHqlExpression * expr, IHqlExpression * dataset) { node_operator dsOp = dataset->getOperator(); //really this is isSimple() CHqlBoundExpr optimized; bool canOptimizeCount = canBuildOptimizedCount(ctx, dataset, optimized); node_operator op = (expr->getOperator() == no_exists) ? no_ne : no_eq; bool specialCase = false; switch (dsOp) { case no_select: specialCase = canOptimizeCount; break; default: specialCase = !canOptimizeCount && alwaysEvaluatesToBound(dataset); break; } if (specialCase) { CHqlBoundExpr temp; buildDataset(ctx, dataset, temp, FormatNatural); IHqlExpression * test; if (temp.count) test = LINK(temp.count); else test = getBoundSize(temp); return createBoolExpr(op, test, createConstant(test->queryType()->castFrom(false, 0))); } else if (canOptimizeCount) { IHqlExpression * count = optimized.expr; return createBoolExpr(op, LINK(count), createConstant(count->queryType()->castFrom(false, 0))); } return NULL; } void HqlCppTranslator::doBuildExprExists(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt) { OwnedHqlExpr optimized = queryOptimizedExists(ctx, expr, expr->queryChild(0)); if (optimized) tgt.expr.setown(optimized.getClear()); else doBuildExprAggregate(ctx, expr, tgt); } //--------------------------------------------------------------------------- static IHqlExpression * createMinMax(node_operator compareOp, ITypeInfo * type, IHqlExpression * left, IHqlExpression * right) { return createValue(no_if, LINK(type), createBoolExpr(compareOp, LINK(left), LINK(right)), LINK(left), LINK(right)); } bool HqlCppTranslator::doBuildAggregateMinMaxList(BuildCtx & ctx, const CHqlBoundTarget * target, IHqlExpression * expr, IHqlExpression * list, CHqlBoundExpr * tgt, node_operator compareOp) { OwnedHqlExpr value; unsigned max = list->numChildren(); switch(max) { case 0: value.setown(createNullExpr(expr)); break; case 1: value.set(list->queryChild(0)); break; case 2: case 3: { OwnedHqlExpr simple[3]; for (unsigned i=0; i < max; i++) { CHqlBoundExpr bound; buildSimpleExpr(ctx, list->queryChild(i), bound); simple[i].setown(bound.getTranslatedExpr()); } ITypeInfo * type = expr->queryType(); if (max == 2) value.setown(createMinMax(compareOp, type, simple[0], simple[1])); else { OwnedHqlExpr cmp02 = createMinMax(compareOp, type, simple[0], simple[2]); OwnedHqlExpr cmp12 = createMinMax(compareOp, type, simple[1], simple[2]); value.setown(createValue(no_if, expr->getType(), createBoolExpr(compareOp, LINK(simple[0]), LINK(simple[1])), LINK(cmp02), LINK(cmp12))); } } } if (value) { buildExprOrAssign(ctx, target, value, tgt); return true; } { CHqlBoundTarget temp; createTempFor(ctx, expr, temp); buildExprAssign(ctx, temp, list->queryChild(0)); OwnedHqlExpr best = temp.getTranslatedExpr(); for (unsigned i=1; i < list->numChildren(); i++) { CHqlBoundExpr bound; buildSimpleExpr(ctx, list->queryChild(i), bound); OwnedHqlExpr simple = bound.getTranslatedExpr(); BuildCtx subctx(ctx); OwnedHqlExpr cond = createBoolExpr(compareOp, LINK(simple), LINK(best)); buildFilter(subctx, cond); buildExprAssign(subctx, temp, simple); } buildExprOrAssign(ctx, target, best, tgt); return true; } } void HqlCppTranslator::doBuildAggregateList(BuildCtx & ctx, const CHqlBoundTarget * target, IHqlExpression * expr, CHqlBoundExpr * tgt) { OwnedHqlExpr list = normalizeListCasts(expr->queryChild(0)); if (list->getOperator() == no_alias_scope) { expandAliasScope(ctx, list); list.set(list->queryChild(0)); } node_operator aggOp; switch (expr->getOperator()) { case no_existslist: { //Fixed length lists should have been optimized away CHqlBoundExpr bound; buildExpr(ctx, list, bound); OwnedHqlExpr test; if (bound.count) test.set(bound.count); else test.setown(getBoundLength(bound)); OwnedHqlExpr value = createValue(no_ne, makeBoolType(), LINK(test), ensureExprType(queryZero(), test->queryType())); OwnedHqlExpr translated = createTranslated(value); buildExprOrAssign(ctx, target, translated, tgt); return; } case no_countlist: { //Fixed length lists should have been optimized away CHqlBoundExpr bound; buildExpr(ctx, list, bound); OwnedHqlExpr test = getBoundCount(bound); OwnedHqlExpr value = ensureExprType(test, expr->queryType()); OwnedHqlExpr translated = createTranslated(value); buildExprOrAssign(ctx, target, translated, tgt); return; } case no_sumlist: aggOp = no_sumgroup; if (list->getOperator() == no_list) { HqlExprArray args; ITypeInfo * exprType = expr->queryType(); ForEachChild(i, list) args.append(*ensureExprType(list->queryChild(i), exprType)); OwnedHqlExpr balanced = createBalanced(no_add, exprType, args); if (!balanced) balanced.setown(ensureExprType(queryZero(), exprType)); buildExprOrAssign(ctx, target, balanced, tgt); return; } //special case fixed length lists break; case no_minlist: aggOp = no_mingroup; if (list->getOperator() == no_list) { if (doBuildAggregateMinMaxList(ctx, target, expr, list, tgt, no_lt)) return; } break; case no_maxlist: aggOp = no_maxgroup; if (list->getOperator() == no_list) { if (doBuildAggregateMinMaxList(ctx, target, expr, list, tgt, no_gt)) return; } break; default: throwUnexpectedOp(expr->getOperator()); } ITypeInfo * elemType = list->queryType()->queryChildType(); if (!elemType) elemType = defaultIntegralType; //Default implementation in terms of a dataset OwnedHqlExpr field = createField(valueAtom, LINK(elemType), NULL); OwnedHqlExpr record = createRecord(field); OwnedHqlExpr ds = createDataset(no_temptable, LINK(list), LINK(record)); OwnedHqlExpr aggField = createField(valueAtom, expr->getType(), NULL); OwnedHqlExpr aggRecord = createRecord(aggField); OwnedHqlExpr self = createSelector(no_self, aggRecord, NULL); OwnedHqlExpr aggExpr = createValue(aggOp, expr->getType(), createSelectExpr(LINK(ds), LINK(field))); OwnedHqlExpr aggAssign = createAssign(createSelectExpr(LINK(self), LINK(aggField)), LINK(aggExpr)); OwnedHqlExpr aggTransform = createValue(no_newtransform, makeTransformType(aggRecord->getType()), LINK(aggAssign)); OwnedHqlExpr agg = createDataset(no_newaggregate, LINK(ds), createComma(LINK(aggRecord), LINK(aggTransform))); OwnedHqlExpr result = createNewSelectExpr(createRow(no_selectnth, LINK(agg), createConstantOne()), LINK(aggField)); buildExprOrAssign(ctx, target, result, tgt); } //--------------------------------------------------------------------------- static HqlTransformerInfo graphIndependanceCheckerInfo("GraphIndependanceChecker"); class GraphIndependanceChecker : public NewHqlTransformer { public: GraphIndependanceChecker(IHqlExpression * _graph) : NewHqlTransformer(graphIndependanceCheckerInfo), graph(_graph) { independent = true; } void analyseExpr(IHqlExpression * expr) { if (!independent || alreadyVisited(expr)) return; switch (expr->getOperator()) { case no_getgraphresult: case no_getgraphloopresultset: case no_getgraphloopresult: if (expr->queryChild(1) == graph) { independent = false; return; } break; } NewHqlTransformer::analyseExpr(expr); } inline bool isIndependent() const { return independent; } protected: LinkedHqlExpr graph; bool independent; }; bool isGraphIndependent(IHqlExpression * expr, IHqlExpression * graph) { switch (expr->getOperator()) { case no_constant: return true; } GraphIndependanceChecker checker(graph); checker.analyse(expr, 0); return checker.isIndependent(); } ///-------------------------------------------------------------------------------------------------------------------- IHqlExpression * createCounterAsGraphResult(IHqlExpression * counter, IHqlExpression * represents, unsigned seq) { OwnedHqlExpr value = createScalarFromGraphResult(counter->queryType(), unsignedType, represents, seq); OwnedHqlExpr internalAttr = createAttribute(internalAtom); return createAlias(value, internalAttr); } ChildGraphExprBuilder::ChildGraphExprBuilder(unsigned _numInputs) : numInputs(_numInputs) { numOutputs=0; represents.setown(createAttribute(graphAtom, createUniqueId())); resultsExpr.setown(createAttribute(resultsAtom, LINK(represents))); } IHqlExpression * ChildGraphExprBuilder::addDataset(IHqlExpression * expr) { OwnedHqlExpr resultNumExpr; ForEachItemIn(i, results) { IHqlExpression & curSetResult = results.item(i); if (expr->queryBody() == curSetResult.queryChild(0)->queryBody()) { resultNumExpr.set(curSetResult.queryChild(2)); break; } } if (!resultNumExpr) { resultNumExpr.setown(getSizetConstant(numResults())); results.append(*createValue(no_setgraphresult, makeVoidType(), LINK(expr), LINK(represents), LINK(resultNumExpr))); numOutputs++; } HqlExprArray args; args.append(*LINK(expr->queryRecord())); args.append(*LINK(represents)); args.append(*LINK(resultNumExpr)); if (isGrouped(expr)) args.append(*createAttribute(groupedAtom)); if (!expr->isDataset()) args.append(*createAttribute(rowAtom)); args.append(*createAttribute(externalAtom, LINK(resultsExpr))); args.append(*createAttribute(_original_Atom, LINK(expr))); IHqlExpression * recordCountAttr = queryRecordCountInfo(expr); if (recordCountAttr) args.append(*LINK(recordCountAttr)); OwnedHqlExpr ret = createDataset(no_getgraphresult, args); if (expr->isDatarow()) ret.setown(createRow(no_selectnth, LINK(ret), createComma(getSizetConstant(1), createAttribute(noBoundCheckAtom)))); return ret.getClear(); } void ChildGraphExprBuilder::addAction(IHqlExpression * expr) { results.append(*LINK(expr)); } unsigned ChildGraphExprBuilder::addInput() { unsigned id = numResults(); numInputs++; return id; } IHqlExpression * ChildGraphExprBuilder::getGraph() { HqlExprArray args; args.append(*LINK(represents)); args.append(*getSizetConstant(numResults())); args.append(*createActionList(results)); return createValue(no_childquery, makeVoidType(), args); } //--------------------------------------------------------------------------- // Child dataset processing ChildGraphBuilder::ChildGraphBuilder(HqlCppTranslator & _translator, IHqlExpression * subgraph) : translator(_translator) { represents.set(subgraph->queryChild(0)); id = translator.nextActivityId(); appendUniqueId(instanceName.append("child"), id); instanceExpr.setown(createQuoted(instanceName, makeBoolType())); resultsExpr.setown(createAttribute(resultsAtom, LINK(represents))); StringBuffer s; resultInstanceExpr.setown(createQuoted(appendUniqueId(s.append("res"), id), makeBoolType())); numResults = (unsigned)getIntValue(subgraph->queryChild(1)); IHqlExpression * actions = subgraph->queryChild(2); actions->unwindList(results, no_actionlist); } void ChildGraphBuilder::generateGraph(BuildCtx & ctx) { BuildCtx graphctx(ctx); //Make sure at least one results - because currently that's how we determine if new resourcing is being used //Remove this line once all engines use the new child queries exclusively if (numResults == 0) numResults++; OwnedHqlExpr query = createActionList(results); OwnedHqlExpr resourced = translator.getResourcedChildGraph(graphctx, query, represents, numResults, no_none); Owned extractBuilder = translator.createExtractBuilder(graphctx, PETchild, represents, resourced, true); if (!translator.queryOptions().serializeRowsetInExtract) extractBuilder->setAllowDestructor(); translator.beginExtract(graphctx, extractBuilder); translator.doBuildThorSubGraph(graphctx, resourced, SubGraphChild, id, represents); EvalContext * instance = translator.queryEvalContext(graphctx); OwnedHqlExpr retInstanceExpr; if (instance && !translator.insideOnCreate(graphctx)) retInstanceExpr.setown(instance->createGraphLookup(id, true)); else retInstanceExpr.setown(translator.doCreateGraphLookup(graphctx, graphctx, id, "this", true)); assertex(retInstanceExpr == instanceExpr); CHqlBoundExpr boundExtract; extractBuilder->endCreateExtract(boundExtract); HqlExprArray args; args.append(*LINK(instanceExpr)); args.append(*createTranslated(boundExtract.length)); args.append(*boundExtract.getTranslatedExpr()); OwnedHqlExpr call = translator.bindFunctionCall(evaluateChildQueryInstanceAtom, args); CHqlBoundExpr bound; translator.buildExpr(graphctx, call, bound); StringBuffer s; s.append("Owned "); translator.generateExprCpp(s, resultInstanceExpr); s.append(" = "); translator.generateExprCpp(s, bound.expr); s.append(";"); graphctx.addQuoted(s); translator.endExtract(graphctx, extractBuilder); ctx.associateExpr(resultsExpr, resultInstanceExpr); } void ChildGraphBuilder::generatePrefetchGraph(BuildCtx & _ctx, OwnedHqlExpr * retGraphExpr) { BuildCtx ctx(_ctx); ctx.addGroup(); BuildCtx aliasctx(ctx); aliasctx.addGroup(); OwnedHqlExpr query = createActionList(results); OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_none); Owned extractBuilder = translator.createExtractBuilder(ctx, PETchild, represents, resourced, false); createBuilderAlias(aliasctx, extractBuilder); translator.beginExtract(ctx, extractBuilder); translator.doBuildThorSubGraph(ctx, resourced, SubGraphChild, id, represents); EvalContext * instance = translator.queryEvalContext(ctx); OwnedHqlExpr retInstanceExpr; assertex(instance && !translator.insideOnCreate(ctx)); retInstanceExpr.setown(instance->createGraphLookup(id, true)); assertex(retInstanceExpr == instanceExpr); retGraphExpr->setown(retInstanceExpr.getClear()); } void ChildGraphBuilder::createBuilderAlias(BuildCtx & ctx, ParentExtract * extractBuilder) { StringBuffer s; s.append("rtlRowBuilder & "); translator.generateExprCpp(s, extractBuilder->queryExtractName()); s.append(" = builder;"); ctx.addQuoted(s); } unique_id_t ChildGraphBuilder::buildLoopBody(BuildCtx & ctx, bool multiInstance) { BuildCtx subctx(ctx); subctx.addGroup(); OwnedHqlExpr query = createActionList(results); OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_loop); //Add a flag to indicate multi instance if (multiInstance) resourced.setown(appendOwnedOperand(resourced, createAttribute(multiInstanceAtom))); bool isGlobalThorLoop = translator.targetThor() && !translator.insideChildQuery(ctx); Owned extractBuilder = isGlobalThorLoop ? translator.createExtractBuilder(ctx, PETloop, represents, GraphRemote, false) : translator.createExtractBuilder(ctx, PETloop, represents, resourced, false); createBuilderAlias(subctx, extractBuilder); translator.beginExtract(ctx, extractBuilder); translator.doBuildThorSubGraph(ctx, resourced, SubGraphChild, id, represents); translator.endExtract(ctx, extractBuilder); return id; } static HqlTransformerInfo graphLoopReplacerInfo("GraphLoopReplacer"); class GraphLoopReplacer : public NewHqlTransformer { public: GraphLoopReplacer(IHqlExpression * _rowsid, IHqlExpression * _represents, IHqlExpression * _counter, bool _isParallel) : NewHqlTransformer(graphLoopReplacerInfo), rowsid(_rowsid), represents(_represents), counter(_counter), isParallel(_isParallel) { } virtual IHqlExpression * createTransformed(IHqlExpression * expr) { switch (expr->getOperator()) { case no_counter: if (expr->queryBody() == counter) { if (isParallel) { HqlExprArray args; args.append(*LINK(represents)); // unwindChildren(args, expr); OwnedHqlExpr ret = createValue(no_loopcounter, expr->getType(), args); //Yuk: Wrap this in an alias to ensure it is evaluated at the correct place. //there has to be a better way..... We could... //a) strictly defined when it can be evaluated - e.g., ctx->defines(graph) && (!parentctx || !parentctx->definesGraph) //b) set a flag in the expression to indicate forced evaluation (even worse than the alias) //c) add the code to evaluate no_loopcounter inside evaluateInContext return createAlias(ret, internalAttrExpr); } else { counterResult.setown(createCounterAsGraphResult(counter, represents, 0)); return LINK(counterResult); } } break; case no_rowsetindex: { IHqlExpression * rowset = expr->queryChild(0); if (rowset->getOperator() != no_rowset) break; IHqlExpression * rows = rowset->queryChild(0); if (rows->queryChild(1) != rowsid) break; HqlExprArray args; args.append(*LINK(rows->queryChild(0)->queryRecord())); args.append(*LINK(represents)); args.append(*transform(expr->queryChild(1))); return createDataset(no_getgraphloopresult, args); } case no_rowset: { IHqlExpression * rows = expr->queryChild(0); if (rows->queryChild(1) != rowsid) break; HqlExprArray args; args.append(*LINK(rows->queryChild(0)->queryRecord())); args.append(*LINK(represents)); return createValue(no_getgraphloopresultset, expr->getType(), args); } } return NewHqlTransformer::createTransformed(expr); } inline IHqlExpression * queryCounterResult() { return counterResult; } protected: IHqlExpression * rowsid; IHqlExpression * represents; IHqlExpression * counter; OwnedHqlExpr counterResult; bool isParallel; }; unique_id_t ChildGraphBuilder::buildGraphLoopBody(BuildCtx & ctx, bool isParallel) { BuildCtx subctx(ctx); subctx.addGroup(); OwnedHqlExpr query = createActionList(results); translator.traceExpression("Before Loop resource", query); OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_loop); translator.traceExpression("After Loop resource", resourced); //Add a flag to indicate multi instance if (isParallel) { HqlExprArray args; unwindChildren(args, resourced); args.append(*createAttribute(multiInstanceAtom)); args.append(*createAttribute(delayedAtom)); resourced.setown(resourced->clone(args)); } bool isGlobalThorLoop = translator.targetThor() && !translator.insideChildQuery(ctx); Owned extractBuilder = isGlobalThorLoop ? translator.createExtractBuilder(ctx, PETloop, represents, GraphRemote, false) : translator.createExtractBuilder(ctx, PETloop, represents, resourced, false); createBuilderAlias(subctx, extractBuilder); translator.beginExtract(ctx, extractBuilder); translator.doBuildThorSubGraph(ctx, resourced, SubGraphChild, id, represents); translator.endExtract(ctx, extractBuilder); return id; } unique_id_t ChildGraphBuilder::buildRemoteGraph(BuildCtx & ctx) { BuildCtx subctx(ctx); subctx.addGroup(); OwnedHqlExpr query = createActionList(results); OwnedHqlExpr resourced = translator.getResourcedChildGraph(ctx, query, represents, numResults, no_allnodes); Owned extractBuilder = translator.createExtractBuilder(ctx, PETremote, represents, GraphRemote, false); createBuilderAlias(subctx, extractBuilder); translator.beginExtract(ctx, extractBuilder); translator.doBuildThorSubGraph(ctx, resourced, SubGraphChild, id, represents); translator.endExtract(ctx, extractBuilder); return id; } void HqlCppTranslator::buildChildGraph(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * represents= expr->queryChild(0); OwnedHqlExpr resultsExpr = createAttribute(resultsAtom, LINK(represents)); //Shouldn't really happen, but if this graph has already benn called just use the results if (ctx.queryMatchExpr(resultsExpr)) return; ChildGraphBuilder graphBuilder(*this, expr); graphBuilder.generateGraph(ctx); } void HqlCppTranslator::beginExtract(BuildCtx & ctx, ParentExtract * extractBuilder) { ctx.associate(*extractBuilder); } void HqlCppTranslator::endExtract(BuildCtx & ctx, ParentExtract * extractBuilder) { extractBuilder->endUseExtract(ctx); ctx.removeAssociation(extractBuilder); } void HqlCppTranslator::buildAssignChildDataset(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr) { switch (expr->getOperator()) { case no_call: case no_externalcall: case no_libraryinput: buildDatasetAssign(ctx, target, expr); return; } OwnedHqlExpr call; { ChildGraphExprBuilder builder(0); call.setown(builder.addDataset(expr)); OwnedHqlExpr subquery = builder.getGraph(); buildStmt(ctx, subquery); } buildExprAssign(ctx, target, call); } IHqlExpression * HqlCppTranslator::getResourcedChildGraph(BuildCtx & ctx, IHqlExpression * expr, IHqlExpression * graphIdExpr, unsigned numResults, node_operator graphKind) { if (options.paranoidCheckNormalized || options.paranoidCheckDependencies) DBGLOG("Before resourcing a child graph"); LinkedHqlExpr resourced = expr; checkNormalized(ctx, resourced); unsigned csfFlags = CSFindex|options.optimizeDiskFlag; switch (targetClusterType) { case HThorCluster: csfFlags |= CSFcompoundSpill; break; case ThorCluster: case ThorLCRCluster: //Don't compound spills inside a child query because it can cause non remote projects to become remote //And we'll also probably be using the roxie code to implement break; case RoxieCluster: break; } { unsigned time = msTick(); CompoundSourceTransformer transformer(*this, CSFpreload|csfFlags); resourced.setown(transformer.process(resourced)); checkNormalized(ctx, resourced); DEBUG_TIMER("EclServer: tree transform: optimize disk read", msTick()-time); } if (options.optimizeChildGraph) { unsigned time = msTick(); traceExpression("BeforeOptimizeSub", resourced); resourced.setown(optimizeHqlExpression(resourced, getOptimizeFlags()|HOOcompoundproject)); traceExpression("AfterOptimizeSub", resourced); DEBUG_TIMER("EclServer: optimize graph", msTick()-time); } traceExpression("BeforeResourcing Child", resourced); cycle_t time = msTick(); HqlExprCopyArray activeRows; gatherActiveCursors(ctx, activeRows); if (graphKind == no_loop) { bool insideChild = insideChildQuery(ctx); resourced.setown(resourceLoopGraph(*this, activeRows, resourced, targetClusterType, graphIdExpr, &numResults, insideChild)); } else resourced.setown(resourceNewChildGraph(*this, activeRows, resourced, targetClusterType, graphIdExpr, &numResults)); DEBUG_TIMER("EclServer: resource graph", msTick()-time); checkNormalized(ctx, resourced); traceExpression("AfterResourcing Child", resourced); //Convert queries on preloaded into compound activities - before resourcing so keyed gets done correctly // Second attempt to spot compound disk reads - this time of spill files. Since resourcing has removed // any sharing we don't need to bother about sharing. { unsigned time = msTick(); CompoundSourceTransformer transformer(*this, csfFlags); resourced.setown(transformer.process(resourced)); DEBUG_TIMER("EclServer: tree transform: optimize disk read", msTick()-time); } //Now call the optimizer again - the main purpose is to move projects over limits and into compound index/disk reads if (options.optimizeChildGraph) { unsigned time = msTick(); traceExpression("BeforeOptimize2", resourced); resourced.setown(optimizeHqlExpression(resourced, getOptimizeFlags()|HOOcompoundproject)); traceExpression("AfterOptimize2", resourced); DEBUG_TIMER("EclServer: optimize graph", msTick()-time); } if (numResults == 0) numResults++; HqlExprArray children; resourced->unwindList(children, no_actionlist); children.append(*createAttribute(numResultsAtom, getSizetConstant(numResults))); children.append(*LINK(graphIdExpr)); resourced.setown(createValue(no_subgraph, makeVoidType(), children)); if (options.paranoidCheckNormalized || options.paranoidCheckDependencies) DBGLOG("After resourcing a child graph"); return resourced.getClear(); } void HqlCppTranslator::buildChildDataset(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt) { if (expr->isPure() && ctx.getMatchExpr(expr, tgt)) return; LoopInvariantHelper helper; BuildCtx bestctx(ctx); if (options.optimizeLoopInvariant) helper.getBestContext(bestctx, expr); CHqlBoundTarget temp; //MORE: Should have similar code to buildTempExpr() createTempFor(bestctx, expr, temp); buildAssignChildDataset(bestctx, temp, expr); tgt.setFromTarget(temp); if (expr->isPure()) bestctx.associateExpr(expr, tgt); } unique_id_t HqlCppTranslator::buildGraphLoopSubgraph(BuildCtx & ctx, IHqlExpression * dataset, IHqlExpression * selSeq, IHqlExpression * rowsid, IHqlExpression * body, IHqlExpression * counter, bool multiInstance) { ChildGraphExprBuilder graphBuilder(0); OwnedHqlExpr transformedBody; OwnedHqlExpr counterResult; IHqlExpression * graphid = graphBuilder.queryRepresents(); { const bool isParallel = multiInstance; GraphLoopReplacer replacer(rowsid, graphid, counter, isParallel); transformedBody.setown(replacer.transformRoot(body)); counterResult.set(replacer.queryCounterResult()); } if (counterResult) graphBuilder.addInput(); OwnedHqlExpr result = createValue(no_setgraphloopresult, makeVoidType(), LINK(transformedBody), LINK(graphid)); graphBuilder.addAction(result); OwnedHqlExpr subquery = graphBuilder.getGraph(); ChildGraphBuilder builder(*this, subquery); return builder.buildGraphLoopBody(ctx, multiInstance); } unique_id_t HqlCppTranslator::buildRemoteSubgraph(BuildCtx & ctx, IHqlExpression * dataset) { ChildGraphExprBuilder graphBuilder(0); if (dataset->isAction()) { graphBuilder.addAction(dataset); } else { OwnedHqlExpr ignoredResult = graphBuilder.addDataset(dataset); } OwnedHqlExpr subquery = graphBuilder.getGraph(); ChildGraphBuilder builder(*this, subquery); return builder.buildRemoteGraph(ctx); } //--------------------------------------------------------------------------- // Functions to check whether a dataset can be evaluated inline or not. //MORE: These should probably be split out into an hqlinline.cpp bool HqlCppTranslator::canIterateInline(BuildCtx * ctx, IHqlExpression * expr) { return (isInlineOk() && ::canIterateInline(ctx, expr)); } bool HqlCppTranslator::canAssignInline(BuildCtx * ctx, IHqlExpression * expr) { if (!isInlineOk()) return false; return options.allowInlineSpill ? ::canProcessInline(ctx, expr) : ::canAssignInline(ctx, expr); } bool HqlCppTranslator::canEvaluateInline(BuildCtx * ctx, IHqlExpression * expr) { if (!isInlineOk()) return false; return options.allowInlineSpill ? ::canProcessInline(ctx, expr) : ::canEvaluateInline(ctx, expr); } bool HqlCppTranslator::canProcessInline(BuildCtx * ctx, IHqlExpression * expr) { if (!isInlineOk()) return false; return ::canProcessInline(ctx, expr); } bool HqlCppTranslator::isInlineOk() { if (!activeGraphCtx) return true; return true; } IHqlExpression * HqlCppTranslator::buildSpillChildDataset(BuildCtx & ctx, IHqlExpression * expr) { CHqlBoundExpr bound; buildChildDataset(ctx, expr, bound); return bound.getTranslatedExpr(); } IHqlExpression * HqlCppTranslator::forceInlineAssignDataset(BuildCtx & ctx, IHqlExpression * expr) { loop { CHqlBoundExpr bound; if (expr->isPure() && ctx.getMatchExpr(expr, bound)) return bound.getTranslatedExpr(); if (canProcessInline(&ctx, expr) || (expr->getOperator() == no_translated)) return LINK(expr); switch (expr->getOperator()) { case no_compound: buildStmt(ctx, expr->queryChild(0)); expr = expr->queryChild(1); break; default: return buildSpillChildDataset(ctx, expr); } } } //--------------------------------------------------------------------------- // Dataset temp creation IHqlExpression * createGetResultFromWorkunitDataset(IHqlExpression * expr) { IHqlExpression * name = queryPropertyChild(expr, nameAtom, 0); if (name) name = createExprAttribute(namedAtom, LINK(name)); assertex(expr->isDataset()); return createDataset(no_getresult, LINK(expr->queryRecord()), createComma(LINK(expr->queryProperty(sequenceAtom)), name)); } void HqlCppTranslator::buildAssignSerializedDataset(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr) { OwnedITypeInfo serializedType = getSerializedForm(expr->queryType()); assertex(recordTypesMatch(target.queryType(), serializedType)); HqlExprArray args; args.append(*createRowSerializer(ctx, expr->queryRecord(), serializerAtom)); args.append(*LINK(expr)); OwnedHqlExpr call = bindFunctionCall(rowset2DatasetXAtom, args); buildExprAssign(ctx, target, call); } void HqlCppTranslator::buildSerializedDataset(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt) { CHqlBoundTarget target; OwnedITypeInfo serializedType = getSerializedForm(expr->queryType()); createTempFor(ctx, serializedType, target, typemod_none, FormatBlockedDataset); buildAssignSerializedDataset(ctx, target, expr); tgt.setFromTarget(target); } void HqlCppTranslator::buildAssignLinkedDataset(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr) { OwnedITypeInfo serializedType = getSerializedForm(target.queryType()); assertex(recordTypesMatch(serializedType, expr->queryType())); IHqlExpression * record = ::queryRecord(target.queryType()); HqlExprArray args; args.append(*createRowSerializer(ctx, record, deserializerAtom)); args.append(*LINK(expr)); OwnedHqlExpr call = bindFunctionCall(isGrouped(expr) ? groupedDataset2RowsetXAtom : dataset2RowsetXAtom, args, target.queryType()); buildExprAssign(ctx, target, call); } void HqlCppTranslator::buildLinkedDataset(BuildCtx & ctx, ITypeInfo * type, IHqlExpression * expr, CHqlBoundExpr & tgt) { CHqlBoundTarget target; createTempFor(ctx, type, target, typemod_none, FormatLinkedDataset); if (hasLinkedRow(expr->queryType())) buildDatasetAssign(ctx, target, expr); else buildAssignLinkedDataset(ctx, target, expr); tgt.setFromTarget(target); } void HqlCppTranslator::ensureDatasetFormat(BuildCtx & ctx, ITypeInfo * type, CHqlBoundExpr & tgt, ExpressionFormat format) { switch (format) { case FormatBlockedDataset: if (isArrayRowset(tgt.queryType())) { OwnedHqlExpr deserializedExpr = tgt.getTranslatedExpr(); LinkedHqlExpr savedCount = tgt.count; buildSerializedDataset(ctx, deserializedExpr, tgt); if (savedCount && !isFixedWidthDataset(deserializedExpr)) tgt.count.set(savedCount); return; } break; case FormatLinkedDataset: if (!hasLinkCountedModifier(tgt.queryType())) { OwnedHqlExpr serializedExpr = tgt.getTranslatedExpr(); buildLinkedDataset(ctx, type, serializedExpr, tgt); return; } break; case FormatArrayDataset: if (!isArrayRowset(tgt.queryType())) { OwnedHqlExpr serializedExpr = tgt.getTranslatedExpr(); buildLinkedDataset(ctx, type, serializedExpr, tgt); return; } break; } } void HqlCppTranslator::buildDataset(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ExpressionFormat format) { doBuildDataset(ctx, expr, tgt, format); ensureDatasetFormat(ctx, expr->queryType(), tgt, format); } void HqlCppTranslator::doBuildDataset(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ExpressionFormat format) { if (expr->isPure() && ctx.getMatchExpr(expr, tgt)) return; /* OwnedHqlExpr transformed = normalizeAnyDatasetAliases(expr); if (transformed && (transformed != expr)) { doBuildDataset(ctx, transformed, tgt, format); ctx.associateExpr(expr, tgt); return; } */ node_operator op = expr->getOperator(); switch (op) { case no_dataset_alias: if (!expr->hasProperty(_normalized_Atom)) { OwnedHqlExpr uniqueChild = normalizeDatasetAlias(expr); doBuildDataset(ctx, uniqueChild, tgt, format); } else doBuildDataset(ctx, expr->queryChild(0), tgt, format); return; case no_alias: doBuildExprAlias(ctx, expr, &tgt); return; case no_owned_ds: buildTempExpr(ctx, expr, tgt); return; case no_null: { tgt.count.setown(getSizetConstant(0)); tgt.length.setown(getSizetConstant(0)); IHqlExpression * record = expr->queryRecord(); ITypeInfo * type = makeTableType(makeRowType(record->getType()), NULL, NULL, NULL); if ((format == FormatLinkedDataset) || (format == FormatArrayDataset)) type = makeAttributeModifier(type, getLinkCountedAttr()); tgt.expr.setown(createValue(no_nullptr, makeReferenceModifier(type))); return; } case no_translated: expandTranslated(expr, tgt); return; case no_select: { if (isMultiLevelDatasetSelector(expr, false)) break; Owned selected = buildReference(ctx, expr); selected->get(ctx, tgt); return; } case no_libraryinput: if (!buildExprInCorrectContext(ctx, expr, tgt, false)) throwUnexpected(); return; case no_call: case no_externalcall: if (!hasStreamedModifier(expr->queryType())) { buildTempExpr(ctx, expr, tgt); return; } break; case no_newaggregate: if (canAssignInline(&ctx, expr)) { Owned tempRow = declareTempAnonRow(ctx, ctx, expr); Owned rowBuilder = createRowBuilder(ctx, tempRow); Owned createdRef = createReferenceSelector(rowBuilder); BuildCtx subctx(ctx); subctx.addGroup(); doBuildRowAssignAggregate(subctx, createdRef, expr); finalizeTempRow(ctx, tempRow, rowBuilder); convertBoundRowToDataset(ctx, tgt, tempRow, format); return; } break; case no_id2blob: doBuildExprIdToBlob(ctx, expr, tgt); return; case no_rows: { if (!buildExprInCorrectContext(ctx, expr, tgt, false)) throwError(HQLERR_RowsUsedOutsideContext); return; } case no_limit: if (expr->hasProperty(skipAtom) || expr->hasProperty(onFailAtom)) break; doBuildDatasetLimit(ctx, expr, tgt, format); return; case no_compound_childread: case no_compound_childnormalize: case no_compound_childaggregate: case no_compound_selectnew: case no_compound_inline: case no_distributed: case no_preservemeta: case no_sorted: case no_nofold: case no_nohoist: case no_section: case no_sectioninput: buildDataset(ctx, expr->queryChild(0), tgt, format); return; case no_forcegraph: #ifdef _DEBUG throwUnexpected(); #endif buildDataset(ctx, expr->queryChild(0), tgt, format); return; case no_getgraphresult: doBuildExprGetGraphResult(ctx, expr, tgt, format); return; case no_getresult: case no_workunit_dataset: if (!isGrouped(expr)) { doBuildExprGetResult(ctx, expr, tgt); return; } break; case no_skip: { buildStmt(ctx, expr); OwnedHqlExpr null = createNullExpr(expr); buildDataset(ctx, null, tgt, format); return; } case no_serialize: if (isDummySerializeDeserialize(expr)) doBuildDataset(ctx, expr->queryChild(0)->queryChild(0), tgt, format); else buildSerializedDataset(ctx, expr->queryChild(0), tgt); return; case no_deserialize: if (isDummySerializeDeserialize(expr)) doBuildDataset(ctx, expr->queryChild(0)->queryChild(0), tgt, format); else buildLinkedDataset(ctx, expr->queryType(), expr->queryChild(0), tgt); return; case no_datasetfromrow: { IHqlExpression * row = expr->queryChild(0); if (isAlwaysActiveRow(row) && (format == FormatNatural)) { Owned selector = buildActiveRow(ctx, row); BuildCtx groupctx(ctx); groupctx.addGroup(); BoundRow * bound = bindSelectorAsRootRow(groupctx, selector, row); convertBoundRowToDataset(groupctx, tgt, bound, format); tgt.count.setown(getSizetConstant(1)); ctx.associateExpr(expr, tgt); return; } break; } case no_inlinetable: case no_inlinedictionary: if (doBuildDatasetInlineTable(ctx, expr, tgt, format)) return; break; case no_compound: { buildStmt(ctx, expr->queryChild(0)); buildDataset(ctx, expr->queryChild(1), tgt, format); return; } } bool singleRow = hasSingleRow(expr); bool useTempRow = singleRow && canAssignInline(&ctx, expr) && (format != FormatLinkedDataset) && (format != FormatArrayDataset); //Conditional row assignment if variable length causes offset to be recalculated outside of the if() //if (useTempRow && (op == no_if) && isVariableSizeRecord(expr->queryRecord())) // useTempRow = false; if (useTempRow) { Owned tempRow = declareTempAnonRow(ctx, ctx, expr); Owned rowBuilder = createRowBuilder(ctx, tempRow); Owned builder = createSingleRowTempDatasetBuilder(expr->queryRecord(), rowBuilder); builder->buildDeclare(ctx); buildDatasetAssign(ctx, builder, expr); //builder->buildFinish(ctx, tempTarget); finalizeTempRow(ctx, tempRow, rowBuilder); convertBoundRowToDataset(ctx, tgt, tempRow, format); } else { if (!canAssignInline(&ctx, expr)) { CHqlBoundTarget tempTarget; createTempFor(ctx, expr->queryType(), tempTarget, typemod_none, format); buildDatasetAssign(ctx, tempTarget, expr); tgt.setFromTarget(tempTarget); //buildTempExpr(ctx, expr, tgt); // can't use this because it causes recursion on no_selectnth } else { Owned builder; IHqlExpression * record = expr->queryRecord(); OwnedHqlExpr serializedRecord = getSerializedForm(record); if (format == FormatNatural) { if (record != serializedRecord) ensureContextAvailable(ctx); if (!ctx.queryMatchExpr(codeContextMarkerExpr)) { if (record != serializedRecord) throwError(HQLERR_LinkedDatasetNoContext); format = FormatBlockedDataset; } else if ((record == serializedRecord) && !options.tempDatasetsUseLinkedRows) { format = FormatBlockedDataset; } else { format = FormatLinkedDataset; } } else if (record != serializedRecord) format = FormatLinkedDataset; // Have to serialize it later - otherwise it won't be compatible if (format == FormatLinkedDataset || format == FormatArrayDataset) { IHqlExpression * choosenLimit = NULL; if ((op == no_choosen) && !isChooseNAllLimit(expr->queryChild(1)) && !queryRealChild(expr, 2)) { choosenLimit = expr->queryChild(1); expr = expr->queryChild(0); } //MORE: Extract limit and choosen and pass as parameters builder.setown(createLinkedDatasetBuilder(record, choosenLimit)); } else if ((op == no_choosen) && !isChooseNAllLimit(expr->queryChild(1)) && !queryRealChild(expr, 2)) { //Build a limited builder - it is likely to be just as efficient, and often much more e.g., choosen(a+b, n) builder.setown(createChoosenDatasetBuilder(serializedRecord, expr->queryChild(1))); expr = expr->queryChild(0); } else builder.setown(createBlockedDatasetBuilder(serializedRecord)); builder->buildDeclare(ctx); buildDatasetAssign(ctx, builder, expr); builder->buildFinish(ctx, tgt); } } if (singleRow) tgt.count.setown(getSizetConstant(1)); else if (op == no_inlinetable) { IHqlExpression * transforms = expr->queryChild(0); if (!transformListContainsSkip(transforms)) tgt.count.setown(getSizetConstant(transforms->numChildren())); } ctx.associateExpr(expr, tgt); } //--------------------------------------------------------------------------- // Dataset assignment - to temp void HqlCppTranslator::buildDatasetAssign(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr) { node_operator op = expr->getOperator(); switch (op) { case no_call: case no_externalcall: if (!hasStreamedModifier(expr->queryType())) { doBuildCall(ctx, &target, expr, NULL); return; } break; case no_getgraphresult: doBuildAssignGetGraphResult(ctx, target, expr); return; case no_workunit_dataset: case no_getresult: buildExprAssign(ctx, target, expr); return; case no_null: { CHqlBoundExpr bound; buildDataset(ctx, expr, bound, isArrayRowset(expr->queryType()) ? FormatLinkedDataset : FormatBlockedDataset); OwnedHqlExpr translated = bound.getTranslatedExpr(); if (target.count) ctx.addAssign(target.count, bound.count); if (target.length) ctx.addAssign(target.length, bound.length); ctx.addAssign(target.expr, bound.expr); return; } case no_inlinedictionary: case no_inlinetable: { //This will typically generate a loop. If few items then it is more efficient to expand the assigns/clones out. if (expr->queryChild(0)->numChildren() > INLINE_TABLE_EXPAND_LIMIT) { CHqlBoundExpr bound; if (doBuildDatasetInlineTable(ctx, expr, bound, FormatNatural)) { OwnedHqlExpr translated = bound.getTranslatedExpr(); buildDatasetAssign(ctx, target, translated); return; } } break; } case no_alias: { CHqlBoundExpr bound; buildDataset(ctx, expr, bound, FormatNatural); OwnedHqlExpr translated = bound.getTranslatedExpr(); buildDatasetAssign(ctx, target, translated); return; } case no_owned_ds: { ITypeInfo * targetType = target.queryType(); if (hasLinkCountedModifier(targetType) && hasWrapperModifier(targetType)) { CHqlBoundExpr bound; buildDataset(ctx, expr->queryChild(0), bound, FormatLinkedDataset); OwnedHqlExpr compound = createValue(no_complex, bound.expr->getType(), LINK(bound.count), LINK(bound.expr)); ctx.addAssign(target.expr, compound); return; } break; } case no_compound: { buildStmt(ctx, expr->queryChild(0)); buildDatasetAssign(ctx, target, expr->queryChild(1)); return; } case no_compound_childread: case no_compound_childnormalize: case no_compound_childaggregate: case no_compound_selectnew: case no_compound_inline: case no_distributed: case no_preservemeta: case no_sorted: case no_nofold: case no_nohoist: case no_section: case no_sectioninput: buildDatasetAssign(ctx, target, expr->queryChild(0)); return; case no_serialize: if (isDummySerializeDeserialize(expr)) buildDatasetAssign(ctx, target, expr->queryChild(0)->queryChild(0)); else buildAssignSerializedDataset(ctx, target, expr->queryChild(0)); return; case no_deserialize: if (isDummySerializeDeserialize(expr)) buildDatasetAssign(ctx, target, expr->queryChild(0)->queryChild(0)); else buildAssignLinkedDataset(ctx, target, expr->queryChild(0)); return; case no_select: { bool isNew; IHqlExpression * ds = querySelectorDataset(expr, isNew); if (!isNew || ds->isDatarow()) { Owned selected = buildReference(ctx, expr); selected->assignTo(ctx, target); return; } break; } case no_typetransfer: { IHqlExpression * child = expr->queryChild(0); if (expr->isDataset() && child->isDataset()) { //Special case no-op type transfers and assignment from a call returning unknown-type dataset if (!recordTypesMatch(expr, child) && !(child->getOperator() == no_externalcall && recordTypesMatch(child, queryNullRecord()))) { CHqlBoundExpr bound; buildDataset(ctx, child, bound, FormatNatural); ITypeInfo * newType = cloneModifiers(bound.expr->queryType(), expr->queryType()); bound.expr.setown(createValue(no_typetransfer, newType, LINK(bound.expr))); OwnedHqlExpr translated = bound.getTranslatedExpr(); buildDatasetAssign(ctx, target, translated); } else buildDatasetAssign(ctx, target, child); return; } break; } } if (!canAssignInline(&ctx, expr) && (op != no_translated)) { buildAssignChildDataset(ctx, target, expr); return; } ITypeInfo * to = target.queryType(); ITypeInfo * exprType = expr->queryType(); bool targetOutOfLine = isArrayRowset(to); switch (op) { case no_limit: assertex(!expr->hasProperty(skipAtom) && !expr->hasProperty(onFailAtom)); //Do the limit check as a post test. //It means we may read more records than we need to, but the code is inline, and the code is generally much better. if (target.count) { buildDatasetAssign(ctx, target, expr->queryChild(0)); CHqlBoundExpr bound; bound.setFromTarget(target); doBuildCheckDatasetLimit(ctx, expr, bound); return; } break; case no_translated: { bool sourceOutOfLine = isArrayRowset(exprType); if (sourceOutOfLine != targetOutOfLine) { OwnedITypeInfo serializedSourceType = getSerializedForm(exprType); OwnedITypeInfo serializedTargetType = getSerializedForm(to); if (queryUnqualifiedType(serializedSourceType) == queryUnqualifiedType(serializedTargetType)) { if (targetOutOfLine) { buildAssignLinkedDataset(ctx, target, expr); } else { buildAssignSerializedDataset(ctx, target, expr); } return; } } break; } } if (recordTypesMatch(to, exprType)) { switch (op) { case no_rows: { CHqlBoundExpr bound; buildDataset(ctx, expr, bound, FormatLinkedDataset); OwnedHqlExpr translated = bound.getTranslatedExpr(); buildDatasetAssign(ctx, target, translated); return; } case no_select: { bool isNew; IHqlExpression * ds = querySelectorDataset(expr, isNew); if (isNew && !ds->isDatarow()) break; } //fall through case no_translated: case no_null: case no_id2blob: { _ATOM func = NULL; if (!isArrayRowset(to)) { if (!isArrayRowset(exprType)) func = dataset2DatasetXAtom; } else if (hasLinkCountedModifier(to)) { if (hasLinkCountedModifier(exprType)) { CHqlBoundExpr bound; buildDataset(ctx, expr, bound, FormatLinkedDataset); assertex(bound.count && bound.expr); if (hasWrapperModifier(to)) { //assigns to count and rows members StringBuffer s; generateExprCpp(s, target.expr); s.append(".set("); generateExprCpp(s, bound.count); s.append(","); generateExprCpp(s, bound.expr); s.append(");"); ctx.addQuoted(s); } else { ctx.addAssign(target.count, bound.count); HqlExprArray args; args.append(*LINK(bound.expr)); OwnedHqlExpr call = bindTranslatedFunctionCall(linkRowsetAtom, args); ctx.addAssign(target.expr, call); } return; } } if (func) { HqlExprArray args; args.append(*LINK(expr)); OwnedHqlExpr call = bindFunctionCall(func, args); buildExprAssign(ctx, target, call); return; } } } } IHqlExpression * record = ::queryRecord(to); Owned builder; if (targetOutOfLine) { if (target.queryType()->getTypeCode() == type_dictionary) { builder.setown(createLinkedDictionaryBuilder(record)); } else { IHqlExpression * choosenLimit = NULL; if ((op == no_choosen) && !isChooseNAllLimit(expr->queryChild(1)) && !queryRealChild(expr, 2)) { choosenLimit = expr->queryChild(1); expr = expr->queryChild(0); } builder.setown(createLinkedDatasetBuilder(record, choosenLimit)); } } else builder.setown(createBlockedDatasetBuilder(record)); builder->buildDeclare(ctx); buildDatasetAssign(ctx, builder, expr); builder->buildFinish(ctx, target); } //--------------------------------------------------------------------------- void HqlCppTranslator::doBuildCheckDatasetLimit(BuildCtx & ctx, IHqlExpression * expr, const CHqlBoundExpr & bound) { IHqlExpression * record = expr->queryRecord(); IHqlExpression * limit = expr->queryChild(1); OwnedHqlExpr test; if (!bound.count && bound.length && isFixedRecordSize(record)) { OwnedHqlExpr size = bound.length->queryValue() ? LINK(bound.length) : createTranslated(bound.length); OwnedHqlExpr maxSize = createValue(no_mul, LINK(sizetType), ensureExprType(limit, sizetType), getSizetConstant(getFixedRecordSize(record))); test.setown(createBoolExpr(no_gt, ensureExprType(size, sizetType), LINK(maxSize))); } else { OwnedHqlExpr count = getBoundCount(bound); OwnedHqlExpr translatedCount = count->queryValue() ? LINK(count) : createTranslated(count); test.setown(createBoolExpr(no_gt, ensureExprType(translatedCount, sizetType), ensureExprType(limit, sizetType))); } OwnedHqlExpr folded = foldHqlExpression(test); LinkedHqlExpr fail = queryRealChild(expr, 2); if (folded->queryValue()) { if (!folded->queryValue()->getBoolValue()) return; StringBuffer failMessageText; if (fail) { OwnedHqlExpr failMessage = getFailMessage(fail, true); if (failMessage && failMessage->queryValue()) failMessage->queryValue()->getStringValue(failMessageText); } if (failMessageText.length()) WARNING1(HQLWRN_LimitAlwaysExceededX, failMessageText.str()); else WARNING(HQLWRN_LimitAlwaysExceeded); } if (!fail) fail.setown(createFailAction("Limit exceeded", limit, NULL, queryCurrentActivityId(ctx))); BuildCtx subctx(ctx); buildFilter(subctx, folded); buildStmt(subctx, fail); } void HqlCppTranslator::doBuildDatasetLimit(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ExpressionFormat format) { buildDataset(ctx, expr->queryChild(0), tgt, format); doBuildCheckDatasetLimit(ctx, expr, tgt); } bool HqlCppTranslator::doBuildDatasetInlineTable(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ExpressionFormat format) { if (!options.generateStaticInlineTables) return false; IHqlExpression * transforms = expr->queryChild(0); IHqlExpression * record = expr->queryRecord(); if (transforms->numChildren() == 0) { OwnedHqlExpr null = createDataset(no_null, LINK(record)); buildDataset(ctx, null, tgt, format); return true; } BuildCtx declareCtx(*code, literalAtom); //Remove unique id when checking for constant datasets already generated OwnedHqlExpr exprNoUnique = removeProperty(expr, _uid_Atom); if (declareCtx.getMatchExpr(exprNoUnique, tgt)) return true; HqlExprArray rows; unsigned maxRows = transforms->numChildren(); unsigned row; for (row = 0; row < maxRows; row++) { OwnedHqlExpr constRow = createConstantRowExpr(transforms->queryChild(row)); if (!constRow || !canGenerateStringInline(constRow->queryType()->getSize())) return false; rows.append(*constRow.getClear()); } HqlExprArray boundRows; ForEachItemIn(i, rows) { CHqlBoundExpr bound; buildConstRow(record, &rows.item(i), bound); boundRows.append(*bound.expr.getClear()); } Owned rowType = makeConstantModifier(makeReferenceModifier(makeRowType(LINK(queryRecordType(expr->queryType()))))); OwnedHqlExpr values = createValue(no_list, makeSetType(LINK(rowType)), boundRows); Owned tableType = makeConstantModifier(makeArrayType(LINK(rowType), maxRows)); OwnedITypeInfo rowsType = makeOutOfLineModifier(makeTableType(LINK(rowType), NULL, NULL, NULL)); OwnedHqlExpr table = declareCtx.getTempDeclare(tableType, values); if (options.spanMultipleCpp) { BuildCtx protoctx(*code, mainprototypesAtom); protoctx.addDeclareExternal(table); } tgt.count.setown(getSizetConstant(maxRows)); tgt.expr.setown(createValue(no_typetransfer, LINK(rowsType), LINK(table))); declareCtx.associateExpr(exprNoUnique, tgt); return true; } //--------------------------------------------------------------------------- // Dataset creation via builder void HqlCppTranslator::buildDatasetAssignTempTable(BuildCtx & ctx, IHqlCppDatasetBuilder * target, IHqlExpression * expr) { OwnedHqlExpr values = normalizeListCasts(expr->queryChild(0)); if (values->getOperator() == no_null) return; IHqlExpression * record = expr->queryChild(1); OwnedHqlExpr rowsExpr; if (values->queryType()->getTypeCode() == type_set) { if ((values->getOperator() == no_list) && !values->isConstant()) { ForEachChild(i, values) { BuildCtx loopctx(ctx); BoundRow * targetRow = target->buildCreateRow(loopctx); OwnedHqlExpr targetField = createSelectExpr(LINK(targetRow->querySelector()), LINK(record->queryChild(0))); buildAssign(loopctx, targetField, values->queryChild(i)); target->finishRow(loopctx, targetRow); } } else { Owned cursor = createSetSelector(ctx, values); BuildCtx loopctx(ctx); CHqlBoundExpr boundCurElement; cursor->buildIterateLoop(loopctx, boundCurElement, false); BoundRow * targetRow = target->buildCreateRow(loopctx); OwnedHqlExpr targetField = createSelectExpr(LINK(targetRow->querySelector()), LINK(record->queryChild(0))); OwnedHqlExpr value = boundCurElement.getTranslatedExpr(); buildAssign(loopctx, targetField, value); target->finishRow(loopctx, targetRow); } } else { BuildCtx subctx(ctx); BoundRow * targetRow = target->buildCreateRow(subctx); Owned targetRef = buildActiveRow(subctx, targetRow->querySelector()); buildRowAssign(subctx, targetRef, values); target->finishRow(subctx, targetRow); } } void HqlCppTranslator::buildDatasetAssignInlineTable(BuildCtx & ctx, IHqlCppDatasetBuilder * target, IHqlExpression * expr) { IHqlExpression * transforms = expr->queryChild(0); if (transforms->numChildren() == 0) return; unsigned maxRows = transforms->numChildren(); unsigned row; const bool copyConstantRows = true;//getFieldCount(expr->queryRecord()) > 2; for (row = 0; row < maxRows; row++) { IHqlExpression * transform = transforms->queryChild(row); OwnedHqlExpr rowValue = createRow(no_createrow, LINK(transform)); BuildCtx subctx(ctx); CHqlBoundExpr bound; //Work in progress. Check if there are several fields - otherwise not worth it.s if (doBuildRowConstantTransform(transform, bound)) { BoundRow * row = bindConstantRow(subctx, rowValue, bound); if (target->buildLinkRow(subctx, row)) continue; } BoundRow * targetRow = target->buildCreateRow(subctx); Owned targetRef = buildActiveRow(subctx, targetRow->querySelector()); buildRowAssign(subctx, targetRef, rowValue); target->finishRow(subctx, targetRow); } } class InlineDatasetSkipCallback : public CInterface, implements IHqlCodeCallback { public: IMPLEMENT_IINTERFACE virtual void buildCode(HqlCppTranslator & translator, BuildCtx & ctx) { ctx.addContinue(); } }; void HqlCppTranslator::buildDatasetAssignProject(BuildCtx & ctx, IHqlCppDatasetBuilder * target, IHqlExpression * expr) { BuildCtx iterctx(ctx); IHqlExpression * ds = expr->queryChild(0); IHqlExpression * counter = queryPropertyChild(expr, _countProject_Atom, 0); OwnedHqlExpr counterVar; if (counter) { counterVar.setown(iterctx.getTempDeclare(unsignedType, queryZero())); } bool containsSkip = transformContainsSkip(expr->queryChild(1)); BoundRow * sourceCursor = buildDatasetIterate(iterctx, ds, containsSkip); if (counter) { iterctx.associateExpr(counter, counterVar); OwnedHqlExpr inc = createValue(no_postinc, LINK(unsignedType), LINK(counterVar)); iterctx.addExpr(inc); } if (sourceCursor) { BoundRow * targetRow = target->buildCreateRow(iterctx); HqlExprAssociation * skipAssociation = NULL; if (containsSkip) { OwnedHqlExpr callback = createUnknown(no_unknown, makeVoidType(), NULL, new InlineDatasetSkipCallback); skipAssociation = ctx.associateExpr(skipActionMarker, callback); } Owned targetRef = buildActiveRow(iterctx, targetRow->querySelector()); switch (expr->getOperator()) { case no_hqlproject: doBuildRowAssignProject(iterctx, targetRef, expr); break; case no_newusertable: doBuildRowAssignUserTable(iterctx, targetRef, expr); break; } ctx.removeAssociation(skipAssociation); target->finishRow(iterctx, targetRow); } } void HqlCppTranslator::buildDatasetAssignJoin(BuildCtx & ctx, IHqlCppDatasetBuilder * target, IHqlExpression * expr) { IHqlExpression * left = expr->queryChild(0); IHqlExpression * right = expr->queryChild(1); IHqlExpression * cond = expr->queryChild(2); IHqlExpression * selSeq = querySelSeq(expr); bool leftOuter = expr->hasProperty(leftonlyAtom) || expr->hasProperty(leftouterAtom); CHqlBoundExpr nullRhs; if (leftOuter) buildDefaultRow(ctx, right, nullRhs); BuildCtx leftIterCtx(ctx); BoundRow * leftCursor = buildDatasetIterate(leftIterCtx, left, false); bindTableCursor(leftIterCtx, left, leftCursor->queryBound(), no_left, selSeq); OwnedHqlExpr matchedAnyVar; if (leftOuter) matchedAnyVar.setown(leftIterCtx.getTempDeclare(queryBoolType(), queryBoolExpr(false))); BuildCtx rightIterCtx(leftIterCtx); BoundRow * rightCursor = buildDatasetIterate(rightIterCtx, right, false); bindTableCursor(rightIterCtx, right, rightCursor->queryBound(), no_right, selSeq); OwnedHqlExpr cseCond = options.spotCSE ? spotScalarCSE(cond) : LINK(cond); buildFilter(rightIterCtx, cseCond); if (!expr->hasProperty(leftonlyAtom)) { BoundRow * targetRow = target->buildCreateRow(rightIterCtx); Owned targetRef = buildActiveRow(rightIterCtx, targetRow->querySelector()); OwnedHqlExpr rowValue = createRow(no_createrow, LINK(expr->queryChild(3))); buildRowAssign(rightIterCtx, targetRef, rowValue); target->finishRow(rightIterCtx, targetRow); } if (matchedAnyVar) { buildAssignToTemp(rightIterCtx, matchedAnyVar, queryBoolExpr(true)); OwnedHqlExpr test = getInverse(matchedAnyVar); leftIterCtx.addFilter(test); OwnedHqlExpr defaultRowPtr = getPointer(nullRhs.expr); bindTableCursor(leftIterCtx, right, defaultRowPtr, no_right, selSeq); BoundRow * targetRow = target->buildCreateRow(leftIterCtx); Owned targetRef = buildActiveRow(leftIterCtx, targetRow->querySelector()); OwnedHqlExpr rowValue = createRow(no_createrow, LINK(expr->queryChild(3))); buildRowAssign(leftIterCtx, targetRef, rowValue); target->finishRow(leftIterCtx, targetRow); } } void HqlCppTranslator::buildDatasetAssignAggregate(BuildCtx & ctx, IHqlCppDatasetBuilder * target, IHqlExpression * expr) { BuildCtx subctx(ctx); subctx.addGroup(); BoundRow * targetRow = target->buildCreateRow(subctx); Owned targetRef = buildActiveRow(subctx, targetRow->querySelector()); doBuildRowAssignAggregate(subctx, targetRef, expr); target->finishRow(subctx, targetRow); } void HqlCppTranslator::buildDatasetAssign(BuildCtx & ctx, IHqlCppDatasetBuilder * target, IHqlExpression * _expr) { OwnedHqlExpr expr = forceInlineAssignDataset(ctx, _expr); bool isRowAssign = false; BuildCtx subctx(ctx); switch (expr->getOperator()) { case no_addfiles: buildDatasetAssign(subctx, target, expr->queryChild(0)); buildDatasetAssign(subctx, target, expr->queryChild(1)); return; case no_temptable: buildDatasetAssignTempTable(subctx, target, expr); //MORE: Create rows and assign each one in turn. Could possibly be done with a different dataset selector return; case no_inlinedictionary: case no_inlinetable: buildDatasetAssignInlineTable(subctx, target, expr); return; case no_xmlproject: buildDatasetAssignXmlProject(subctx, target, expr); return; case no_datasetfromrow: { isRowAssign = true; expr.set(expr->queryChild(0)); break; } case no_if: { CHqlBoundExpr bound; buildExpr(subctx, expr->queryChild(0), bound); IHqlStmt * filter = subctx.addFilter(bound.expr); buildDatasetAssign(subctx, target, expr->queryChild(1)); IHqlExpression * elseExpr = expr->queryChild(2); if (elseExpr && elseExpr->getOperator() != no_null) { subctx.selectElse(filter); buildDatasetAssign(subctx, target, elseExpr); } } return; case no_null: return; case no_activetable: case no_temprow: case no_createrow: case no_projectrow: case no_typetransfer: isRowAssign = true; break; case no_newaggregate: buildDatasetAssignAggregate(subctx, target, expr); return; case no_hqlproject: case no_newusertable: buildDatasetAssignProject(subctx, target, expr); return; case no_compound_childread: case no_compound_childnormalize: case no_compound_childaggregate: case no_compound_selectnew: case no_compound_inline: case no_distributed: case no_preservemeta: case no_sorted: case no_nofold: case no_nohoist: case no_section: case no_sectioninput: buildDatasetAssign(subctx, target, expr->queryChild(0)); return; case no_alias_scope: // expandAliasScope(subctx, expr); buildDatasetAssign(subctx, target, expr->queryChild(0)); return; case no_filter: { //We want to evaluate invariant conditions outside of the loop, rather than inside the dataset assignment //Currently the test is whether the expression is independent of any tables. Better would be to //see if the test was dependent on any of the datasets introduced by expr->queryChild(0). HqlExprArray conds; unwindFilterConditions(conds, expr); IHqlExpression * ds = expr->queryChild(0); #if 0 HqlExprCopyArray selectors; loop { selectors.append(*ds->queryNormalizedSelector()); IHqlExpression * root = queryRoot(expr); if (!root || root->getOperator() != no_select) break; bool isNew; ds = querySelectorDataset(root, isNew); if (!isNew) break; } #endif unsigned max = conds.ordinality(); unsigned i = 0; bool optimized = false; while (i < max) { IHqlExpression & cur = conds.item(i); #if 0 bool overlap = false; ForEachItemIn(j, selectors) { if (containsSelector(&cur, &selectors.item(j))) { overlap = true; break; } } #else bool overlap = containsSelector(&cur, ds->queryNormalizedSelector()); #endif if (!overlap) { buildFilter(subctx, &cur); conds.remove(i); optimized = true; max--; } else i++; } if (max == 0) { buildDatasetAssign(subctx, target, ds); return; } if (optimized) { conds.add(*LINK(ds), 0); expr.setown(expr->clone(conds)); } break; } case no_join: buildDatasetAssignJoin(subctx, target, expr); return; } ITypeInfo * type = expr->queryType(); if (type) { switch (type->getTypeCode()) { case type_record: // e.g. dataset.recordfield case type_row: isRowAssign = true; break; } } if (isRowAssign) { bool done = false; subctx.addGroup(); //Some code primarily here to improve the generated code for productions inside parse statements for text parsing. //see if we can replace a memcpy of the child record with a link... if (!target->isRestricted()) { switch (expr->getOperator()) { case no_matchattr: case no_left: case no_right: { //Only try for really simple cases... Owned sourceRef = buildNewRow(subctx, expr); Owned sourceRow = sourceRef->getRow(subctx); if (!target->buildLinkRow(subctx, sourceRow)) { BoundRow * targetRow = target->buildCreateRow(subctx); Owned targetRef = buildActiveRow(subctx, targetRow->querySelector()); buildRowAssign(subctx, targetRef, sourceRef); target->finishRow(subctx, targetRow); } done = true; break; } } } if (!done) { BoundRow * match = static_cast(ctx.queryAssociation(expr, AssocRow, NULL)); if (match && target->buildLinkRow(subctx, match)) done = true; } if (!done) { BoundRow * targetRow = target->buildCreateRow(subctx); Owned targetRef = buildActiveRow(subctx, targetRow->querySelector()); buildRowAssign(subctx, targetRef, expr); target->finishRow(subctx, targetRow); } } else { if (!target->buildAppendRows(subctx, expr)) { BoundRow * sourceRow = buildDatasetIterate(subctx, expr, false); if (sourceRow) { if (!target->buildLinkRow(subctx, sourceRow)) { BoundRow * targetRow = target->buildCreateRow(subctx); Owned targetRef = buildActiveRow(subctx, targetRow->querySelector()); Owned sourceRef = buildActiveRow(subctx, sourceRow->querySelector()); buildRowAssign(subctx, targetRef, sourceRef); target->finishRow(subctx, targetRow); } } } } } //--------------------------------------------------------------------------- // Dataset iteration BoundRow * HqlCppTranslator::buildDatasetIterateSelectN(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { OwnedHqlExpr counter = ctx.getTempDeclare(sizetType, NULL); buildAssignToTemp(ctx, counter, expr->queryChild(1)); BoundRow * cursor = buildDatasetIterate(ctx, expr->queryChild(0), needToBreak); if (cursor) { OwnedHqlExpr dec = createValue(no_predec, counter->getType(), LINK(counter)); OwnedHqlExpr test = createBoolExpr(no_eq, LINK(dec), getSizetConstant(0)); ctx.addFilter(test); } return cursor; } BoundRow * HqlCppTranslator::buildDatasetIterateChoosen(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { OwnedHqlExpr counter = ctx.getTempDeclare(sizetType, queryZero()); CHqlBoundExpr boundLow, boundHigh; OwnedHqlExpr foldedHigh = foldHqlExpression(expr->queryChild(1)); if (!isChooseNAllLimit(foldedHigh)) buildSimpleExpr(ctx, foldedHigh, boundHigh); if (queryRealChild(expr, 2)) { OwnedHqlExpr foldedLow = foldHqlExpression(expr->queryChild(2)); OwnedHqlExpr low = adjustValue(foldedLow, -1); buildSimpleExpr(ctx, low, boundLow); if (boundHigh.expr) boundHigh.expr.setown(createValue(no_add, LINK(boundHigh.queryType()), LINK(boundHigh.expr), LINK(boundLow.expr))); } BoundRow * cursor = buildDatasetIterate(ctx, expr->queryChild(0), needToBreak); if (cursor) { OwnedHqlExpr inc = createValue(no_postinc, counter->getType(), LINK(counter)); ctx.addExpr(inc); OwnedHqlExpr cond; if (boundLow.expr) extendConditionOwn(cond, no_and, createBoolExpr(no_gt, LINK(counter), LINK(boundLow.expr))); if (boundHigh.expr) extendConditionOwn(cond, no_and, createBoolExpr(no_le, LINK(counter), LINK(boundHigh.expr))); if (cond) ctx.addFilter(cond); } return cursor; } BoundRow * HqlCppTranslator::buildDatasetIterateLimit(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { OwnedHqlExpr counter = ctx.getTempDeclare(sizetType, queryZero()); CHqlBoundExpr boundHigh; OwnedHqlExpr foldedHigh = foldHqlExpression(expr->queryChild(1)); buildSimpleExpr(ctx, foldedHigh, boundHigh); BoundRow * cursor = buildDatasetIterate(ctx, expr->queryChild(0), needToBreak); if (cursor) { OwnedHqlExpr inc = createValue(no_preinc, counter->getType(), LINK(counter)); OwnedHqlExpr cond = createBoolExpr(no_gt, LINK(inc), LINK(boundHigh.expr)); BuildCtx subctx(ctx); subctx.addFilter(cond); LinkedHqlExpr fail = expr->queryChild(2); if (!fail || fail->isAttribute()) fail.setown(createFailAction("Limit exceeded", foldedHigh, NULL, queryCurrentActivityId(ctx))); buildStmt(subctx, fail); } return cursor; } BoundRow * HqlCppTranslator::buildDatasetIterateProject(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { IHqlExpression * dataset = expr->queryChild(0); OwnedHqlExpr counterVar; IHqlExpression * counter = queryPropertyChild(expr, _countProject_Atom, 0); if (counter) { counterVar.setown(ctx.getTempDeclare(unsignedType, queryZero())); } bool containsSkip = transformContainsSkip(expr->queryChild(1)); if (containsSkip) needToBreak = true; buildDatasetIterate(ctx, dataset, needToBreak); Owned tempRow = declareTempAnonRow(ctx, ctx, expr); if (counter) { ctx.associateExpr(counter, counterVar); OwnedHqlExpr inc = createValue(no_postinc, LINK(unsignedType), LINK(counterVar)); ctx.addExpr(inc); } Owned rowBuilder = createRowBuilder(ctx, tempRow); OwnedHqlExpr leftSelect = createSelector(no_left, dataset, querySelSeq(expr)); OwnedHqlExpr transform = replaceSelector(expr->queryChild(1), leftSelect, dataset->queryNormalizedSelector()); HqlExprAssociation * skipAssociation = NULL; if (containsSkip) { OwnedHqlExpr callback = createUnknown(no_unknown, makeVoidType(), NULL, new InlineDatasetSkipCallback); skipAssociation = ctx.associateExpr(skipActionMarker, callback); } doTransform(ctx, transform, rowBuilder); ctx.removeAssociation(skipAssociation); //remove it in case keeping hold of it causes issues. finalizeTempRow(ctx, tempRow, rowBuilder); return bindTableCursor(ctx, expr, tempRow->queryBound()); } BoundRow * HqlCppTranslator::buildDatasetIterateUserTable(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { IHqlExpression * dataset = expr->queryChild(0); buildDatasetIterate(ctx, dataset, needToBreak); Owned tempRow = declareTempAnonRow(ctx, ctx, expr); Owned rowBuilder = createRowBuilder(ctx, tempRow); doTransform(ctx, expr->queryChild(2), rowBuilder); finalizeTempRow(ctx, tempRow, rowBuilder); return bindTableCursor(ctx, expr, tempRow->queryBound()); } BoundRow * HqlCppTranslator::buildDatasetIterateSpecialTempTable(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { IHqlExpression * values = expr->queryChild(0); bool requiresTempRow = false; ITypeInfo * type = values->queryType()->queryChildType(); switch (type->getTypeCode()) { case type_alien: case type_bitfield: requiresTempRow = true; break; default: if (type->getSize() == UNKNOWN_LENGTH) requiresTempRow = true; break; } Owned cursor = createSetSelector(ctx, values); CHqlBoundExpr boundCurElement; cursor->buildIterateLoop(ctx, boundCurElement, false); OwnedHqlExpr address = getPointer(boundCurElement.expr); if (requiresTempRow) { Owned tempRow = declareTempAnonRow(ctx, ctx, expr); Owned rowBuilder = createRowBuilder(ctx, tempRow); IHqlExpression * record = expr->queryRecord(); OwnedHqlExpr target = createSelectExpr(LINK(rowBuilder->querySelector()), LINK(record->queryChild(0))); OwnedHqlExpr curValue = boundCurElement.getTranslatedExpr(); buildAssign(ctx, target, curValue); finalizeTempRow(ctx, tempRow, rowBuilder); return bindTableCursor(ctx, expr, tempRow->queryBound()); } else { assertex(!boundCurElement.length && !boundCurElement.count); address.setown(createValue(no_implicitcast, makeRowReferenceType(expr), LINK(address))); return bindTableCursor(ctx, expr, address); } } BoundRow * HqlCppTranslator::buildDatasetIterateStreamedCall(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { CHqlBoundExpr bound; doBuildExprCall(ctx, expr, bound); ITypeInfo * exprType = expr->queryType(); Owned wrappedType = makeWrapperModifier(LINK(exprType)); OwnedHqlExpr temp = ctx.getTempDeclare(wrappedType, bound.expr); ctx.addLoop(NULL, NULL, false); Owned wrappedRowType = makeWrapperModifier(LINK(queryRowType(exprType))); OwnedHqlExpr tempRow = ctx.getTempDeclare(wrappedRowType, NULL); StringBuffer s; generateExprCpp(s, tempRow).append(".setown("); generateExprCpp(s, temp).append("->nextRow());"); ctx.addQuoted(s); s.clear().append("if (!");generateExprCpp(s, tempRow).append(".getbytes()) break;"); ctx.addQuoted(s); return bindTableCursor(ctx, expr, tempRow); } BoundRow * HqlCppTranslator::buildDatasetIterate(BuildCtx & ctx, IHqlExpression * expr, bool needToBreak) { if (!canProcessInline(&ctx, expr)) { Owned cursor = createDatasetSelector(ctx, expr); return cursor->buildIterateLoop(ctx, needToBreak); } switch (expr->getOperator()) { case no_dataset_alias: if (!expr->hasProperty(_normalized_Atom)) { OwnedHqlExpr uniqueChild = normalizeDatasetAlias(expr); BoundRow * childCursor = buildDatasetIterate(ctx, uniqueChild, needToBreak); return rebindTableCursor(ctx, expr, childCursor, no_none, NULL); } else { throwUnexpected(); //The following would only be triggered for a splitter (not yet generated), and that would require //disambiguation when that was built. BoundRow * childCursor = buildDatasetIterate(ctx, expr->queryChild(0), needToBreak); return rebindTableCursor(ctx, expr, childCursor, no_none, NULL); } case no_null: buildFilter(ctx, queryBoolExpr(false)); return NULL; case no_filter: { IHqlExpression * dataset = expr->queryChild(0); #ifdef _OPTIMZE_INLINE_FILTERS_ //Good code, but messes up accidental cse in some createSegmentMonitor calls. HqlExprAttr invariant; OwnedHqlExpr cond = extractFilterConditions(invariant, expr, dataset); if (invariant) buildFilter(ctx, invariant); //MORE: if (canAssignInline(ctx, ds) && !canIterateInline(ctx, ds)) break; BoundRow * cursor = buildDatasetIterate(ctx, dataset, needToBreak); if (cond) buildFilter(ctx, cond); #else //MORE: if (canAssignInline(ctx, ds) && !canIterateInline(ctx, ds)) break; BoundRow * cursor = buildDatasetIterate(ctx, dataset, needToBreak); unsigned max = expr->numChildren(); for (unsigned i=1; i < max; i++) buildFilter(ctx, expr->queryChild(i)); #endif return cursor; } case no_id2blob: case no_select: case no_translated: { Owned cursor = createDatasetSelector(ctx, expr); return cursor->buildIterateLoop(ctx, needToBreak); } case no_choosen: return buildDatasetIterateChoosen(ctx, expr, needToBreak); case no_limit: return buildDatasetIterateLimit(ctx, expr, needToBreak); case no_index: case no_selectnth: return buildDatasetIterateSelectN(ctx, expr, needToBreak); case no_hqlproject: return buildDatasetIterateProject(ctx, expr, needToBreak); case no_newusertable: return buildDatasetIterateUserTable(ctx, expr, needToBreak); case no_compound_childread: case no_compound_childnormalize: case no_compound_childaggregate: case no_compound_selectnew: case no_compound_inline: case no_distributed: case no_preservemeta: case no_sorted: case no_nofold: case no_nohoist: return buildDatasetIterate(ctx, expr->queryChild(0), needToBreak); case no_sectioninput: case no_section: { BoundRow * row = buildDatasetIterate(ctx, expr->queryChild(0), needToBreak); #ifdef _DEBUG StringBuffer s; if (expr->getOperator() == no_section) s.append("//---- section "); else s.append("//---- end section "); getStringValue(s, expr->queryChild(1), "").append(" "); getStringValue(s, expr->queryChild(2)).append("----"); ctx.addQuoted(s); #endif return row; } case no_alias_scope: // expandAliasScope(ctx, expr); return buildDatasetIterate(ctx, expr->queryChild(0), needToBreak); case no_temptable: { IHqlExpression * values = expr->queryChild(0); if (values->queryType()->getTypeCode() == type_set) { if (values->getOperator() == no_alias) values = values->queryChild(0); bool special = false; switch (values->getOperator()) { case no_getresult: case no_null: special = true; break; } if (special) return buildDatasetIterateSpecialTempTable(ctx, expr, needToBreak); } break; } case no_call: case no_externalcall: if (hasStreamedModifier(expr->queryType())) return buildDatasetIterateStreamedCall(ctx, expr, needToBreak); break; #if 0 //Following should improve the code, but I'm not sure how to correctly convert a referenceSelector to a boundExpr (since it may be with an existing row) case no_datasetfromrow: if (!needToBreak) { BoundRow * row = buildNewRow(ctx, expr->queryChild(0)); bindTableCursor(ctx, expr, tempRow->queryBound(), no_none, NULL); return row; } break; #endif } Owned cursor = createDatasetSelector(ctx, expr); return cursor->buildIterateLoop(ctx, needToBreak); } //--------------------------------------------------------------------------- // Row Assignment void HqlCppTranslator::buildCompoundAssign(BuildCtx & ctx, IHqlExpression * left, IReferenceSelector * leftSelector, IHqlExpression * rightScope, IHqlExpression * rightSelector) { switch (left->getOperator()) { case no_ifblock: { BuildCtx subctx(ctx); OwnedHqlExpr test = replaceSelector(left->queryChild(0), querySelfReference(), leftSelector->queryExpr()); buildFilter(subctx, test); buildCompoundAssign(subctx, left->queryChild(1), leftSelector, rightScope, rightSelector); //This calculates the size of the previous block. It means that subsequent uses of the //offsets are cached - even if they are inside another ifblock(). CHqlBoundExpr bound; IHqlExpression * mapexpr = createSelectExpr(LINK(leftSelector->queryExpr()), LINK(left)); OwnedHqlExpr size = createValue(no_sizeof, makeIntType(4,false), mapexpr); buildCachedExpr(ctx, size, bound); } break; case no_field: { Owned selectedLeft = leftSelector->select(ctx, left); OwnedHqlExpr selectedRight; IHqlExpression * leftRecord = ::queryRecord(leftSelector->queryType()); if (!rightScope || (leftRecord==rightScope)) { selectedRight.setown(createSelectExpr(LINK(rightSelector), LINK(left))); } else { IHqlSimpleScope * scope = rightScope->querySimpleScope(); IHqlExpression * resolved = scope->lookupSymbol(left->queryName()); assertex(resolved); selectedRight.setown(createSelectExpr(LINK(rightSelector), resolved)); } if (left->queryType()->getTypeCode() == type_row) buildCompoundAssign(ctx, left->queryRecord(), selectedLeft, selectedRight->queryRecord(), selectedRight); else selectedLeft->set(ctx, selectedRight); break; } break; case no_record: { ForEachChild(i, left) buildCompoundAssign(ctx, left->queryChild(i), leftSelector, rightScope, rightSelector); break; } case no_attr: case no_attr_expr: case no_attr_link: break; default: UNIMPLEMENTED; } } void HqlCppTranslator::buildCompoundAssign(BuildCtx & ctx, IHqlExpression * left, IHqlExpression * right) { //MORE: May need to resolve lhs and rhs in scope to find out the record type. // buildCompoundAssign(ctx, column->queryRecord(), selector->queryExpr(), value->queryRecord(), value); UNIMPLEMENTED; } void HqlCppTranslator::doBuildRowAssignAggregateClear(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { IHqlExpression * transform = expr->queryChild(2); unsigned numAggregates = transform->numChildren(); unsigned idx; OwnedHqlExpr self = getSelf(expr); for (idx = 0; idx < numAggregates; idx++) { IHqlExpression * cur = transform->queryChild(idx); Owned curTarget = createSelfSelect(ctx, target, cur->queryChild(0), self); IHqlExpression * src = cur->queryChild(1); switch (src->getOperator()) { case no_countgroup: case no_maxgroup: case no_mingroup: case no_sumgroup: case no_existsgroup: curTarget->buildClear(ctx, 0); break; default: if (src->isConstant()) curTarget->set(ctx, src); else curTarget->buildClear(ctx, 0); break; } } } void HqlCppTranslator::doBuildRowAssignAggregateNext(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr, bool isSingleExists, IHqlExpression * alreadyDoneExpr) { IHqlExpression * transform = expr->queryChild(2); unsigned numAggregates = transform->numChildren(); OwnedHqlExpr self = getSelf(expr); OwnedHqlExpr notAlreadyDone = alreadyDoneExpr ? getInverse(alreadyDoneExpr) : NULL; bool isVariableOffset = false; for (unsigned idx = 0; idx < numAggregates; idx++) { IHqlExpression * cur = transform->queryChild(idx); if (cur->isAttribute()) continue; IHqlExpression * targetSelect = cur->queryChild(0); Owned curTarget = createSelfSelect(ctx, target, targetSelect, self); IHqlExpression * src = cur->queryChild(1); IHqlExpression * arg = src->queryChild(0); IHqlExpression * cond = src->queryChild(1); BuildCtx condctx(ctx); node_operator srcOp = src->getOperator(); switch (srcOp) { case no_countgroup: { assertex(!(arg && isVariableOffset)); if (arg) buildFilter(condctx, arg); OwnedHqlExpr one = getSizetConstant(1); if (isVariableOffset) { IHqlStmt * ifStmt = condctx.addFilter(notAlreadyDone); curTarget->set(condctx, one); condctx.selectElse(ifStmt); } buildIncrementAssign(condctx, curTarget, one); } break; case no_sumgroup: { assertex(!(cond && isVariableOffset)); if (cond) buildFilter(condctx, cond); if (isVariableOffset) { IHqlStmt * ifStmt = condctx.addFilter(notAlreadyDone); curTarget->set(condctx, arg); condctx.selectElse(ifStmt); } buildIncrementAssign(condctx, curTarget, arg); } break; case no_maxgroup: case no_mingroup: { node_operator op = (srcOp == no_maxgroup) ? no_gt : no_lt; assertex(!cond); OwnedHqlExpr castArg = ensureExprType(arg, targetSelect->queryType()); // cast to correct type, assume it can fit in the target type. OwnedHqlExpr temp = buildSimplifyExpr(condctx, castArg); OwnedHqlExpr compare = createBoolExpr (op, LINK(temp), LINK(curTarget->queryExpr())); if (notAlreadyDone) compare.setown(createBoolExpr(no_or, LINK(notAlreadyDone), compare.getClear())); buildFilter(condctx, compare); curTarget->set(condctx, temp); } break; case no_existsgroup: assertex(!(arg && isVariableOffset)); if (arg) buildFilter(condctx, arg); curTarget->set(condctx, queryBoolExpr(true)); if (isSingleExists) condctx.addBreak(); break; default: if (!src->isConstant() || isVariableOffset) { condctx.addFilter(notAlreadyDone); curTarget->set(condctx, src); } break; } if (targetSelect->queryType()->getSize() == UNKNOWN_LENGTH) isVariableOffset = true; } if (alreadyDoneExpr) buildAssignToTemp(ctx, alreadyDoneExpr, queryBoolExpr(true)); } void HqlCppTranslator::doBuildRowAssignAggregate(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { IHqlExpression * dataset = expr->queryChild(0); IHqlExpression * transform = expr->queryChild(2); unsigned numAggregates = transform->numChildren(); if (isKeyedCountAggregate(expr)) throwError(HQLERR_KeyedCountNonKeyable); bool needGuard = false; bool isSingleExists = true; unsigned idx; for (idx = 0; idx < numAggregates; idx++) { IHqlExpression * cur = transform->queryChild(idx); if (cur->isAttribute()) continue; IHqlExpression * tgt = cur->queryChild(0); IHqlExpression * src = cur->queryChild(1); switch (src->getOperator()) { case no_countgroup: case no_sumgroup: isSingleExists = false; break; case no_existsgroup: break; case no_mingroup: isSingleExists = false; needGuard = true; break; case no_maxgroup: isSingleExists = false; if (!isNullValueMinimumValue(src->queryType())) needGuard = true; break; default: isSingleExists = false; if (!src->isConstant()) needGuard = true; break; } if ((tgt->queryType()->getSize() == UNKNOWN_LENGTH) && (idx+1 != numAggregates)) needGuard = true; } OwnedHqlExpr guard; if (needGuard) { Owned boolType = makeBoolType(); guard.setown(ctx.getTempDeclare(boolType, queryBoolExpr(false))); } doBuildRowAssignAggregateClear(ctx, target, expr); BuildCtx condctx(ctx); BoundRow * cursor = buildDatasetIterate(condctx, dataset, isSingleExists); doBuildRowAssignAggregateNext(condctx, target, expr, isSingleExists, guard); } void HqlCppTranslator::doBuildRowAssignProject(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { IHqlExpression * dataset = expr->queryChild(0); IHqlExpression * counter = queryPropertyChild(expr, _countProject_Atom, 0); if (counter && !ctx.queryMatchExpr(counter)) throwError(HQLERR_CounterNotFound); BuildCtx subctx(ctx); assertex(target->isRoot()); IHqlExpression * selSeq = querySelSeq(expr); OwnedHqlExpr leftSelect = createSelector(no_left, dataset, selSeq); OwnedHqlExpr activeDataset = ensureActiveRow(dataset->queryNormalizedSelector()); OwnedHqlExpr transform = queryNewReplaceSelector(expr->queryChild(1), leftSelect, activeDataset); Owned selfCursor; if (!transform) { subctx.addGroup(); selfCursor.set(bindSelectorAsSelf(subctx, target, expr)); //Mapping may potentially be ambiguous, so do things correctly (see hqlsource for details) BoundRow * prevCursor = resolveSelectorDataset(subctx, dataset->queryNormalizedSelector()); transform.set(expr->queryChild(1)); bindTableCursor(subctx, dataset, prevCursor->queryBound(), no_left, selSeq); } else { //Not introducing any new left rows => no problem assigning to target selector selfCursor.setown(target->getRow(subctx)); } doTransform(subctx, transform, selfCursor); } void HqlCppTranslator::doBuildRowAssignCreateRow(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { IHqlExpression * transform = expr->queryChild(0); if (transform->isConstant()) { #ifdef USE_CONSTANT_ROW_FOR_ASSIGN //Generally not worthwhile - unless maybe it has a large number of fields.... CHqlBoundExpr bound; if (doBuildRowConstantTransform(transform, bound)) { OwnedHqlExpr raw = bound.getTranslatedExpr(); buildRowAssign(ctx, target, raw); return; } #endif } Owned selfCursor = target->getRow(ctx); doTransform(ctx, transform, selfCursor); } void HqlCppTranslator::doBuildRowAssignNullRow(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { target->buildClear(ctx, 0); } void HqlCppTranslator::doBuildRowAssignProjectRow(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { IHqlExpression * srcRow = expr->queryChild(0); IHqlExpression * transform = expr->queryChild(1); Owned source = buildNewRow(ctx, srcRow); BuildCtx subctx(ctx); OwnedHqlExpr leftSelect = createSelector(no_left, srcRow, querySelSeq(expr)); OwnedHqlExpr newTransform = replaceSelector(transform, leftSelect, srcRow); Owned selfCursor = target->getRow(subctx); doTransform(subctx, newTransform, selfCursor); } void HqlCppTranslator::doBuildRowAssignSerializeRow(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { IHqlExpression * srcRow = expr->queryChild(0); Owned source = buildNewRow(ctx, srcRow); BuildCtx subctx(ctx); Owned leftCursor = source->getRow(subctx); BoundRow * selfCursor = bindSelectorAsSelf(subctx, target, expr); IHqlExpression * unserializedRecord = srcRow->queryRecord(); //If builder isn't provided then the target must be a fixed size record that doesn't require serialization //Therefore this should never be called. assertex(selfCursor->queryBuilder()); { HqlExprArray args; args.append(*createRowSerializer(ctx, unserializedRecord, serializerAtom)); args.append(*LINK(srcRow)); Owned type = makeTransformType(expr->queryRecord()->getType()); OwnedHqlExpr call = bindFunctionCall(rtlSerializeToBuilderAtom, args, type); doTransform(subctx, call, selfCursor); //MORE: This doesn't associated the returned size with the target if assigned to a child field. //very unusual code, so not too concerned. } subctx.removeAssociation(selfCursor); } void HqlCppTranslator::doBuildRowAssignUserTable(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { Owned selfCursor = target->getRow(ctx); doTransform(ctx, expr->queryChild(2), selfCursor); } void HqlCppTranslator::buildRowAssign(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * expr) { switch (expr->getOperator()) { case no_temprow: throwUnexpected(); case no_projectrow: doBuildRowAssignProjectRow(ctx, target, expr); return; case no_createrow: doBuildRowAssignCreateRow(ctx, target, expr); return; case no_null: doBuildRowAssignNullRow(ctx, target, expr); return; case no_serialize: if (isDummySerializeDeserialize(expr)) buildRowAssign(ctx, target, expr->queryChild(0)->queryChild(0)); else doBuildRowAssignSerializeRow(ctx, target, expr); return; case no_if: { OwnedHqlExpr foldedCond = foldHqlExpression(expr->queryChild(0)); if (foldedCond->queryValue()) { unsigned branch = (foldedCond->queryValue()->getBoolValue()) ? 1 : 2; buildRowAssign(ctx, target, expr->queryChild(branch)); return; } //Assigning a variable size record can mean that references to self need recalculating outside of the condition, //producing poor code. if (!isVariableSizeRecord(expr->queryRecord())) { BuildCtx condctx(ctx); IHqlStmt * cond = buildFilterViaExpr(condctx, foldedCond); buildRowAssign(condctx, target, expr->queryChild(1)); condctx.selectElse(cond); buildRowAssign(condctx, target, expr->queryChild(2)); return; } } break; /* case no_externalcall: //MORE: Should assign directly to the target, but may not be very easy.... if (target->isBinary() && queryRecord(source->queryType()) == expr->queryRecord()) { CHqlBoundExpr address; target->buildAddress(ctx, address); row.setown(createValue(no_typetransfer, makeReferenceModifier(LINK(selector->queryType())), LINK(address.expr))); doBuildCall(ctx, &target, expr, NULL); } break; */ case no_comma: case no_compound: buildStmt(ctx, expr->queryChild(0)); buildRowAssign(ctx, target, expr->queryChild(1)); return; } Owned src = buildNewRow(ctx, expr); buildRowAssign(ctx, target, src); } void HqlCppTranslator::buildRowAssign(BuildCtx & ctx, IReferenceSelector * target, IReferenceSelector * source) { BuildCtx subctx(ctx); IHqlExpression * sourceRecord = ::queryRecord(source->queryType()); IHqlExpression * targetRecord = ::queryRecord(target->queryType()); //if record structures are identical, then we must just be able to block copy the information across. bool useMemcpy = (sourceRecord == targetRecord) && source->isBinary() && !source->isConditional() && !recordRequiresSerialization(sourceRecord); if (useMemcpy) { if (source->queryRootRow()->isConditional()) { IHqlStmt * ifStmt = subctx.addFilter(source->queryRootRow()->queryBound()); target->setRow(subctx, source); subctx.selectElse(ifStmt); target->buildClear(subctx, 0); } else target->setRow(subctx, source); } else buildCompoundAssign(subctx, targetRecord, target, sourceRecord, source->queryExpr()); } //--------------------------------------------------------------------------- // Dataset selection IReferenceSelector * HqlCppTranslator::doBuildRowSelectTop(BuildCtx & ctx, IHqlExpression * expr) { //create a temporary Owned rowType = makeReferenceModifier(expr->getType()); OwnedHqlExpr rowExpr = ctx.getTempDeclare(rowType, NULL); Owned row = createBoundRow(expr, rowExpr); ctx.associate(*row); // associate here because it is compared inside the loop CHqlBoundExpr boundCleared; #ifdef CREATE_DEAULT_ROW_IF_NULL buildDefaultRow(ctx, expr, boundCleared); #else buildNullRow(ctx, expr, boundCleared); #endif OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); //Declare row for final level, iterate the appropriate number of times, and then assign and break. BuildCtx initctx(ctx); initctx.addGroup(); // add a group to allow a temporary to be declared later. initctx.addAssign(rowExpr, defaultRowPtr); HqlExprAssociation * savedMarker = ctx.associateExpr(queryConditionalRowMarker(), rowExpr); BuildCtx iterctx(ctx); IHqlExpression * sort = expr->queryChild(0); IHqlExpression * dataset = sort->queryChild(0); IHqlExpression * sortList = sort->queryChild(1); BoundRow * chooseCursor = buildDatasetIterate(iterctx, dataset, false); //if (!best) { best=row; } else { if (next < best) best = row; } Must short-circuit the test of best OwnedHqlExpr testBest = createBoolExpr(no_eq, LINK(rowExpr), LINK(defaultRowPtr)); IHqlStmt * ifStmt = iterctx.addFilter(testBest); { OwnedHqlExpr source = ensureIteratedRowIsLive(initctx, ctx, iterctx, chooseCursor, dataset, expr); OwnedHqlExpr castLeftRow = createValue(no_implicitcast, LINK(rowType), LINK(source));//chooseCursor->queryBound())); iterctx.addAssign(rowExpr, castLeftRow); } iterctx.selectElse(ifStmt); CHqlBoundExpr bound; buildOrderedCompare(iterctx, dataset, sortList, bound, dataset, expr); OwnedHqlExpr compare = createBoolExpr(no_lt, LINK(bound.expr), getZero()); iterctx.addFilter(compare); { OwnedHqlExpr source = ensureIteratedRowIsLive(initctx, ctx, iterctx, chooseCursor, dataset, expr); OwnedHqlExpr castLeftRow = createValue(no_implicitcast, LINK(rowType), LINK(source));//chooseCursor->queryBound())); iterctx.addAssign(rowExpr, castLeftRow); } ctx.removeAssociation(savedMarker); //Set conditional later on, so test in main loop is explicit #ifndef CREATE_DEAULT_ROW_IF_NULL row->setConditional(true); #endif return createReferenceSelector(row); } BoundRow * HqlCppTranslator::buildOptimizeSelectFirstRow(BuildCtx & ctx, IHqlExpression * expr) { BoundRow * parentRow = NULL; node_operator op = expr->getOperator(); switch (op) { case no_compound_childaggregate: return buildOptimizeSelectFirstRow(ctx, expr->queryChild(0)); case no_hqlproject: case no_newusertable: { parentRow = buildOptimizeSelectFirstRow(ctx, expr->queryChild(0)); if (!parentRow) return NULL; } //fall through case no_newaggregate: { Owned tempRow = declareTempAnonRow(ctx, ctx, expr); Owned rowBuilder = createRowBuilder(ctx, tempRow); Owned createdRef = createReferenceSelector(rowBuilder); BuildCtx subctx(ctx); subctx.addGroup(); if (parentRow) { if (op == no_hqlproject) bindTableCursor(ctx, expr->queryChild(0), tempRow->queryBound(), no_left, querySelSeq(expr)); else bindTableCursor(ctx, expr->queryChild(0), tempRow->queryBound(), no_none, NULL); } doBuildRowAssignAggregate(subctx, createdRef, expr); finalizeTempRow(ctx, tempRow, rowBuilder); return tempRow; } // default: return NULL; } } void HqlCppTranslator::convertBoundDatasetToFirstRow(IHqlExpression * expr, CHqlBoundExpr & bound) { Owned type = makeReferenceModifier(expr->getType()); ITypeInfo * boundType = bound.queryType(); if (isArrayRowset(boundType)) { Linked rowType = queryUnqualifiedType(queryRowType(boundType)); rowType.setown(makeReferenceModifier(LINK(rowType))); if (hasLinkedRow(boundType)) type.setown(setLinkCountedAttr(type, true)); bound.expr.setown(createValue(no_deref, LINK(rowType), LINK(bound.expr))); } else if (bound.queryType()->isReference()) bound.expr.setown(createValue(no_typetransfer, LINK(type), bound.expr.getClear())); else bound.expr.setown(createValue(no_implicitcast, LINK(type), bound.expr.getClear())); } void HqlCppTranslator::convertBoundRowToDataset(BuildCtx & ctx, CHqlBoundExpr & bound, const BoundRow * row, ExpressionFormat preferredFormat) { IHqlExpression * boundRow = row->queryBound(); IHqlExpression * record = row->queryDataset()->queryRecord(); Owned type = makeTableType(makeRowType(LINK(record->queryType())), NULL, NULL, NULL); Owned refType = makeReferenceModifier(LINK(type)); if (hasLinkCountedModifier(boundRow->queryType()) && (preferredFormat != FormatBlockedDataset)) { OwnedHqlExpr curActivityId = getCurrentActivityId(ctx); StringBuffer allocatorName; ensureRowAllocator(allocatorName, ctx, record, curActivityId); OwnedHqlExpr src = getPointer(boundRow); //We can't just take the address of the link counted row, have to create a temporary dataset //could be fixed once the link counting is tracked on rows and datasets CHqlBoundTarget target; createTempFor(ctx, type, target, typemod_none, FormatLinkedDataset); StringBuffer s; generateExprCpp(s, target.expr).append(".setRow(").append(allocatorName).append(","); generateExprCpp(s, src).append(");"); ctx.addQuoted(s); bound.setFromTarget(target); } else if (boundRow->queryType()->isReference()) bound.expr.setown(createValue(no_typetransfer, LINK(refType), LINK(boundRow))); else bound.expr.setown(createValue(no_implicitcast, LINK(refType), LINK(boundRow))); bound.count.setown(getSizetConstant(1)); } IHqlExpression * HqlCppTranslator::ensureIteratedRowIsLive(BuildCtx & initctx, BuildCtx & searchctx, BuildCtx & iterctx, BoundRow * row, IHqlExpression * dataset, IHqlExpression * rowExpr) { //The problem is that we are iterating through the rows in a dataset, and we need to access the "best" row outside of the loop //However subsequent iterations of the loop might invalidate the current row, so we need to ensure the best row is retained. //There should really be a better way, but it isn't too bad with link counted rows. bool needToPreserve = false; IHqlExpression * ds = dataset; while (ds && !needToPreserve) { if (ds->isDatarow()) { if (initctx.queryAssociation(ds, AssocRow, NULL)) break; } else { if (initctx.queryMatchExpr(ds)) break; } switch (ds->getOperator()) { case no_filter: case no_grouped: case no_stepped: case no_sorted: case no_distributed: case no_preservemeta: case no_choosen: case no_selectnth: // can occur as the lhs of no_select case no_compound_childnormalize: case no_compound_selectnew: case no_compound_childread: case no_dataset_alias: ds = ds->queryChild(0); break; case no_select: if (ds->isDataset()) { if (ds->hasProperty(newAtom)) { ds = ds->queryChild(0); //don't walk complexds[1].childDataset since the [1] will be generated as a temporary // if (!ds->isDataset() && (ds->getOperator() != no_select)) // ds = NULL; } else ds = NULL; } else { //ds.x.y, always walk to to ds.x ds = ds->queryChild(0); } break; case no_rows: case no_call: case no_externalcall: case no_getresult: case no_getgraphresult: case no_alias: ds = NULL; break; default: needToPreserve = true; break; } } OwnedHqlExpr source = getPointer(row->queryBound()); if (!needToPreserve || initctx.hasAssociation(*row, false)) return source.getClear(); //If link counted, then declare a member that is a link counted row to ensure this row remains linked. ITypeInfo * rowType = rowExpr->queryType(); if (hasLinkCountedModifier(row->queryBound())) { CHqlBoundTarget saveTarget; createTempFor(initctx, rowType, saveTarget, typemod_wrapper, FormatLinkedDataset); StringBuffer s; generateExprCpp(s, saveTarget.expr); s.append(".set("); generateExprCpp(s, source).append(");"); iterctx.addQuoted(s); return getPointer(saveTarget.expr); } BuildCtx childctx(iterctx); childctx.addGroup(); Owned tempRow = declareTempRow(childctx, childctx, rowExpr); Owned rowBuilder = createRowBuilder(childctx, tempRow); OwnedHqlExpr sourceSelector = ensureActiveRow(row->querySelector()); buildAssign(childctx, rowBuilder->querySelector(), sourceSelector); finalizeTempRow(childctx, tempRow, rowBuilder); return getPointer(tempRow->queryBound()); } IReferenceSelector * HqlCppTranslator::buildDatasetIndexViaIterator(BuildCtx & ctx, IHqlExpression * expr) { OwnedHqlExpr dataset = normalizeAnyDatasetAliases(querySkipDatasetMeta(expr->queryChild(0))); IHqlExpression * index = expr->queryChild(1); IHqlExpression * childDataset = dataset; switch (dataset->getOperator()) { case no_hqlproject: //optimize selectnth(project(rows, t), n) to projectrow(selectnth(rows, n), t) IHqlExpression * transform = dataset->queryChild(1); if (!containsSkip(transform) && !expr->hasProperty(_countProject_Atom)) childDataset = dataset->queryChild(0); break; } //create a temporary //Following works because rows are created as temporaries in the class, so still in scope outside the iterate loop. //Not a strictly correct assumption - e.g., if ever done in the main process() code. Owned rowType = makeReferenceModifier(expr->getType()); OwnedHqlExpr rowExpr = ctx.getTempDeclare(rowType, NULL); Owned row = createBoundRow(expr, rowExpr); CHqlBoundExpr boundCleared; #ifdef CREATE_DEAULT_ROW_IF_NULL buildDefaultRow(ctx, expr, boundCleared); #else buildNullRow(ctx, expr, boundCleared); row->setConditional(true); #endif OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); //Declare row for final level, iterate the appropriate number of times, and then assign and break. BuildCtx initctx(ctx); initctx.addGroup(); // add a group to allow a temporary to be declared later. initctx.addAssign(rowExpr, defaultRowPtr); HqlExprAssociation * savedMarker = ctx.associateExpr(queryConditionalRowMarker(), rowExpr); BuildCtx iterctx(ctx); bool done = false; if (childDataset->getOperator() == no_rows) //hasOutOfLineModifier(dataset->queryType())) { CHqlBoundExpr boundDs; buildDataset(ctx, childDataset, boundDs, FormatNatural); if (boundDs.count && isArrayRowset(boundDs.expr->queryType())) { OwnedHqlExpr castIndex = ensureExprType(index, unsignedType); OwnedHqlExpr adjustedIndex = adjustValue(castIndex, -1); CHqlBoundExpr boundIndex; buildExpr(ctx, adjustedIndex, boundIndex); OwnedHqlExpr count = getBoundCount(boundDs); // could be serialized, so can't assume bound.count is set OwnedHqlExpr test = createValue(no_gt, makeBoolType(), LINK(count), LINK(boundIndex.expr)); iterctx.addFilter(test); if (dataset != childDataset) { Owned datasetRowType = makeRowReferenceType(boundDs); OwnedHqlExpr selectedRow = createValue(no_index, LINK(datasetRowType), LINK(boundDs.expr), LINK(boundIndex.expr)); OwnedHqlExpr projected = createRow(no_projectrow, createTranslated(selectedRow), createComma(LINK(dataset->queryChild(1)), LINK(querySelSeq(dataset)))); Owned newRow = buildNewRow(iterctx, projected); OwnedHqlExpr newPtr = getPointer(newRow->queryRootRow()->queryBound()); iterctx.addAssign(rowExpr, newPtr); } else { OwnedHqlExpr selectedRow = createValue(no_index, LINK(rowType), LINK(boundDs.expr), LINK(boundIndex.expr)); iterctx.addAssign(rowExpr, selectedRow); } done = true; } } if (!done) { BoundRow * chooseCursor; //If choosing the first element, then no need to maintain a counter... IValue * indexValue = index->queryValue(); if (indexValue && (indexValue->getIntValue() == 1)) chooseCursor = buildDatasetIterate(iterctx, dataset, true); else chooseCursor = buildDatasetIterate(iterctx, expr, true); if (chooseCursor) { OwnedHqlExpr source = getPointer(chooseCursor->queryBound()); //MORE: Need casts because cursor may be (probably are) constant, but temporary isn't //should somehow fnd out by looking at the cursors. OwnedHqlExpr castLeftRow = createValue(no_implicitcast, LINK(rowType), LINK(source)); iterctx.addAssign(rowExpr, castLeftRow); iterctx.addBreak(); } } ctx.removeAssociation(savedMarker); ctx.associate(*row); return createReferenceSelector(row); } IReferenceSelector * HqlCppTranslator::buildDatasetIndex(BuildCtx & ctx, IHqlExpression * expr) { HqlExprAssociation * match = ctx.queryAssociation(expr, AssocRow, NULL); if (match) return createReferenceSelector(static_cast(match)); #if 0 //Causes some queries (ncf10) to run out of memory, so disable for the moment. OwnedHqlExpr optimized = optimizeHqlExpression(expr, getOptimizeFlags()|HOOcompoundproject); if (optimized != expr) return buildNewRow(ctx, optimized); #endif OwnedHqlExpr dataset = normalizeAnyDatasetAliases(expr->queryChild(0)); //Special cases: //i) selecting row [1] from something that only has a single row //ii) selecting row [n] from something that can be iterated. //iii) row[1] from something sorted that can be iterated. BoundRow * row = NULL; if (isTrivialSelectN(expr)) { BoundRow * row = NULL; #if 0 //This could be a good idea - but again it can mess up cse since dataset never gets bound. //Could enable if I implement cse on datasets within transforms. // if (canIterateInline(&ctx, dataset)) // row = buildOptimizeSelectFirstRow(ctx, dataset); #endif if (!row) { CHqlBoundExpr bound; buildDataset(ctx, dataset, bound, FormatNatural); convertBoundDatasetToFirstRow(expr, bound); row = bindRow(ctx, expr, bound.expr); } return createReferenceSelector(row); } else if (canIterateInline(&ctx, dataset)) { //MORE? Following doesn't work for implicit normalize which iterates multiple levels bool specialCase = false; dataset.set(querySkipDatasetMeta(dataset)); switch (dataset->getOperator()) { case no_select: specialCase = !isMultiLevelDatasetSelector(expr, false); break; case no_if: case no_inlinedictionary: case no_inlinetable: case no_join: //Always creates a temporary, so don't use an iterator specialCase = true; break; default: specialCase = alwaysEvaluatesToBound(dataset); break; } if (!specialCase) return buildDatasetIndexViaIterator(ctx, expr); } else if (isSelectSortedTop(expr) && canIterateInline(&ctx, dataset->queryChild(0))) { return doBuildRowSelectTop(ctx, expr); } //MORE: Is this a good idea??? else if (!canProcessInline(&ctx, expr)) { CHqlBoundExpr bound; OwnedHqlExpr dsExpr = expr->isDatarow() ? createDatasetFromRow(LINK(expr)) : LINK(expr); buildDataset(ctx, dsExpr, bound, FormatNatural); convertBoundDatasetToFirstRow(expr, bound); row = bindRow(ctx, expr, bound.expr); } if (!row) { Owned cursor = createDatasetSelector(ctx, dataset); row = cursor->buildSelect(ctx, expr); if (!row) { CHqlBoundExpr boundCleared; buildDefaultRow(ctx, dataset, boundCleared); OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); row = bindRow(ctx, expr, defaultRowPtr); } } return createReferenceSelector(row); } IReferenceSelector * HqlCppTranslator::buildDatasetSelectMap(BuildCtx & ctx, IHqlExpression * expr) { HqlExprAssociation * match = ctx.queryAssociation(expr, AssocRow, NULL); if (match) return createReferenceSelector(static_cast(match)); OwnedHqlExpr dataset = normalizeAnyDatasetAliases(expr->queryChild(0)); // Create a row to pass to the lookup function BoundRow * row = NULL; if (!canProcessInline(&ctx, expr)) { CHqlBoundExpr bound; OwnedHqlExpr dsExpr = expr->isDatarow() ? createDatasetFromRow(LINK(expr)) : LINK(expr); buildDataset(ctx, dsExpr, bound, FormatNatural); convertBoundDatasetToFirstRow(expr, bound); row = bindRow(ctx, expr, bound.expr); } if (!row) { Owned cursor = createDatasetSelector(ctx, dataset); row = cursor->buildSelect(ctx, expr); if (!row) { CHqlBoundExpr boundCleared; buildDefaultRow(ctx, dataset, boundCleared); OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); row = bindRow(ctx, expr, defaultRowPtr); } } return createReferenceSelector(row); } //--------------------------------------------------------------------------- IHqlExpression * HqlCppTranslator::buildGetLocalResult(BuildCtx & ctx, IHqlExpression * expr, bool preferLinkedRows) { IHqlExpression * graphId = expr->queryChild(1); IHqlExpression * resultNum = expr->queryChild(2); Linked exprType = queryUnqualifiedType(expr->queryType()); if (preferLinkedRows && !hasLinkCountedModifier(exprType)) exprType.setown(makeAttributeModifier(LINK(exprType), getLinkCountedAttr())); if (expr->hasProperty(externalAtom)) { IHqlExpression * resultInstance = queryPropertyChild(expr, externalAtom, 0); HqlExprAssociation * matchedResults = ctx.queryMatchExpr(resultInstance); if (!matchedResults) { //Very unusual - a result is required from a child query, but that child query is actually in //the parent/grandparent. We need to evaluate in the parent instead. CHqlBoundExpr match; if (!buildExprInCorrectContext(ctx, expr, match, false)) throwUnexpected(); return match.getTranslatedExpr(); } HqlExprArray args; args.append(*LINK(matchedResults->queryExpr())); args.append(*LINK(resultNum)); if (preferLinkedRows) return bindFunctionCall(getChildQueryLinkedResultAtom, args, exprType); return bindFunctionCall(getChildQueryResultAtom, args, exprType); } assertex(activeActivities.ordinality()); queryAddResultDependancy(activeActivities.tos(), graphId, resultNum); SubGraphInfo * activeSubgraph = queryActiveSubGraph(ctx); assertex(activeSubgraph && graphId == activeSubgraph->graphTag); unique_id_t id = activeSubgraph->graphId; EvalContext * instance = queryEvalContext(ctx); OwnedHqlExpr retInstanceExpr; if (instance && !insideOnCreate(ctx)) retInstanceExpr.setown(instance->createGraphLookup(id, false)); else retInstanceExpr.setown(doCreateGraphLookup(ctx, ctx, id, "this", true)); HqlExprArray args; args.append(*LINK(retInstanceExpr)); args.append(*LINK(resultNum)); if (preferLinkedRows) return bindFunctionCall(getLocalLinkedResultAtom, args, exprType); return bindFunctionCall(getLocalResultAtom, args, exprType); } void HqlCppTranslator::doBuildAssignGetGraphResult(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr) { if (expr->hasProperty(_streaming_Atom)) throwError(HQLERR_LoopTooComplexForParallel); bool isTargetLinkCounted = hasLinkCountedModifier(target.queryType()); if (expr->hasProperty(externalAtom)) { OwnedHqlExpr call = buildGetLocalResult(ctx, expr, isTargetLinkCounted); buildExprAssign(ctx, target, call); return; } if (!isCurrentActiveGraph(ctx, expr->queryChild(1))) { CHqlBoundExpr match; if (!buildExprInCorrectContext(ctx, expr, match, false)) throwError(HQLERR_GraphContextNotFound); assign(ctx, target, match); return; } OwnedHqlExpr call = buildGetLocalResult(ctx, expr, isTargetLinkCounted); buildExprAssign(ctx, target, call); } void HqlCppTranslator::doBuildExprGetGraphResult(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ExpressionFormat format) { if (!expr->hasProperty(externalAtom)) { doBuildAliasValue(ctx, expr, tgt); return; if (!isCurrentActiveGraph(ctx, expr->queryChild(1))) { if (!buildExprInCorrectContext(ctx, expr, tgt, false)) throwError(HQLERR_GraphContextNotFound); return; } } bool useLinkCounted = recordRequiresSerialization(expr->queryRecord()) || options.tempDatasetsUseLinkedRows; OwnedHqlExpr call = buildGetLocalResult(ctx, expr, useLinkCounted); switch (expr->queryType()->getTypeCode()) { case type_row: throwUnexpected(); case type_dictionary: case type_table: case type_groupedtable: buildTempExpr(ctx, call, tgt); break; default: buildExpr(ctx, call, tgt); break; } } ABoundActivity * HqlCppTranslator::doBuildActivityGetGraphResult(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * graphId = expr->queryChild(1); IHqlExpression * resultNum = expr->queryChild(2); ThorActivityKind activityKind = (expr->hasProperty(_streaming_Atom) ? TAKlocalstreamread : TAKlocalresultread); bool useImplementationClass = options.minimizeActivityClasses && (resultNum->getOperator() == no_constant); Owned instance = new ActivityInstance(*this, ctx, activityKind, expr, "LocalResultRead"); if (useImplementationClass) instance->setImplementationClass(newLocalResultReadArgAtom); if (expr->hasProperty(_loop_Atom)) { if (isCurrentActiveGraph(ctx, graphId)) instance->graphLabel.set("Begin Loop"); else instance->graphLabel.set("Outer Loop Input"); } buildActivityFramework(instance); buildInstancePrefix(instance); if (!useImplementationClass) doBuildUnsignedFunction(instance->classctx, "querySequence", resultNum); else instance->addConstructorParameter(resultNum); addGraphIdAttribute(instance, ctx, graphId); buildInstanceSuffix(instance); queryAddResultDependancy(*instance->queryBoundActivity(), graphId, resultNum); return instance->getBoundActivity(); } ABoundActivity * HqlCppTranslator::doBuildActivitySetGraphResult(BuildCtx & ctx, IHqlExpression * expr, bool isRoot) { IHqlExpression * dataset = expr->queryChild(0); IHqlExpression * graphId = expr->queryChild(1); IHqlExpression * resultNum = expr->queryChild(2); bool isSpill = expr->hasProperty(_spill_Atom); ABoundActivity * parentActivity = activeActivities.ordinality() ? &activeActivities.tos() : NULL; Owned boundDataset = buildCachedActivity(ctx, dataset); bool useImplementationClass = options.minimizeActivityClasses; Owned instance; if (expr->getOperator() == no_spillgraphresult) { instance.setown(new ActivityInstance(*this, ctx, TAKlocalresultspill, expr, "LocalResultSpill")); } else { instance.setown(new ActivityInstance(*this, ctx, TAKlocalresultwrite, expr, "LocalResultWrite")); } if (useImplementationClass) instance->setImplementationClass(newLocalResultSpillArgAtom); if (expr->hasProperty(_loop_Atom)) instance->graphLabel.set("End Loop"); buildActivityFramework(instance, isRoot && !isSpill); buildInstancePrefix(instance); if (!useImplementationClass) { doBuildUnsignedFunction(instance->classctx, "querySequence", resultNum); doBuildBoolFunction(instance->classctx, "usedOutsideGraph", !isSpill); } else { instance->addConstructorParameter(resultNum); instance->addConstructorParameter(queryBoolExpr(!isSpill)); } if (parentActivity && !insideRemoteGraph(ctx) && !isSpill) { const char * relationship; if (expr->hasProperty(_loop_Atom)) relationship = "Body"; else if (insideRemoteGraph(ctx)) relationship = "Remote"; else relationship = "Child"; addDependency(ctx, instance->queryBoundActivity(), parentActivity, childAtom, relationship); } instance->addAttributeBool("_isSpill", isSpill); if (targetRoxie()) addGraphIdAttribute(instance, ctx, graphId); buildInstanceSuffix(instance); buildConnectInputOutput(ctx, instance, boundDataset, 0, 0); associateRemoteResult(*instance, graphId, resultNum); return instance->getBoundActivity(); } ABoundActivity * HqlCppTranslator::doBuildActivityReturnResult(BuildCtx & ctx, IHqlExpression * expr, bool isRoot) { IHqlExpression * dataset = expr->queryChild(0); ABoundActivity * parentActivity = activeActivities.ordinality() ? &activeActivities.tos() : NULL; Owned boundDataset = buildCachedActivity(ctx, dataset); ThorActivityKind kind; const char * helper; if (dataset->isDataset()) { kind = TAKdatasetresult; helper = "DatasetResult"; } else { kind = TAKrowresult; helper = "RowResult"; } Owned instance = new ActivityInstance(*this, ctx, kind, expr, helper); buildActivityFramework(instance, isRoot); buildInstancePrefix(instance); if (parentActivity && !insideRemoteGraph(ctx)) addDependency(ctx, instance->queryBoundActivity(), parentActivity, childAtom, "Child"); buildInstanceSuffix(instance); buildConnectInputOutput(ctx, instance, boundDataset, 0, 0); return instance->getBoundActivity(); } void HqlCppTranslator::doBuildAssignLoopCounter(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr) { if (!isCurrentActiveGraph(ctx, expr->queryChild(0))) { CHqlBoundExpr match; if (!buildExprInCorrectContext(ctx, expr, match, false)) throwError(HQLERR_GraphContextNotFound); assign(ctx, target, match); return; } HqlExprArray args; OwnedHqlExpr call = bindFunctionCall(getGraphLoopCounterAtom, args); buildExprAssign(ctx, target, call); } //--------------------------------------------------------------------------- ABoundActivity * HqlCppTranslator::doBuildActivityGetGraphLoopResult(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * graphId = expr->queryChild(1); IHqlExpression * resultNum = expr->queryChild(2); Owned instance = new ActivityInstance(*this, ctx, TAKgraphloopresultread, expr, "GraphLoopResultRead"); buildActivityFramework(instance); buildInstancePrefix(instance); doBuildUnsignedFunction(instance->startctx, "querySequence", resultNum); addGraphIdAttribute(instance, ctx, graphId); buildInstanceSuffix(instance); return instance->getBoundActivity(); } ABoundActivity * HqlCppTranslator::doBuildActivitySetGraphLoopResult(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * dataset = expr->queryChild(0); IHqlExpression * graphId = expr->queryChild(1); bool isSpill = expr->hasProperty(_spill_Atom); ABoundActivity * parentActivity = activeActivities.ordinality() ? &activeActivities.tos() : NULL; Owned boundDataset = buildCachedActivity(ctx, dataset); bool useImplementationClass = options.minimizeActivityClasses; Owned instance = new ActivityInstance(*this, ctx, TAKgraphloopresultwrite, expr, "GraphLoopResultWrite"); if (useImplementationClass) instance->setImplementationClass(newGraphLoopResultWriteArgAtom); buildActivityFramework(instance, true); buildInstancePrefix(instance); if (parentActivity && !insideRemoteGraph(ctx) && !isSpill) addDependency(ctx, instance->queryBoundActivity(), parentActivity, childAtom, "Body"); if (targetRoxie()) addGraphIdAttribute(instance, ctx, graphId); buildInstanceSuffix(instance); buildConnectInputOutput(ctx, instance, boundDataset, 0, 0); return instance->getBoundActivity(); } //--------------------------------------------------------------------------- static IHqlExpression * queryResultExpr(IHqlExpression * expr) { loop { switch (expr->getOperator()) { case no_compound: expr = expr->queryChild(1); break; case no_subgraph: expr = expr->queryChild(0); break; case no_returnresult: return expr; default: throwUnexpectedOp(expr->getOperator()); } } } ABoundActivity * HqlCppTranslator::doBuildActivityForceLocal(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * child = expr->queryChild(0); if (targetHThor() || (targetThor() && !insideChildQuery(ctx))) { WARNING(HQLWRN_LocalHasNoEffect); return buildCachedActivity(ctx, child); } OwnedHqlExpr result = createValue(no_returnresult, makeVoidType(), LINK(child)); OwnedHqlExpr remote = resourceThorGraph(*this, result, RoxieCluster, 1, NULL); unique_id_t localId = doBuildThorSubGraph(ctx, remote, SubGraphRemote); Owned instance = new ActivityInstance(*this, ctx, TAKlocalgraph, expr, "Null"); buildActivityFramework(instance); buildInstancePrefix(instance); instance->addAttributeInt("_subgraph", localId); ActivityAssociation * match = static_cast(ctx.queryAssociation(queryResultExpr(remote), AssocActivity, NULL)); assertex(match); addDependency(ctx, match->activity, instance->queryBoundActivity(), childAtom); buildInstanceSuffix(instance); return instance->getBoundActivity(); } void HqlCppTranslator::doBuildStmtApply(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * dataset = expr->queryChild(0); IHqlExpression * start = expr->queryProperty(beforeAtom); IHqlExpression * end = expr->queryProperty(afterAtom); if (start) buildStmt(ctx, start->queryChild(0)); BuildCtx condctx(ctx); BoundRow * cursor = buildDatasetIterate(condctx, dataset, false); unsigned max = expr->numChildren(); for (unsigned i=1; i < max; i++) { IHqlExpression * cur = expr->queryChild(i); if (!cur->isAttribute()) buildStmt(condctx, cur); } if (end) buildStmt(ctx, end->queryChild(0)); }