/*############################################################################## Copyright (C) 2011 HPCC Systems. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ############################################################################## */ #include "jliball.hpp" #include "hql.hpp" #include "platform.h" #include "jlib.hpp" #include "jexcept.hpp" #include "jmisc.hpp" #include "javahash.hpp" #include "eclhelper.hpp" #include "hqlfunc.hpp" #include "hqlattr.hpp" #include "hqlhtcpp.ipp" #include "hqlwcpp.hpp" #include "hqlcpputil.hpp" #include "hqlcerrors.hpp" #include "hqlcatom.hpp" #include "hqlpmap.hpp" #include "hqlthql.hpp" #include "hqlcset.ipp" #include "hqlfold.hpp" #include "hqltcppc.ipp" #include "hqlutil.hpp" #include "hqliter.ipp" #ifdef CREATE_DEAULT_ROW_IF_NULL #define CREATE_DEAULT_ROW_IF_NULL_VALUE 1 #else #define CREATE_DEAULT_ROW_IF_NULL_VALUE 0 #endif //=========================================================================== IHqlExpression * getOutOfRangeValue(IHqlExpression * indexExpr) { IHqlExpression * dft = indexExpr->queryProperty(defaultAtom); if (dft) return LINK(dft->queryChild(0)); else return createNullExpr(indexExpr); } //=========================================================================== BaseDatasetCursor::BaseDatasetCursor(HqlCppTranslator & _translator, IHqlExpression * _ds, CHqlBoundExpr * _boundDs) : translator(_translator) { ds.set(_ds); record.set(ds->queryRecord()); if (_boundDs) boundDs.set(*_boundDs); } BoundRow * BaseDatasetCursor::buildIterateLoop(BuildCtx & ctx, bool needToBreak) { StringBuffer iterName; buildIterateClass(ctx, iterName, NULL); StringBuffer s, rowName; OwnedHqlExpr row = createRow(ctx, "row", rowName, false); //row = iter.first() s.clear().append(rowName).append(" = ").append(iterName).append(".first();"); ctx.addQuoted(s); //while (row) ctx.addLoop(row, NULL, false); BoundRow * cursor = translator.bindTableCursor(ctx, ds, row); // row = iter.next(); ctx.setNextDestructor(); s.clear().append(rowName).append(" = (byte *)").append(iterName).append(".next();"); ctx.addQuoted(s); return cursor; } void BaseDatasetCursor::buildIterateClass(BuildCtx & ctx, SharedHqlExpr & iter, SharedHqlExpr & row) { StringBuffer cursorName, rowName; buildIterateClass(ctx, cursorName, NULL); iter.setown(createVariable(cursorName.str(), makeBoolType())); row.setown(createRow(ctx, "row", rowName, false)); } void BaseDatasetCursor::buildIterateMembers(BuildCtx & declarectx, BuildCtx & initctx) { StringBuffer iterName; buildIterateClass(declarectx, iterName, &initctx); StringBuffer s, rowName; OwnedHqlExpr row = createRow(declarectx, "row", rowName, false); //row = iter.first() BuildCtx firstctx(declarectx); firstctx.addQuotedCompound("virtual bool first()"); s.clear().append(rowName).append(" = (byte *)").append(iterName).append(".first();"); firstctx.addQuoted(s); s.clear().append("return ").append(rowName).append(" != NULL;"); firstctx.addQuoted(s); //row = iter.first() BuildCtx nextctx(declarectx); nextctx.addQuotedCompound("virtual bool next()"); s.clear().append(rowName).append(" = (byte *)").append(iterName).append(".next();"); nextctx.addQuoted(s); s.clear().append("return ").append(rowName).append(" != NULL;"); nextctx.addQuoted(s); //iterate translator.bindTableCursor(declarectx, ds, row); } BoundRow * BaseDatasetCursor::buildSelectMap(BuildCtx & ctx, IHqlExpression * indexExpr) { // Should only be seen for dictionaries throwUnexpected(); } BoundRow * BaseDatasetCursor::buildSelectNth(BuildCtx & ctx, IHqlExpression * indexExpr) { //MORE: Check if the cursor already exists.... StringBuffer cursorName; buildIterateClass(ctx, cursorName, NULL); bool conditional = !indexExpr->hasProperty(noBoundCheckAtom); //create a unique dataset and associate it with a call to select //set value to be the field selection from the dataset StringBuffer s, rowName; OwnedHqlExpr row = createRow(ctx, "row", rowName, conditional && CREATE_DEAULT_ROW_IF_NULL_VALUE); CHqlBoundExpr boundIndex; OwnedHqlExpr index = adjustIndexBaseToZero(indexExpr->queryChild(1)); translator.buildExpr(ctx, index, boundIndex); //MORE: CREATE_DEAULT_ROW_IF_NULL - pass the default row to the select() function. //row = iter.select(n) s.clear().append(rowName).append(" = (byte *)").append(cursorName).append(".select("); translator.generateExprCpp(s, boundIndex.expr); s.append(");"); ctx.addQuoted(s); #ifdef CREATE_DEAULT_ROW_IF_NULL if (conditional) { CHqlBoundExpr boundCleared; translator.buildDefaultRow(ctx, ds, boundCleared); OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); BuildCtx subctx(ctx); OwnedHqlExpr test = createValue(no_not, makeBoolType(), LINK(row)); subctx.addFilter(test); subctx.addAssign(row, defaultRowPtr); conditional = false; } #endif BoundRow * cursor = translator.bindRow(ctx, indexExpr, row); cursor->setConditional(conditional); return cursor; } IHqlExpression * BaseDatasetCursor::createRow(BuildCtx & ctx, const char * prefix, StringBuffer & rowName, bool conditional) { translator.getUniqueId(rowName.append(prefix)); OwnedITypeInfo type; if (boundDs.expr && boundDs.expr->queryRecord()) type.setown(makeConstantModifier(makeRowReferenceType(boundDs))); else type.setown(makeConstantModifier(makeRowReferenceType(ds))); if (conditional) type.setown(setLinkCountedAttr(type, false)); OwnedHqlExpr row = createVariable(rowName, type.getClear()); ctx.addDeclare(row); return row.getClear(); } //--------------------------------------------------------------------------- BlockDatasetCursor::BlockDatasetCursor(HqlCppTranslator & _translator, IHqlExpression * _ds, CHqlBoundExpr & _boundDs) : BaseDatasetCursor(_translator, _ds, &_boundDs) { boundDs.set(_boundDs); assertex(boundDs.expr->isDatarow() || !isArrayRowset(boundDs.expr->queryType())); // I don't think this can ever be called at the moment } void BlockDatasetCursor::buildCount(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.setown(translator.getBoundCount(boundDs)); } void BlockDatasetCursor::buildExists(BuildCtx & ctx, CHqlBoundExpr & tgt) { if (boundDs.count) tgt.expr.setown(createValue(no_ne, makeBoolType(), LINK(boundDs.count), getZero())); else tgt.expr.setown(createValue(no_ne, makeBoolType(), LINK(boundDs.length), getZero())); } void BlockDatasetCursor::buildIterateClass(BuildCtx & ctx, StringBuffer & cursorName, BuildCtx * initctx) { translator.getUniqueId(cursorName.append("iter")); StringBuffer extraParams; StringBuffer decl,args; if (translator.isFixedRecordSize(record)) { //RtlFixedDatasetCursor cursor(len, data, size) decl.append("RtlFixedDatasetCursor"); extraParams.append(", ").append(translator.getFixedRecordSize(record)); } else { //RtlVariableDatasetCursor cursor(len, data, recordSize) decl.append("RtlVariableDatasetCursor"); translator.buildMetaForRecord(extraParams.append(", "), record); } OwnedHqlExpr size = translator.getBoundSize(boundDs); decl.append(" ").append(cursorName); translator.generateExprCpp(args, size); args.append(", "); translator.generateExprCpp(args, boundDs.expr); args.append(extraParams); if (initctx) { StringBuffer s; s.append(cursorName).append(".init(").append(args).append(");"); initctx->addQuoted(s); } else { decl.append("(").append(args).append(")"); } decl.append(";"); ctx.addQuoted(decl); } //--------------------------------------------------------------------------- bool isEmptyDataset(const CHqlBoundExpr & bound) { IValue * value = NULL; if (bound.length) value = bound.length->queryValue(); else if (bound.count) value = bound.count->queryValue(); return (value && value->getIntValue() == 0); } InlineBlockDatasetCursor::InlineBlockDatasetCursor(HqlCppTranslator & _translator, IHqlExpression * _ds, CHqlBoundExpr & _boundDs) : BlockDatasetCursor(_translator, _ds, _boundDs) { } BoundRow * InlineBlockDatasetCursor::buildIterateLoop(BuildCtx & ctx, bool needToBreak) { StringBuffer rowName; OwnedHqlExpr row = createRow(ctx, "row", rowName, false); if (isEmptyDataset(boundDs)) { ctx.addFilter(queryBoolExpr(false)); return translator.bindTableCursor(ctx, ds, row); } StringBuffer s; //row = ds; OwnedHqlExpr address = getPointer(boundDs.expr); OwnedHqlExpr cast = createValue(no_implicitcast, row->getType(), LINK(address)); ctx.addAssign(row, cast); OwnedHqlExpr test; if (boundDs.length) { OwnedHqlExpr length = translator.getBoundLength(boundDs); StringBuffer endName; OwnedHqlExpr end = createRow(ctx, "end", endName, false); //end = row+length; s.clear().append(endName).append(" = ").append(rowName).append("+"); translator.generateExprCpp(s, length).append(";"); ctx.addQuoted(s); //while (row < end) test.setown(createValue(no_lt, makeBoolType(), LINK(row), LINK(end))); } else if (matchesConstantValue(boundDs.count, 1) && !needToBreak) { //Optimize count=1, needToBreak = false; ctx.addGroup(); return translator.bindTableCursor(ctx, ds, row); } else { OwnedHqlExpr count = translator.getBoundCount(boundDs); //count = OwnedHqlExpr counter = ctx.getTempDeclare(unsignedType, count); //while (count--) test.setown(createValue(no_postdec, LINK(counter))); } ctx.addLoop(test, NULL, false); BoundRow * cursor = translator.bindTableCursor(ctx, ds, row); //row = row + recordSize OwnedHqlExpr size = translator.getRecordSize(cursor->querySelector()); CHqlBoundExpr boundSize; translator.buildExpr(ctx, size, boundSize); ctx.setNextDestructor(); if (translator.queryOptions().optimizeIncrement) { ctx.addAssignIncrement(row, boundSize.expr); } else { OwnedHqlExpr inc = createValue(no_add, row->getType(), LINK(row), LINK(boundSize.expr)); ctx.addAssign(row, inc); } return cursor; } BoundRow * InlineBlockDatasetCursor::buildSelectFirst(BuildCtx & ctx, IHqlExpression * indexExpr, bool createDefaultRowIfNull) { StringBuffer s, rowName; bool conditional = !indexExpr->hasProperty(noBoundCheckAtom); OwnedHqlExpr row = createRow(ctx, "row", rowName, (conditional && createDefaultRowIfNull)); BuildCtx subctx(ctx); if (conditional) { HqlExprAttr test; if (boundDs.count) { IValue * countValue = boundDs.count->queryValue(); if (countValue) { if (countValue->getIntValue() == 0) return NULL; } else { OwnedHqlExpr max = createTranslated(boundDs.count); test.setown(createCompare(no_ne, max, queryZero())); } } else { OwnedHqlExpr max = createTranslated(boundDs.length); test.setown(createCompare(no_gt, max, queryZero())); } if (test) { CHqlBoundExpr boundCleared; if (createDefaultRowIfNull) { translator.buildDefaultRow(ctx, ds, boundCleared); conditional = false; } else translator.buildNullRow(ctx, ds, boundCleared); OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); ctx.addAssign(row, defaultRowPtr); translator.buildFilter(subctx, test); } else conditional = false; } if (isArrayRowset(boundDs.expr->queryType())) { s.clear().append(rowName).append(" = "); translator.generateExprCpp(s, boundDs.expr).append("[0];"); subctx.addQuoted(s); } else { OwnedHqlExpr address = getPointer(boundDs.expr); s.clear().append(rowName).append(" = (byte *)(void *)"); // more: should really be const... translator.generateExprCpp(s, address); s.append(";"); subctx.addQuoted(s); } BoundRow * cursor = translator.bindRow(ctx, indexExpr, row); cursor->setConditional(conditional); return cursor; } BoundRow * InlineBlockDatasetCursor::buildSelectNth(BuildCtx & ctx, IHqlExpression * indexExpr) { assertex(!isArrayRowset(boundDs.expr->queryType())); // I don't think this can ever be called at the moment OwnedHqlExpr index = foldHqlExpression(indexExpr->queryChild(1)); if (!translator.isFixedRecordSize(record)) { if (matchesConstantValue(index, 1)) return buildSelectFirst(ctx, indexExpr, CREATE_DEAULT_ROW_IF_NULL_VALUE); return BlockDatasetCursor::buildSelectNth(ctx, indexExpr); } if (matchesConstantValue(index, 1)) return buildSelectFirst(ctx, indexExpr, CREATE_DEAULT_ROW_IF_NULL_VALUE); bool conditional = !indexExpr->hasProperty(noBoundCheckAtom); //row = NULL StringBuffer s, rowName; OwnedHqlExpr row = createRow(ctx, "row", rowName, (conditional && CREATE_DEAULT_ROW_IF_NULL_VALUE)); //if (index > 0 && (index <= count) or (index * fixedSize <= size) //MORE: Need to be very careful about the types... OwnedHqlExpr base0Index; unsigned fixedSize = translator.getFixedRecordSize(record); BuildCtx subctx(ctx); if (conditional) { OwnedHqlExpr simpleIndex = translator.buildSimplifyExpr(ctx, index); base0Index.setown(adjustIndexBaseToZero(simpleIndex)); IValue * indexValue = index->queryValue(); OwnedHqlExpr test; if (indexValue) { if (indexValue->getIntValue() <= 0) return NULL; } else test.setown(createCompare(no_gt, simpleIndex, queryZero())); IHqlExpression * test2 = NULL; if (boundDs.count) { IValue * countValue = boundDs.count->queryValue(); if (countValue && indexValue) { if (indexValue->getIntValue() > countValue->getIntValue()) return NULL; } else { OwnedHqlExpr max = createTranslated(boundDs.count); test2 = createCompare(no_le, simpleIndex, max); } } else { OwnedHqlExpr max = createTranslated(boundDs.length); OwnedHqlExpr offset = multiplyValue(simpleIndex, fixedSize); test2 = createCompare(no_le, offset, max); } extendConditionOwn(test, no_and, test2); if (test) { CHqlBoundExpr boundCleared; #ifdef CREATE_DEAULT_ROW_IF_NULL translator.buildDefaultRow(ctx, ds, boundCleared); conditional = false; #else translator.buildNullRow(ctx, ds, boundCleared); #endif OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); ctx.addAssign(row, defaultRowPtr); translator.buildFilter(subctx, test); } else conditional = false; } else { CHqlBoundExpr boundIndex; OwnedHqlExpr base0 = adjustIndexBaseToZero(index); translator.buildExpr(ctx, base0, boundIndex); base0Index.setown(boundIndex.getTranslatedExpr()); } //row = base + index * fixedSize; OwnedHqlExpr address = LINK(boundDs.expr);//getPointer(boundDs.expr); s.clear().append(rowName).append(" = (byte *)(void *)"); // more: should really be const... translator.generateExprCpp(s, address); CHqlBoundExpr boundOffset; OwnedHqlExpr offset = multiplyValue(base0Index, fixedSize); translator.buildExpr(subctx, offset, boundOffset); s.append(" + ("); translator.generateExprCpp(s, boundOffset.expr).append(")"); s.append(";"); subctx.addQuoted(s); BoundRow * cursor = translator.bindRow(ctx, indexExpr, row); cursor->setConditional(conditional); return cursor; } //--------------------------------------------------------------------------- InlineLinkedDatasetCursor::InlineLinkedDatasetCursor(HqlCppTranslator & _translator, IHqlExpression * _ds, CHqlBoundExpr & _boundDs) : BaseDatasetCursor(_translator, _ds, &_boundDs) { assertex(boundDs.count != NULL); assertex(isArrayRowset(boundDs.expr->queryType())); } void InlineLinkedDatasetCursor::buildCount(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.set(boundDs.count); } void InlineLinkedDatasetCursor::buildExists(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.setown(createValue(no_ne, makeBoolType(), LINK(boundDs.count), getZero())); } void InlineLinkedDatasetCursor::buildIterateClass(BuildCtx & ctx, StringBuffer & cursorName, BuildCtx * initctx) { translator.getUniqueId(cursorName.append("iter")); //RtlFixedDatasetCursor cursor(len, data, size) StringBuffer decl; decl.append("RtlLinkedDatasetCursor ").append(cursorName); StringBuffer args; translator.generateExprCpp(args, boundDs.count); args.append(", "); translator.generateExprCpp(args, boundDs.expr); if (initctx) { StringBuffer s; s.append(cursorName).append(".init(").append(args).append(");"); initctx->addQuoted(s); } else { decl.append("(").append(args).append(")"); } decl.append(";"); ctx.addQuoted(decl); } BoundRow * InlineLinkedDatasetCursor::buildIterateLoop(BuildCtx & ctx, bool needToBreak) { StringBuffer rowName; OwnedHqlExpr row = createRow(ctx, "row", rowName, false); if (isEmptyDataset(boundDs)) { ctx.addFilter(queryBoolExpr(false)); return translator.bindTableCursor(ctx, ds, row); } if (matchesConstantValue(boundDs.count, 1) && !needToBreak) { CHqlBoundExpr boundRow; boundRow.set(boundDs); translator.convertBoundDatasetToFirstRow(ds, boundRow); //Optimize count=1, needToBreak = false; ctx.addGroup(); return translator.bindTableCursor(ctx, ds, boundRow.expr); } StringBuffer cursorName, s; translator.getUniqueId(cursorName.append("cur")); //row = ds; OwnedHqlExpr address = getPointer(boundDs.expr); // ensure no longer a wrapped item s.clear().append("byte * * ").append(cursorName).append(" = "); translator.generateExprCpp(s, address).append(";"); ctx.addQuoted(s); OwnedHqlExpr test; OwnedHqlExpr count = translator.getBoundCount(boundDs); //count = OwnedHqlExpr counter = ctx.getTempDeclare(unsignedType, count); //while (count--) test.setown(createValue(no_postdec, LINK(counter))); ctx.addLoop(test, NULL, false); ctx.addQuoted(s.clear().append(rowName).append(" = *").append(cursorName).append("++;")); BoundRow * cursor = translator.bindTableCursor(ctx, ds, row); return cursor; } BoundRow * InlineLinkedDatasetCursor::buildSelectNth(BuildCtx & ctx, IHqlExpression * indexExpr) { OwnedHqlExpr index = foldHqlExpression(indexExpr->queryChild(1)); bool conditional = !indexExpr->hasProperty(noBoundCheckAtom); //row = NULL StringBuffer s, rowName; OwnedHqlExpr row = createRow(ctx, "row", rowName, (conditional && CREATE_DEAULT_ROW_IF_NULL_VALUE)); //if (index > 0 && (index <= count) //MORE: Need to be very careful about the types... CHqlBoundExpr boundBase0Index; BuildCtx subctx(ctx); if (conditional) { IValue * indexValue = index->queryValue(); if (indexValue) { if (indexValue->getIntValue() <= 0) return NULL; if (indexValue->getIntValue() > (size32_t)-1) return NULL; if (indexValue->queryType()->getSize() > sizeof(size32_t)) index.setown(ensureExprType(index, sizetType)); } OwnedHqlExpr simpleIndex = translator.buildSimplifyExpr(ctx, index); OwnedHqlExpr base0Index = adjustIndexBaseToZero(simpleIndex); translator.buildExpr(ctx, base0Index, boundBase0Index); OwnedHqlExpr test; if (!indexValue) test.setown(createCompare(no_gt, simpleIndex, queryZero())); IHqlExpression * test2 = NULL; IValue * countValue = boundDs.count->queryValue(); if (countValue && indexValue) { if (indexValue->getIntValue() > countValue->getIntValue()) return NULL; } else { OwnedHqlExpr max = createTranslated(boundDs.count); test2 = createCompare(no_le, simpleIndex, max); } extendConditionOwn(test, no_and, test2); if (test) { CHqlBoundExpr boundCleared; #ifdef CREATE_DEAULT_ROW_IF_NULL translator.buildDefaultRow(ctx, ds, boundCleared); conditional = false; #else translator.buildNullRow(ctx, ds, boundCleared); #endif OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); ctx.addAssign(row, defaultRowPtr); translator.buildFilter(subctx, test); } else conditional = false; } else { OwnedHqlExpr base0 = adjustIndexBaseToZero(index); translator.buildExpr(ctx, base0, boundBase0Index); } //row = base[index] OwnedHqlExpr address = getPointer(boundDs.expr); OwnedHqlExpr indexedValue = createValue(no_index, row->getType(), LINK(address), LINK(boundBase0Index.expr)); subctx.addAssign(row, indexedValue); //MORE: Should mark as linked if it is. BoundRow * cursor = translator.bindRow(ctx, indexExpr, row); cursor->setConditional(conditional); return cursor; } //--------------------------------------------------------------------------- InlineLinkedDictionaryCursor::InlineLinkedDictionaryCursor(HqlCppTranslator & _translator, IHqlExpression * _ds, CHqlBoundExpr & _boundDs) : InlineLinkedDatasetCursor(_translator, _ds, _boundDs) { } BoundRow * InlineLinkedDictionaryCursor::buildSelectMap(BuildCtx & ctx, IHqlExpression * mapExpr) { Owned tempRow = translator.declareLinkedRow(ctx, mapExpr, false); IHqlExpression *record = ds->queryRecord(); StringBuffer lookupHelperName; OwnedHqlExpr dict = createDictionary(no_null, LINK(record)); translator.buildDictionaryHashClass(ctx, record, dict, lookupHelperName); CHqlBoundTarget target; target.expr.set(tempRow->queryBound()); HqlExprArray args; args.append(*createQuoted(lookupHelperName, makeBoolType())); args.append(*LINK(mapExpr->queryChild(0))); args.append(*LINK(mapExpr->queryChild(1))); args.append(*::createRow(no_null, LINK(record))); Owned resultType = makeReferenceModifier(makeAttributeModifier(makeRowType(record->getType()), getLinkCountedAttr())); OwnedHqlExpr call = translator.bindFunctionCall(dictionaryLookupAtom, args, resultType); translator.buildExprAssign(ctx, target, call); return tempRow.getClear(); } //--------------------------------------------------------------------------- MultiLevelDatasetCursor::MultiLevelDatasetCursor(HqlCppTranslator & _translator, IHqlExpression * _ds) : BaseDatasetCursor(_translator, _ds, NULL) { } void MultiLevelDatasetCursor::buildCount(BuildCtx & ctx, CHqlBoundExpr & tgt) { throwUnexpected(); } void MultiLevelDatasetCursor::buildExists(BuildCtx & ctx, CHqlBoundExpr & tgt) { throwUnexpected(); } BoundRow * MultiLevelDatasetCursor::buildIterateLoop(BuildCtx & ctx, bool needToBreak) { OwnedHqlExpr breakVar; if (needToBreak) { CHqlBoundTarget bound; translator.createTempFor(ctx, boolType, bound, typemod_none, FormatNatural); breakVar.set(bound.expr); ctx.addAssign(breakVar, queryBoolExpr(false)); } return doBuildIterateLoop(ctx, ds, breakVar, true); } BoundRow * MultiLevelDatasetCursor::buildSelectNth(BuildCtx & ctx, IHqlExpression * indexExpr) { //Declare row for final level, iterate the appropriate number of times, and then assign and break. BuildCtx initctx(ctx); IHqlExpression * selector = ds->queryNormalizedSelector(); StringBuffer cursorName; translator.getUniqueId(cursorName.append("row")); OwnedHqlExpr rowExpr = createVariable(cursorName, makeRowReferenceType(selector)); initctx.addDeclare(rowExpr); CHqlBoundExpr boundCleared; translator.buildDefaultRow(initctx, selector, boundCleared); OwnedHqlExpr defaultRowPtr = getPointer(boundCleared.expr); initctx.addAssign(rowExpr, defaultRowPtr); HqlExprAssociation * savedMarker = ctx.associateExpr(queryConditionalRowMarker(), rowExpr); CHqlBoundTarget boundCount; IHqlExpression * index = indexExpr->queryChild(1); bool selectFirst = matchesConstValue(index, 1); if (!selectFirst) { translator.createTempFor(initctx, index, boundCount); translator.buildExprAssign(initctx, boundCount, index); } BuildCtx subctx(ctx); buildIterateLoop(subctx, true); if (!selectFirst) { OwnedHqlExpr test = createValue(no_eq, makeBoolType(), createValue(no_predec, LINK(boundCount.expr)), getZero()); subctx.addFilter(test); } //Now we have the correct element, assign it to the pointer. //Need to be careful that the row we are pointing at is preserved, and doesn't go out of scope. (Don't need to worry about t can't be reused). BoundRow * curIter = translator.resolveSelectorDataset(subctx, selector); OwnedHqlExpr source = getPointer(curIter->queryBound()); subctx.addAssign(rowExpr, source); subctx.addBreak(); //Bind the expression as a row - so that the same select expression will get commoned up (e.g. sqagg) ctx.removeAssociation(savedMarker); return translator.bindRow(ctx, indexExpr, rowExpr); } BoundRow * MultiLevelDatasetCursor::doBuildIterateLoop(BuildCtx & ctx, IHqlExpression * expr, IHqlExpression * breakVar, bool topLevel) { IHqlExpression * root = queryRoot(expr); if (root) { if (isMultiLevelDatasetSelector(root, false)) doBuildIterateLoop(ctx, root->queryChild(0), breakVar, false); } BuildCtx oldctx(ctx); BoundRow * row; if (root) { OwnedHqlExpr thisLevel = replaceExpression(expr, root, root->queryNormalizedSelector()); row = translator.buildDatasetIterate(ctx, thisLevel, breakVar != NULL); } else { //Unusual... Something like (no_select(no_select(somethingComplex))) Assert on topLevel to prevent recursive stack fault //(see dlingle4.xhql for an example) assertex(!topLevel); root = expr->queryChild(0); row = translator.buildDatasetIterate(ctx, expr, breakVar != NULL); } if (breakVar) { if (topLevel) { ctx.addAssign(breakVar, queryBoolExpr(true)); ctx.setNextDestructor(); ctx.addAssign(breakVar, queryBoolExpr(false)); } if (isMultiLevelDatasetSelector(root, false)) { oldctx.addFilter(breakVar); oldctx.addBreak(); } } return row; } //--------------------------------------------------------------------------- BaseSetCursor::BaseSetCursor(HqlCppTranslator & _translator, IHqlExpression * _expr) : translator(_translator) { expr.set(_expr); } ListSetCursor::ListSetCursor(HqlCppTranslator & _translator, IHqlExpression * _expr) : BaseSetCursor(_translator, _expr) { } void ListSetCursor::buildCount(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.setown(getCountExpr()); } void ListSetCursor::buildExists(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.set(queryBoolExpr(expr->numChildren() != 0)); } void ListSetCursor::buildIsAll(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.set(queryBoolExpr(false)); } void ListSetCursor::buildIterateLoop(BuildCtx & ctx, CHqlBoundExpr & curBound, bool needToBreak) { if (expr->numChildren() == 0) { ctx.addFilter(queryBoolExpr(false)); curBound.expr.setown(createNullExpr(expr->queryType()->queryChildType())); return; } if (!needToBreak && (expr->numChildren() == 1)) { translator.buildExpr(ctx, expr->queryChild(0), curBound); return; } CHqlBoundExpr boundList; translator.buildSimpleExpr(ctx, expr, boundList); OwnedHqlExpr loopVar = ctx.getTempDeclare(unsignedType, NULL); OwnedHqlExpr loopTest = createValue(no_lt, makeBoolType(), LINK(loopVar), getCountExpr()); OwnedHqlExpr inc = createValue(no_postinc, loopVar->getType(), LINK(loopVar)); translator.buildAssignToTemp(ctx, loopVar, queryZero()); ctx.addLoop(loopTest, inc, false); curBound.expr.setown(createValue(no_index, LINK(expr->queryType()->queryChildType()), LINK(boundList.expr), LINK(loopVar))); } void ListSetCursor::buildIterateClass(BuildCtx & ctx, CHqlBoundExpr & tgt) { CHqlBoundExpr boundList; translator.buildSimpleExpr(ctx, expr, boundList); UNIMPLEMENTED; ctx.addQuoted("create fixed iterate (bound.length, bound.getAddress()"); } void ListSetCursor::gatherSelect(BuildCtx & ctx, IHqlExpression * indexExpr, CHqlBoundExpr & value, HqlExprAttr & cond) { if (expr->numChildren() == 0) { OwnedHqlExpr null = getOutOfRangeValue(indexExpr); translator.buildExpr(ctx, null, value); return; } IHqlExpression * index = indexExpr->queryChild(1); if (index->isConstant()) { OwnedHqlExpr folded = foldHqlExpression(index); unsigned which = (unsigned)folded->queryValue()->getIntValue()-1; if (which < expr->numChildren()) translator.buildExpr(ctx, expr->queryChild(which), value); else { OwnedHqlExpr null = getOutOfRangeValue(indexExpr); translator.buildExpr(ctx, null, value); } } else { CHqlBoundExpr boundList; translator.buildSimpleExpr(ctx, expr, boundList); CHqlBoundExpr boundIndex; ITypeInfo * elementType = expr->queryType()->queryChildType(); // not indexExpr->getType() because may now be more specific OwnedHqlExpr base0Index = adjustIndexBaseToZero(index); if (indexExpr->hasProperty(noBoundCheckAtom)) translator.buildExpr(ctx, base0Index, boundIndex); else translator.buildSimpleExpr(ctx, base0Index, boundIndex); value.expr.setown(createValue(no_index, LINK(elementType), LINK(boundList.expr), LINK(boundIndex.expr))); if (!indexExpr->hasProperty(noBoundCheckAtom)) { ITypeInfo * indexType = boundIndex.expr->queryType(); //ok to subtract early and remove a check for > 0 on unsigned values because they will wrap and fail upper limit test if (indexType->isSigned()) cond.setown(createBoolExpr(no_ge, LINK(boundIndex.expr), getZero())); if (indexType->getCardinality() > expr->numChildren()) extendConditionOwn(cond, no_and, createBoolExpr(no_lt, LINK(boundIndex.expr), getCountExpr())); } } } void ListSetCursor::buildExprSelect(BuildCtx & ctx, IHqlExpression * indexExpr, CHqlBoundExpr & tgt) { CHqlBoundExpr value; HqlExprAttr cond; gatherSelect(ctx, indexExpr, value, cond); if (cond) { translator.buildTempExpr(ctx, indexExpr, tgt); return; CHqlBoundTarget tempTarget; translator.createTempFor(ctx, indexExpr, tempTarget); buildAssignSelect(ctx, tempTarget, indexExpr); tgt.setFromTarget(tempTarget); } else tgt.set(value); } void ListSetCursor::buildAssignSelect(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * indexExpr) { CHqlBoundExpr value; HqlExprAttr cond; gatherSelect(ctx, indexExpr, value, cond); if (cond) { BuildCtx subctx(ctx); IHqlStmt * e = subctx.addFilter(cond); translator.assign(subctx, target, value); subctx.selectElse(e); OwnedHqlExpr null = getOutOfRangeValue(indexExpr); translator.buildExprAssign(subctx, target, null); } else translator.assign(ctx, target, value); } IHqlExpression * ListSetCursor::getCountExpr() { return getSizetConstant(expr->numChildren()); } //--------------------------------------------------------------------------- AllSetCursor::AllSetCursor(HqlCppTranslator & _translator) : BaseSetCursor(_translator, NULL) { } void AllSetCursor::buildCount(BuildCtx & ctx, CHqlBoundExpr & tgt) { translator.throwError(HQLERR_CountAllSet); } void AllSetCursor::buildIsAll(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.set(queryBoolExpr(true)); } void AllSetCursor::buildExists(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.set(queryBoolExpr(true)); } void AllSetCursor::buildIterateLoop(BuildCtx & ctx, CHqlBoundExpr & curBound, bool needToBreak) { translator.throwError(HQLERR_IndexAllSet); } void AllSetCursor::buildIterateClass(BuildCtx & ctx, CHqlBoundExpr & tgt) { translator.throwError(HQLERR_IndexAllSet); } void AllSetCursor::buildExprSelect(BuildCtx & ctx, IHqlExpression * indexExpr, CHqlBoundExpr & tgt) { translator.throwError(HQLERR_IndexAllSet); } void AllSetCursor::buildAssignSelect(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * indexExpr) { translator.throwError(HQLERR_IndexAllSet); } //--------------------------------------------------------------------------- GeneralSetCursor::GeneralSetCursor(HqlCppTranslator & _translator, IHqlExpression * _expr, CHqlBoundExpr & boundSet) : BaseSetCursor(_translator, _expr) { isAll.setown(boundSet.getIsAll()); ITypeInfo * elementType = LINK(expr->queryType()->queryChildType()); if (!elementType) elementType = makeStringType(UNKNOWN_LENGTH, NULL, NULL); element.setown(createField(valueAtom, elementType, NULL)); HqlExprArray fields; fields.append(*LINK(element)); ds.setown(createDataset(no_anon, createRecord(fields), LINK(expr))); dsCursor.setown(new InlineBlockDatasetCursor(translator, ds, boundSet)); } void GeneralSetCursor::buildCount(BuildCtx & ctx, CHqlBoundExpr & tgt) { checkNotAll(ctx); dsCursor->buildCount(ctx, tgt); } void GeneralSetCursor::buildExists(BuildCtx & ctx, CHqlBoundExpr & tgt) { if (isAll->queryValue()) { if (isAll->queryValue()->getBoolValue()) { tgt.expr.set(queryBoolExpr(true)); return; } dsCursor->buildExists(ctx, tgt); } else { dsCursor->buildExists(ctx, tgt); tgt.expr.setown(createBoolExpr(no_or, LINK(isAll), LINK(tgt.expr))); } } void GeneralSetCursor::buildIsAll(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.set(isAll); } void GeneralSetCursor::buildIterateLoop(BuildCtx & ctx, CHqlBoundExpr & curBound, bool needToBreak) { BoundRow * cursor = dsCursor->buildIterateLoop(ctx, needToBreak); OwnedHqlExpr select = createSelectExpr(LINK(cursor->querySelector()), LINK(element)); translator.buildExpr(ctx, select, curBound); } void GeneralSetCursor::buildIterateClass(BuildCtx & ctx, CHqlBoundExpr & tgt) { UNIMPLEMENTED; HqlExprAttr row; dsCursor->buildIterateClass(ctx, tgt.expr, row); } IHqlExpression * GeneralSetCursor::createDatasetSelect(IHqlExpression * indexExpr) { HqlExprArray args; args.append(*LINK(ds)); unwindChildren(args, indexExpr, 1); return createRow(no_selectnth, args); } void GeneralSetCursor::buildExprSelect(BuildCtx & ctx, IHqlExpression * indexExpr, CHqlBoundExpr & tgt) { if (indexExpr->hasProperty(noBoundCheckAtom)) { if (indexExpr->hasProperty(forceAllCheckAtom)) checkNotAll(ctx); OwnedHqlExpr dsIndexExpr = createDatasetSelect(indexExpr); BoundRow * cursor = dsCursor->buildSelectNth(ctx, dsIndexExpr); OwnedHqlExpr select = createSelectExpr(LINK(dsIndexExpr), LINK(element)); translator.buildExpr(ctx, select, tgt); } else { translator.buildTempExpr(ctx, indexExpr, tgt); } } void GeneralSetCursor::buildAssignSelect(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * indexExpr) { if (!indexExpr->hasProperty(noBoundCheckAtom) || indexExpr->hasProperty(forceAllCheckAtom)) checkNotAll(ctx); OwnedHqlExpr dsIndexExpr = createDatasetSelect(indexExpr); BoundRow * cursor = dsCursor->buildSelectNth(ctx, dsIndexExpr); if (cursor) { OwnedHqlExpr select = createSelectExpr(LINK(dsIndexExpr), LINK(element)); if (!cursor->isConditional()) translator.buildExprAssign(ctx, target, select); else { // if (row) tgt = x else tgt = dft; BuildCtx subctx(ctx); IHqlStmt * e = subctx.addFilter(cursor->queryBound()); cursor->setConditional(false); // yuk! translator.buildExprAssign(subctx, target, select); cursor->setConditional(true); subctx.selectElse(e); OwnedHqlExpr null = getOutOfRangeValue(indexExpr); translator.buildExprAssign(subctx, target, null); } } else { OwnedHqlExpr null = getOutOfRangeValue(indexExpr); translator.buildExprAssign(ctx, target, null); } } void GeneralSetCursor::checkNotAll(BuildCtx & ctx) { if (isAll->queryValue()) { if (isAll->queryValue()->getBoolValue()) translator.throwError(HQLERR_IndexAllSet); } else { //MORE: Should only really do this once... BuildCtx subctx(ctx); subctx.addFilter(isAll); IHqlExpression * msg = translator.createFailMessage("Cannot index ALL", NULL, NULL, translator.queryCurrentActivityId(ctx)); OwnedHqlExpr fail = createValue(no_fail, makeVoidType(), getZero(), msg, getDefaultAttr()); translator.buildStmt(subctx, fail); } } bool GeneralSetCursor::isSingleValued() { if (!matchesBoolean(isAll, false)) return false; // return dsCursor->hasSingleRow(); return false; } //--------------------------------------------------------------------------- CreateSetCursor::CreateSetCursor(HqlCppTranslator & _translator, IHqlExpression * _expr, IHqlCppDatasetCursor * _dsCursor) : BaseSetCursor(_translator, _expr) { ds.set(expr->queryChild(0)); value.set(expr->queryChild(1)); dsCursor.set(_dsCursor); } void CreateSetCursor::buildCount(BuildCtx & ctx, CHqlBoundExpr & tgt) { dsCursor->buildCount(ctx, tgt); } void CreateSetCursor::buildExists(BuildCtx & ctx, CHqlBoundExpr & tgt) { dsCursor->buildExists(ctx, tgt); } void CreateSetCursor::buildIsAll(BuildCtx & ctx, CHqlBoundExpr & tgt) { tgt.expr.set(queryBoolExpr(false)); } void CreateSetCursor::buildIterateLoop(BuildCtx & ctx, CHqlBoundExpr & curBound, bool needToBreak) { BoundRow * cursor = dsCursor->buildIterateLoop(ctx, needToBreak); translator.buildExpr(ctx, value, curBound); } void CreateSetCursor::buildIterateClass(BuildCtx & ctx, CHqlBoundExpr & tgt) { UNIMPLEMENTED; } IHqlExpression * CreateSetCursor::createDatasetSelect(IHqlExpression * indexExpr) { if (value->getOperator() == no_select && (value->queryChild(0)->queryNormalizedSelector() == ds->queryNormalizedSelector())) { HqlExprArray args; args.append(*LINK(ds)); unwindChildren(args, indexExpr, 1); IHqlExpression * select = createRow(no_selectnth, args); return createNewSelectExpr(select, LINK(value->queryChild(1))); } else { OwnedHqlExpr field = createField(createIdentifierAtom("__f1__"), value->getType(), NULL); IHqlExpression * aggregateRecord = createRecord(field); IHqlExpression * assign = createAssign(createSelectExpr(getSelf(aggregateRecord), LINK(field)), LINK(value)); IHqlExpression * transform = createValue(no_newtransform, makeTransformType(aggregateRecord->getType()), assign); HqlExprArray args; args.append(*createDataset(no_newusertable, LINK(ds), createComma(aggregateRecord, transform))); unwindChildren(args, indexExpr, 1); IHqlExpression * select = createRow(no_selectnth, args); return createNewSelectExpr(select, LINK(field)); } } void CreateSetCursor::buildExprSelect(BuildCtx & ctx, IHqlExpression * indexExpr, CHqlBoundExpr & tgt) { OwnedHqlExpr newExpr = createDatasetSelect(indexExpr); translator.buildExpr(ctx, newExpr, tgt); } void CreateSetCursor::buildAssignSelect(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * indexExpr) { OwnedHqlExpr newExpr = createDatasetSelect(indexExpr); translator.buildExprAssign(ctx, target, newExpr); } bool CreateSetCursor::isSingleValued() { return hasSingleRow(ds); } //--------------------------------------------------------------------------- IHqlCppSetCursor * HqlCppTranslator::createSetSelector(BuildCtx & ctx, IHqlExpression * expr) { OwnedHqlExpr normalized = normalizeListCasts(expr); switch (normalized->getOperator()) { case no_alias_scope: { unsigned max = normalized->numChildren(); for (unsigned idx = 1; idx < max; idx++) expandAliases(ctx, normalized->queryChild(idx)); return createSetSelector(ctx, normalized->queryChild(0)); } case no_null: return new ListSetCursor(*this, normalized); case no_all: return new AllSetCursor(*this); case no_list: if ((normalized->numChildren() == 0) || (normalized->queryType()->queryChildType()->getSize() != UNKNOWN_LENGTH)) return new ListSetCursor(*this, normalized); break; // default case no_createset: { Owned dsCursor = createDatasetSelector(ctx, expr->queryChild(0)); return new CreateSetCursor(*this, expr, dsCursor); } } CHqlBoundExpr bound; buildSimpleExpr(ctx, normalized, bound); return new GeneralSetCursor(*this, normalized, bound); } //--------------------------------------------------------------------------- IHqlCppDatasetCursor * HqlCppTranslator::createDatasetSelector(BuildCtx & ctx, IHqlExpression * expr) { // OwnedHqlExpr normalized = normalizeDatasetCasts(expr); switch (expr->getOperator()) { case no_null: break; case no_select: if (isMultiLevelDatasetSelector(expr, false)) return new MultiLevelDatasetCursor(*this, expr); break; } CHqlBoundExpr bound; buildDataset(ctx, expr, bound, FormatNatural); if (bound.expr->isDatarow() || !isArrayRowset(bound.expr->queryType())) return new InlineBlockDatasetCursor(*this, expr, bound); else if (bound.expr->isDictionary()) return new InlineLinkedDictionaryCursor(*this, expr, bound); else return new InlineLinkedDatasetCursor(*this, expr, bound); } //--------------------------------------------------------------------------- CHqlCppDatasetBuilder::CHqlCppDatasetBuilder(HqlCppTranslator & _translator, IHqlExpression * _record) : translator(_translator), record(_record) { } DatasetBuilderBase::DatasetBuilderBase(HqlCppTranslator & _translator, IHqlExpression * _record, bool _buildLinkedRows) : CHqlCppDatasetBuilder(_translator, _record) { StringBuffer rowName; unique_id_t id = translator.getUniqueId(); appendUniqueId(instanceName.append("cr"), id); builderName.append(instanceName).append(".rowBuilder()"); rowName.append(instanceName).append(".rowBuilder().row()"); // more! IHqlExpression * linkAttr = _buildLinkedRows ? getLinkCountedAttr() : NULL; ITypeInfo * rowType = makeRowReferenceType(record); if (_buildLinkedRows) rowType = makeAttributeModifier(rowType, getLinkCountedAttr()); OwnedHqlExpr cursorVar = createVariable(rowName.str(), rowType); dataset.setown(createDataset(no_anon, LINK(record), createComma(getSelfAttr(), linkAttr))); } BoundRow * DatasetBuilderBase::buildCreateRow(BuildCtx & ctx) { StringBuffer s; OwnedHqlExpr cond = createQuoted(s.append(instanceName).append(".createRow()"), makeBoolType()); if (isRestricted()) ctx.addFilter(cond); else ctx.addExpr(cond); return translator.bindSelf(ctx, dataset, builderName); } BoundRow * DatasetBuilderBase::buildDeserializeRow(BuildCtx & ctx, IHqlExpression * serializedInput) { StringBuffer serializerInstanceName; translator.ensureRowSerializer(serializerInstanceName, ctx, record, deserializerAtom); StringBuffer s; s.append(instanceName).append(".deserializeRow(*"); s.append(serializerInstanceName).append(", "); translator.generateExprCpp(s, serializedInput).append(");"); ctx.addQuoted(s); return translator.bindSelf(ctx, dataset, builderName); } void DatasetBuilderBase::finishRow(BuildCtx & ctx, BoundRow * selfCursor) { OwnedHqlExpr size = createSizeof(selfCursor->querySelector()); doFinishRow(ctx, selfCursor, size); } void DatasetBuilderBase::doFinishRow(BuildCtx & ctx, BoundRow * selfCursor, IHqlExpression *size) { CHqlBoundExpr boundSize; translator.buildExpr(ctx, size, boundSize); StringBuffer s; s.append(instanceName).append(".finalizeRow("); translator.generateExprCpp(s, boundSize.expr).append(");"); ctx.addQuoted(s); ctx.removeAssociation(selfCursor); } //--------------------------------------------------------------------------- BlockedDatasetBuilder::BlockedDatasetBuilder(HqlCppTranslator & _translator, IHqlExpression * _record) : DatasetBuilderBase(_translator, _record, false) { forceLength = false; } void BlockedDatasetBuilder::buildDeclare(BuildCtx & ctx) { StringBuffer decl, extra; if (count) { CHqlBoundExpr boundCount; translator.buildExpr(ctx, count, boundCount); if (translator.isFixedRecordSize(record)) { //RtlFixedDatasetCreator cursor(len, data, size) decl.append("RtlLimitedFixedDatasetBuilder"); extra.append(translator.getFixedRecordSize(record)); } else { //RtlVariableDatasetCursor cursor(len, data, recordSize) decl.append("RtlLimitedVariableDatasetBuilder"); translator.buildMetaForRecord(extra, record); } translator.ensureContextAvailable(ctx); decl.append(" ").append(instanceName).append("(").append(extra).append(","); translator.generateExprCpp(decl, boundCount.expr).append(","); if (forceLength) { OwnedHqlExpr clearFunc = translator.getClearRecordFunction(record); translator.generateExprCpp(decl, clearFunc).append(", ctx);"); } else decl.append("NULL,NULL);"); } else { if (translator.isFixedRecordSize(record)) { //RtlFixedDatasetCreator cursor(len, data, size) decl.append("RtlFixedDatasetBuilder"); extra.append(translator.getFixedRecordSize(record)).append(", 0"); } else { //RtlVariableDatasetCursor cursor(len, data, recordSize) decl.append("RtlVariableDatasetBuilder"); translator.buildMetaForRecord(extra, record); } decl.append(" ").append(instanceName).append("(").append(extra).append(");"); } ctx.addQuoted(decl); } void BlockedDatasetBuilder::buildFinish(BuildCtx & ctx, const CHqlBoundTarget & target) { //more: should I do this by really calling a function? StringBuffer s; s.append(instanceName).append(".getData("); translator.generateExprCpp(s, target.length); s.append(","); OwnedHqlExpr ref = createValue(no_reference, target.getType(), LINK(target.expr)); translator.generateExprCpp(s, ref); s.append(");"); ctx.addQuoted(s); } void BlockedDatasetBuilder::buildFinish(BuildCtx & ctx, CHqlBoundExpr & bound) { StringBuffer s; s.clear().append(instanceName).append(".getSize()"); bound.length.setown(createQuoted(s.str(), LINK(unsignedType))); s.clear().append(instanceName).append(".queryData()"); bound.expr.setown(createQuoted(s.str(), makeReferenceModifier(dataset->getType()))); } //--------------------------------------------------------------------------- SingleRowTempDatasetBuilder::SingleRowTempDatasetBuilder(HqlCppTranslator & _translator, IHqlExpression * _record, BoundRow * _row) : CHqlCppDatasetBuilder(_translator, _record) { row.set(_row); cursor.set(row); } void SingleRowTempDatasetBuilder::buildDeclare(BuildCtx & ctx) { } BoundRow * SingleRowTempDatasetBuilder::buildCreateRow(BuildCtx & ctx) { cursor.set(row); return row; } void SingleRowTempDatasetBuilder::buildFinish(BuildCtx & ctx, const CHqlBoundTarget & target) { assertex(cursor != NULL); } void SingleRowTempDatasetBuilder::buildFinish(BuildCtx & ctx, CHqlBoundExpr & target) { assertex(cursor != NULL); } void SingleRowTempDatasetBuilder::finishRow(BuildCtx & ctx, BoundRow * selfCursor) { } //--------------------------------------------------------------------------- InlineDatasetBuilder::InlineDatasetBuilder(HqlCppTranslator & _translator, IHqlExpression * _record, IHqlExpression * _size, IHqlExpression * _address) : CHqlCppDatasetBuilder(_translator, _record) { StringBuffer cursorName; getUniqueId(cursorName.append("p")); ITypeInfo * rowType = makeRowReferenceType(record); cursorVar.setown(createVariable(cursorName.str(), rowType)); dataset.setown(createDataset(no_anon, LINK(record), getSelfAttr())); size.set(_size); address.set(_address); } void InlineDatasetBuilder::buildDeclare(BuildCtx & ctx) { //NB: This is only ever used where the target has already been checked to ensure there is enough room //If we wanted to be clever we would need to use a RtlNestedRowBuilder(parent, , ...); ctx.addDeclare(cursorVar, address); } BoundRow * InlineDatasetBuilder::buildCreateRow(BuildCtx & ctx) { Owned cursor = translator.createTableCursor(dataset, cursorVar, no_self, NULL); ctx.associate(*cursor); return cursor; } void InlineDatasetBuilder::buildFinish(BuildCtx & ctx, const CHqlBoundTarget & target) { ctx.addAssign(target.length, size); } void InlineDatasetBuilder::buildFinish(BuildCtx & ctx, CHqlBoundExpr & bound) { bound.length.set(size); bound.expr.set(address); } void InlineDatasetBuilder::finishRow(BuildCtx & ctx, BoundRow * selfCursor) { CHqlBoundExpr bound; translator.getRecordSize(ctx, selfCursor->querySelector(), bound); if (translator.queryOptions().optimizeIncrement) { ctx.addAssignIncrement(selfCursor->queryBound(), bound.expr); } else { OwnedHqlExpr inc = createValue(no_add, LINK(selfCursor->queryBound()), LINK(bound.expr)); ctx.addAssign(selfCursor->queryBound(), inc); } } //--------------------------------------------------------------------------- LinkedDatasetBuilderBase::LinkedDatasetBuilderBase(HqlCppTranslator & _translator, IHqlExpression * _record) : DatasetBuilderBase(_translator, _record, true) { } void LinkedDatasetBuilderBase::finishRow(BuildCtx & ctx, BoundRow * selfCursor) { OwnedHqlExpr size = translator.getRecordSize(selfCursor->querySelector()); doFinishRow(ctx, selfCursor, size); } void LinkedDatasetBuilderBase::buildFinish(BuildCtx & ctx, const CHqlBoundTarget & target) { //more: should I do this by really calling a function? StringBuffer s; s.append(instanceName).append(".getcount()"); if (hasWrapperModifier(target.queryType())) { translator.generateExprCpp(s.clear(), target.expr); s.append(".setown(").append(instanceName).append(".getcount()"); s.append(",").append(instanceName).append(".linkrows());"); ctx.addQuoted(s); } else { OwnedHqlExpr countExpr = createQuoted(s.str(), LINK(unsignedType)); ctx.addAssign(target.count, countExpr); s.clear().append(instanceName).append(".linkrows()"); OwnedHqlExpr rowsExpr = createQuoted(s.str(), dataset->getType()); ctx.addAssign(target.expr, rowsExpr); } } void LinkedDatasetBuilderBase::buildFinish(BuildCtx & ctx, CHqlBoundExpr & bound) { StringBuffer s; s.clear().append(instanceName).append(".getcount()"); bound.count.setown(createQuoted(s.str(), LINK(unsignedType))); s.clear().append(instanceName).append(".queryrows()"); bound.expr.setown(createQuoted(s.str(), makeReferenceModifier(dataset->getType()))); } bool LinkedDatasetBuilderBase::buildLinkRow(BuildCtx & ctx, BoundRow * sourceRow) { IHqlExpression * sourceRecord = sourceRow->queryRecord(); if (recordTypesMatch(sourceRecord, record) && sourceRow->isBinary()) { OwnedHqlExpr source = getPointer(sourceRow->queryBound()); BuildCtx subctx(ctx); if (sourceRow->isConditional()) subctx.addFilter(source); if (sourceRow->isLinkCounted()) { StringBuffer s; s.append(instanceName).append(".append("); translator.generateExprCpp(s, source); s.append(");"); subctx.addQuoted(s); return true; } IHqlExpression * sourceExpr = sourceRow->querySelector(); OwnedHqlExpr rowExpr = sourceExpr->isDataset() ? ensureActiveRow(sourceExpr) : LINK(sourceExpr); OwnedHqlExpr size = createSizeof(rowExpr); CHqlBoundExpr boundSize; translator.buildExpr(ctx, size, boundSize); StringBuffer s; s.append(instanceName).append(".cloneRow("); translator.generateExprCpp(s, boundSize.expr).append(","); translator.generateExprCpp(s, source); s.append(");"); subctx.addQuoted(s); return true; } return false; } bool LinkedDatasetBuilderBase::buildAppendRows(BuildCtx & ctx, IHqlExpression * expr) { IHqlExpression * sourceRecord = expr->queryRecord(); if (recordTypesMatch(sourceRecord, record)) { CHqlBoundExpr bound; if (!ctx.getMatchExpr(expr, bound)) { bool tryToOptimize = false; switch (expr->getOperator()) { case no_select: if (isMultiLevelDatasetSelector(expr, false)) break; if (!hasLinkedRow(expr->queryType())) break; tryToOptimize = true; break; default: //Don't speculatively evaluate if the expression isn't pure tryToOptimize = alwaysEvaluatesToBound(expr) && expr->isPure(); break; } if (tryToOptimize) translator.buildDataset(ctx, expr, bound, FormatNatural); } if (bound.expr) { if (hasLinkedRow(bound.queryType())) { OwnedHqlExpr source = getPointer(bound.expr); StringBuffer s; s.append(instanceName).append(".appendRows("); translator.generateExprCpp(s, bound.count); s.append(","); translator.generateExprCpp(s, source); s.append(");"); ctx.addQuoted(s); return true; } } } return false; } LinkedDatasetBuilder::LinkedDatasetBuilder(HqlCppTranslator & _translator, IHqlExpression * _record, IHqlExpression * _choosenLimit) : LinkedDatasetBuilderBase(_translator, _record) { choosenLimit.set(_choosenLimit); } void LinkedDatasetBuilder::buildDeclare(BuildCtx & ctx) { StringBuffer decl, allocatorName; OwnedHqlExpr curActivityId = translator.getCurrentActivityId(ctx); translator.ensureRowAllocator(allocatorName, ctx, record, curActivityId); decl.append("RtlLinkedDatasetBuilder ").append(instanceName).append("("); decl.append(allocatorName); if (choosenLimit) { CHqlBoundExpr boundLimit; translator.buildExpr(ctx, choosenLimit, boundLimit); translator.generateExprCpp(decl.append(", "), boundLimit.expr); } decl.append(");"); ctx.addQuoted(decl); } LinkedDictionaryBuilder::LinkedDictionaryBuilder(HqlCppTranslator & _translator, IHqlExpression * _record) : LinkedDatasetBuilderBase(_translator, _record) { } void LinkedDictionaryBuilder::buildDeclare(BuildCtx & ctx) { StringBuffer decl, allocatorName; OwnedHqlExpr curActivityId = translator.getCurrentActivityId(ctx); translator.ensureRowAllocator(allocatorName, ctx, record, curActivityId); StringBuffer lookupHelperName; OwnedHqlExpr dict = createDictionary(no_null, record.getLink()); // MORE - is the actual dict not available? translator.buildDictionaryHashClass(ctx, record, dict, lookupHelperName); decl.append("RtlLinkedDictionaryBuilder ").append(instanceName).append("("); decl.append(allocatorName).append(", &").append(lookupHelperName); decl.append(");"); ctx.addQuoted(decl); } //--------------------------------------------------------------------------- SetBuilder::SetBuilder(HqlCppTranslator & _translator, ITypeInfo * fieldType, IHqlExpression * _allVar) : translator(_translator) { HqlExprArray fields; fields.append(*createField(valueAtom, LINK(fieldType), NULL)); record.setown(createRecord(fields)); allVar.set(_allVar); activeRow = NULL; } void SetBuilder::buildDeclare(BuildCtx & ctx) { datasetBuilder->buildDeclare(ctx); } IReferenceSelector * SetBuilder::buildCreateElement(BuildCtx & ctx) { activeRow = datasetBuilder->buildCreateRow(ctx); OwnedHqlExpr select = createSelectExpr(LINK(activeRow->querySelector()), LINK(record->queryChild(0))); return translator.buildReference(ctx, select); } void SetBuilder::buildFinish(BuildCtx & ctx, const CHqlBoundTarget & target) { if (target.isAll && (allVar != target.isAll)) { assertex(allVar != NULL); ctx.addAssign(target.isAll, allVar); } datasetBuilder->buildFinish(ctx, target); } void SetBuilder::finishElement(BuildCtx & ctx) { datasetBuilder->finishRow(ctx, activeRow); activeRow = NULL; } void SetBuilder::setAll(BuildCtx & ctx, IHqlExpression * isAll) { if (allVar) { CHqlBoundExpr bound; translator.buildExpr(ctx, isAll, bound); ctx.addAssign(allVar, bound.expr); } else { if (!matchesBoolean(isAll, false)) throwUnexpected(); } } TempSetBuilder::TempSetBuilder(HqlCppTranslator & _translator, ITypeInfo * fieldType, IHqlExpression * _allVar) : SetBuilder(_translator, fieldType, _allVar) { datasetBuilder.setown(new BlockedDatasetBuilder(translator, record)); } InlineSetBuilder::InlineSetBuilder(HqlCppTranslator & _translator, ITypeInfo * fieldType, IHqlExpression * _allVar, IHqlExpression * _size, IHqlExpression * _address) : SetBuilder(_translator, fieldType, _allVar) { datasetBuilder.setown(new InlineDatasetBuilder(translator, record, _size, _address)); } IHqlCppSetBuilder * HqlCppTranslator::createTempSetBuilder(ITypeInfo * type, IHqlExpression * allVar) { return new TempSetBuilder(*this, type, allVar); } IHqlCppSetBuilder * HqlCppTranslator::createInlineSetBuilder(ITypeInfo * type, IHqlExpression * allVar, IHqlExpression * size, IHqlExpression * address) { assertex(allVar); return new InlineSetBuilder(*this, type, allVar, size, address); } IHqlCppDatasetBuilder * HqlCppTranslator::createBlockedDatasetBuilder(IHqlExpression * record) { return new BlockedDatasetBuilder(*this, record); } IHqlCppDatasetBuilder * HqlCppTranslator::createLinkedDatasetBuilder(IHqlExpression * record, IHqlExpression * choosenLimit) { return new LinkedDatasetBuilder(*this, record, choosenLimit); } IHqlCppDatasetBuilder * HqlCppTranslator::createLinkedDictionaryBuilder(IHqlExpression * record) { return new LinkedDictionaryBuilder(*this, record); } IHqlCppDatasetBuilder * HqlCppTranslator::createSingleRowTempDatasetBuilder(IHqlExpression * record, BoundRow * row) { // if (translator.isFixedRecordSize(record)) return new SingleRowTempDatasetBuilder(*this, record, row); return createBlockedDatasetBuilder(record); } IHqlCppDatasetBuilder * HqlCppTranslator::createInlineDatasetBuilder(IHqlExpression * record, IHqlExpression * size, IHqlExpression * address) { assertex(isFixedRecordSize(record)); return new InlineDatasetBuilder(*this, record, size, address); } IHqlCppDatasetBuilder * HqlCppTranslator::createChoosenDatasetBuilder(IHqlExpression * record, IHqlExpression * maxCount) { BlockedDatasetBuilder * builder = new BlockedDatasetBuilder(*this, record); builder->setLimit(maxCount, false); return builder; } IHqlCppDatasetBuilder * HqlCppTranslator::createLimitedDatasetBuilder(IHqlExpression * record, IHqlExpression * maxCount) { BlockedDatasetBuilder * builder = new BlockedDatasetBuilder(*this, record); builder->setLimit(maxCount, true); return builder; } //--------------------------------------------------------------------------- void HqlCppTranslator::doBuildSetAssignAndCast(BuildCtx & ctx, IHqlCppSetBuilder * builder, IHqlExpression * value) { Owned cursor = createSetSelector(ctx, value); CHqlBoundExpr srcIsAll; cursor->buildIsAll(ctx, srcIsAll); OwnedHqlExpr translated = srcIsAll.getTranslatedExpr(); builder->setAll(ctx, translated); BuildCtx loopctx(ctx); CHqlBoundExpr boundCurElement; cursor->buildIterateLoop(loopctx, boundCurElement, false); Owned selector = builder->buildCreateElement(loopctx); OwnedHqlExpr translatedCurElement = boundCurElement.getTranslatedExpr(); selector->set(loopctx, translatedCurElement); builder->finishElement(loopctx); } void HqlCppTranslator::buildSetAssign(BuildCtx & ctx, IHqlCppSetBuilder * builder, IHqlExpression * expr) { switch (expr->getOperator()) { case no_cast: doBuildSetAssignAndCast(ctx, builder, expr->queryChild(0)); break; case no_addsets: doBuildSetAssignAndCast(ctx, builder, expr); break; //MORE: This is wrong because needs to cope with all. buildSetAssign(ctx, builder, expr->queryChild(0)); buildSetAssign(ctx, builder, expr->queryChild(1)); break; case no_all: builder->setAll(ctx, queryBoolExpr(true)); break; case no_list: { unsigned max = expr->numChildren(); if ((max < 3) || isComplexSet(expr) || !isConstantSet(expr)) { for (unsigned i=0; i < max; i++) { //Need a subcontext otherwise sizeof(target-row) gets cached. BuildCtx subctx(ctx); subctx.addGroup(); Owned selector = builder->buildCreateElement(subctx); selector->set(subctx, expr->queryChild(i)); builder->finishElement(subctx); } builder->setAll(ctx, queryBoolExpr(false)); } else doBuildSetAssignAndCast(ctx, builder, expr); } break; case no_createset: { IHqlExpression * ds = expr->queryChild(0); IHqlExpression * value = expr->queryChild(1); builder->setAll(ctx, queryBoolExpr(false)); BuildCtx subctx(ctx); BoundRow * cursor = buildDatasetIterate(subctx, ds, false); Owned selector = builder->buildCreateElement(subctx); selector->set(subctx, value); builder->finishElement(subctx); break; } default: doBuildSetAssignAndCast(ctx, builder, expr); break; } } void HqlCppTranslator::buildSetAssignViaBuilder(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * value) { ITypeInfo * to = target.queryType(); Owned builder = createTempSetBuilder(to->queryChildType(), target.isAll); builder->buildDeclare(ctx); buildSetAssign(ctx, builder, value); builder->buildFinish(ctx, target); } void HqlCppTranslator::doBuildAssignAddSets(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * value) { IHqlExpression * left = value->queryChild(0); IHqlExpression * right = value->queryChild(1); assertex(left->queryType() == right->queryType()); //a poor implementation, but at least it works. HqlExprArray args; args.append(*LINK(left)); args.append(*LINK(right)); OwnedHqlExpr call = bindFunctionCall(appendSetXAtom, args, left->queryType()); buildExprAssign(ctx, target, call); }