Browse Source

Merge pull request #10686 from ghalliday/issue18794

HPCC-18794 Allow the code generator to generate IValueSet/IFieldFilter

Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 7 years ago
parent
commit
2419c6dde5

+ 6 - 5
common/thorhelper/layouttrans.ipp

@@ -99,11 +99,12 @@ class ExpandedSegmentMonitorList : public IRecordLayoutTranslator::SegmentMonito
 public:
     ExpandedSegmentMonitorList(CRecordLayoutTranslator * _owner) : owner(_owner) {}
     IMPLEMENT_IINTERFACE;
-    virtual void append(IKeySegmentMonitor * monitor);
-    virtual bool isLastSegmentWild() const { return false; }
-    virtual unsigned ordinality() const { return monitors.ordinality(); }
-    virtual IKeySegmentMonitor * item(unsigned i) const { return &monitors.item(i); }
-    virtual void reset() { monitors.kill(); }
+    virtual void append(IKeySegmentMonitor * monitor) override;
+    virtual unsigned ordinality() const override { return monitors.ordinality(); }
+    virtual IKeySegmentMonitor * item(unsigned i) const override { return &monitors.item(i); }
+    virtual void reset() override { monitors.kill(); }
+    virtual void append(FFoption option, IFieldFilter * filter) override { UNIMPLEMENTED; }
+
 private:
     CRecordLayoutTranslator * owner;
     IArrayOf<IKeySegmentMonitor> monitors;

+ 4 - 0
ecl/hqlcpp/hqlcatom.cpp

@@ -103,6 +103,7 @@ IIdAtom * acosId;
 IIdAtom * addAggregateRowId;
 IIdAtom * addAllId;
 IIdAtom * addRangeId;
+IIdAtom * addRawRangeId;
 IIdAtom * addWorkunitAssertFailureId;
 IIdAtom * an2bId;
 IIdAtom * an2fId;
@@ -420,6 +421,7 @@ IIdAtom * isResultId;
 IIdAtom * keyUnicodeXId;
 IIdAtom * keyUnicodeStrengthXId;
 IIdAtom * killRangeId;
+IIdAtom * killRawRangeId;
 IIdAtom * l42anId;
 IIdAtom * l42axId;
 IIdAtom * l42vnId;
@@ -754,6 +756,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEID(addAggregateRow);
     MAKEID(addAll);
     MAKEID(addRange);
+    MAKEID(addRawRange);
     MAKEID(addWorkunitAssertFailure);
     MAKEID(an2b);
     MAKEID(an2f);
@@ -1076,6 +1079,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEID(keyUnicodeX);
     MAKEID(keyUnicodeStrengthX);
     MAKEID(killRange);
+    MAKEID(killRawRange);
     MAKEID(l42an);
     MAKEID(l42ax);
     MAKEID(l42vn);

+ 2 - 0
ecl/hqlcpp/hqlcatom.hpp

@@ -101,6 +101,7 @@ extern IIdAtom * acosId;
 extern IIdAtom * addAggregateRowId;
 extern IIdAtom * addAllId;
 extern IIdAtom * addRangeId;
+extern IIdAtom * addRawRangeId;
 extern IIdAtom * addWorkunitAssertFailureId;
 extern IIdAtom * an2bId;
 extern IIdAtom * an2fId;
@@ -418,6 +419,7 @@ extern IIdAtom * isResultId;
 extern IIdAtom * keyUnicodeXId;
 extern IIdAtom * keyUnicodeStrengthXId;
 extern IIdAtom * killRangeId;
+extern IIdAtom * killRawRangeId;
 extern IIdAtom * l42anId;
 extern IIdAtom * l42axId;
 extern IIdAtom * l42vnId;

+ 1 - 0
ecl/hqlcpp/hqlcpp.cpp

@@ -1807,6 +1807,7 @@ void HqlCppTranslator::cacheOptions()
         DebugOption(options.timeTransforms,"timeTransforms", false),
         DebugOption(options.reportDFSinfo,"reportDFSinfo", 0),
         DebugOption(options.useGlobalCompareClass,"useGlobalCompareClass", false),
+        DebugOption(options.createValueSets,"createValueSets", false),
     };
 
     //get options values from workunit

+ 2 - 0
ecl/hqlcpp/hqlcpp.ipp

@@ -791,6 +791,7 @@ struct HqlCppOptions
     bool                translateDFSlayouts;
     bool                timeTransforms;
     bool                useGlobalCompareClass;
+    bool                createValueSets;
 };
 
 //Any information gathered while processing the query should be moved into here, rather than cluttering up the translator class
@@ -1058,6 +1059,7 @@ public:
 
     IHqlExpression * getRtlFieldKey(IHqlExpression * expr, IHqlExpression * ownerRecord, bool &isPayload);
     unsigned buildRtlField(StringBuffer & instanceName, IHqlExpression * field, IHqlExpression * rowRecord);
+    unsigned buildRtlFieldType(StringBuffer & instanceName, IHqlExpression * field, IHqlExpression * rowRecord);
     unsigned buildRtlType(StringBuffer & instanceName, ITypeInfo * type);
     unsigned buildRtlRecordFields(StringBuffer & instanceName, IHqlExpression * record, IHqlExpression * rowRecord);
     unsigned expandRtlRecordFields(StringBuffer & fieldListText, IHqlExpression * record, IHqlExpression * rowRecord);

+ 2 - 0
ecl/hqlcpp/hqlcppsys.ecl

@@ -722,6 +722,8 @@ const char * cppSystemText[]  = {
     "   addAll() : method,include='rtlkey.hpp',entrypoint='addAll';",
     "   addRange(const data1 lo, const data1 hi) : method,include='rtlkey.hpp',entrypoint='addRange';",
     "   killRange(const data1 lo, const data1 hi) : method,include='rtlkey.hpp',entrypoint='killRange';",
+    "   addRawRange(const data1 lo, const data1 hi) : method,entrypoint='addRawRange';",
+    "   killRawRange(const data1 lo, const data1 hi) : method,entrypoint='killRawRange';",
 
     "   boolean columnGetBool(const varstring name) : method,pure,entrypoint='getBool';",
     "   columnGetData(noconst data result, const varstring name) : method,pure,entrypoint='getData';",

+ 34 - 0
ecl/hqlcpp/hqlhtcpp.cpp

@@ -3771,6 +3771,32 @@ unsigned HqlCppTranslator::buildRtlField(StringBuffer & instanceName, IHqlExpres
 }
 
 
+unsigned HqlCppTranslator::buildRtlFieldType(StringBuffer & instanceName, IHqlExpression * field, IHqlExpression * rowRecord)
+{
+    Linked<ITypeInfo> fieldType = field->queryType();
+    switch (field->queryType()->getTypeCode())
+    {
+    case type_alien:
+        //MORE:::
+        break;
+    case type_row:
+        //Backward compatibility - should revisit
+        fieldType.set(fieldType->queryChildType());
+        break;
+    case type_bitfield:
+    {
+        //fieldKey contains a field with a type annotated with offsets/isLastBitfield
+        bool isPayload = field->hasAttribute(_payload_Atom);
+        OwnedHqlExpr fieldKey = getRtlFieldKey(field, rowRecord, isPayload);
+        fieldType.set(fieldKey->queryType());
+        break;
+    }
+    }
+
+    return buildRtlType(instanceName, fieldType);
+}
+
+
 unsigned HqlCppTranslator::buildRtlIfBlockField(StringBuffer & instanceName, IHqlExpression * ifblock, IHqlExpression * rowRecord, bool isPayload)
 {
     StringBuffer typeName, s;
@@ -3965,6 +3991,14 @@ unsigned HqlCppTranslator::buildRtlType(StringBuffer & instanceName, ITypeInfo *
     typectx.setNextPriority(TypeInfoPrio);
     typectx.addQuoted(definition);
 
+    if (options.spanMultipleCpp)
+    {
+        StringBuffer s;
+        s.append("extern const ").append(info.className).append("  ").append(name).append(";");
+        BuildCtx protoctx(*code, mainprototypesAtom);
+        protoctx.addQuoted(s);
+    }
+
     OwnedHqlExpr nameExpr = createVariable(name.str(), makeVoidType());
     OwnedHqlExpr mapped = createAttribute(fieldAtom, LINK(nameExpr), getSizetConstant(info.fieldType));
     declarectx.associateExpr(search, mapped);

+ 138 - 60
ecl/hqlcpp/hqlsource.cpp

@@ -23,6 +23,7 @@
 #include "jstream.ipp"
 #include "jdebug.hpp"
 #include "eclrtl_imp.hpp"
+#include "rtlkey.hpp"
 
 #include "hql.hpp"
 #include "hqlattr.hpp"
@@ -3688,7 +3689,7 @@ void KeyFailureInfo::reportError(HqlCppTranslator & translator, IHqlExpression *
 }
 
 
-const char * BuildMonitorState::getSetName()
+const char * BuildMonitorState::getSetName(bool createValueSets)
 {
     if (!setNames.isItem(numActiveSets))
     {
@@ -3698,7 +3699,10 @@ const char * BuildMonitorState::getSetName()
 
         StringBuffer s;
         funcctx.setNextConstructor();
-        funcctx.addQuoted(s.append("Owned<IStringSet> ").append(name).append(";"));
+        if (createValueSets)
+            funcctx.addQuoted(s.append("Owned<IValueSet> ").append(name).append(";"));
+        else
+            funcctx.addQuoted(s.append("Owned<IStringSet> ").append(name).append(";"));
     }
 
     return setNames.item(numActiveSets++).text;
@@ -3720,6 +3724,21 @@ IHqlExpression * KeyConditionInfo::createConjunction()
     return result.getClear();
 }
 
+//---------------------------------------------------------------------------------------------------------------------
+
+const char * KeySelectorInfo::getFFOptions()
+{
+    switch (keyedKind)
+    {
+    case KeyedExtend:
+        return "FFopt";
+    default:
+        return "FFkeyed";
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+
 MonitorExtractor::MonitorExtractor(IHqlExpression * _tableExpr, HqlCppTranslator & _translator, int _numKeyableFields, bool _isDiskRead) : translator(_translator)
 { 
     tableExpr = _tableExpr;
@@ -3743,6 +3762,17 @@ MonitorExtractor::MonitorExtractor(IHqlExpression * _tableExpr, HqlCppTranslator
     cleanlyKeyedExplicitly = false;
     keyedExplicitly = false;
     allowDynamicFormatChange = !tableExpr->hasAttribute(fixedAtom);
+    createValueSets = translator.queryOptions().createValueSets;
+    if (createValueSets)
+    {
+        addRangeFunc = addRawRangeId;
+        killRangeFunc = killRawRangeId;
+    }
+    else
+    {
+        addRangeFunc = addRangeId;
+        killRangeFunc = killRangeId;
+    }
 }
 
 void MonitorExtractor::callAddAll(BuildCtx & ctx, IHqlExpression * targetVar)
@@ -4067,17 +4097,17 @@ void MonitorExtractor::buildKeySegmentInExpr(BuildMonitorState & buildState, Key
     IHqlExpression * expandedSelector = selectorInfo.expandedSelector;
     ITypeInfo * fieldType = expandedSelector->queryType();
     unsigned curSize = fieldType->getSize();
-    createStringSet(ctx, target, curSize, fieldType);
+    createStringSet(ctx, target, curSize, expandedSelector);
 
     OwnedHqlExpr targetVar = createVariable(target, makeVoidType());
     IHqlExpression * lhs = thisKey.queryChild(0);
     OwnedHqlExpr values = normalizeListCasts(thisKey.queryChild(1));
 
-    IIdAtom * func = addRangeId;
+    IIdAtom * func = addRangeFunc;
     if (thisKey.getOperator() == no_notin)
     {
         callAddAll(ctx, targetVar);
-        func = killRangeId;
+        func = killRangeFunc;
     }
 
     if (values->getOperator() != no_list)
@@ -4205,21 +4235,32 @@ void MonitorExtractor::extractCompareInformation(BuildCtx & ctx, IHqlExpression
         compare.setown(createCompareRecast(no_eq, compareValue, recastValue));
 }
 
-void MonitorExtractor::createStringSet(BuildCtx & ctx, const char * target, unsigned size, ITypeInfo * type)
+void MonitorExtractor::createStringSet(BuildCtx & ctx, const char * target, unsigned size, IHqlExpression * selector)
 {
-    if (onlyHozedCompares)
-        ctx.addQuotedF("%s.setown(createRtlStringSet(%u));", target, size);
+    assertex(selector->getOperator() == no_select);
+    if (createValueSets)
+    {
+        StringBuffer type;
+        translator.buildRtlFieldType(type, selector->queryChild(1), queryRecord(tableExpr));
+        ctx.addQuotedF("%s.setown(createValueSet(%s));", target, type.str());
+    }
     else
     {
-        bool isBigEndian = !type->isInteger() || !isLittleEndian(type);
-        ctx.addQuotedF("%s.setown(createRtlStringSetEx(%u,%d,%d));", target, size, isBigEndian, type->isSigned());
+        if (onlyHozedCompares)
+            ctx.addQuotedF("%s.setown(createRtlStringSet(%u));", target, size);
+        else
+        {
+            ITypeInfo * type = selector->queryType();
+            bool isBigEndian = !type->isInteger() || !isLittleEndian(type);
+            ctx.addQuotedF("%s.setown(createRtlStringSetEx(%u,%d,%d));", target, size, isBigEndian, type->isSigned());
+        }
     }
 }
 
 void MonitorExtractor::buildKeySegmentCompareExpr(BuildMonitorState & buildState, KeySelectorInfo & selectorInfo, BuildCtx & ctx, const char * targetSet, IHqlExpression & thisKey)
 {
     OwnedHqlExpr targetVar = createVariable(targetSet, makeVoidType());
-    createStringSet(ctx, targetSet, selectorInfo.size, selectorInfo.expandedSelector->queryType());
+    createStringSet(ctx, targetSet, selectorInfo.size, selectorInfo.expandedSelector);
 
     if (!exprReferencesDataset(&thisKey, tableExpr))
     {
@@ -4246,7 +4287,7 @@ void MonitorExtractor::buildKeySegmentCompareExpr(BuildMonitorState & buildState
             translator.buildFilter(subctx, compare);
         args.append(*LINK(address));
         args.append(*LINK(address));
-        translator.callProcedure(subctx, addRangeId, args);
+        translator.callProcedure(subctx, addRangeFunc, args);
         break;
     case no_ne:
         subctx.addQuoted(StringBuffer().appendf("%s->addAll();", targetSet));
@@ -4254,12 +4295,12 @@ void MonitorExtractor::buildKeySegmentCompareExpr(BuildMonitorState & buildState
             translator.buildFilter(subctx, compare);
         args.append(*LINK(address));
         args.append(*LINK(address));
-        translator.callProcedure(subctx, killRangeId, args);
+        translator.callProcedure(subctx, killRangeFunc, args);
         break;
     case no_le:
         args.append(*createValue(no_nullptr, makeVoidType()));
         args.append(*LINK(address));
-        translator.callProcedure(subctx, addRangeId, args);
+        translator.callProcedure(subctx, addRangeFunc, args);
         break;
     case no_lt:
         // e) no_lt.  If isExact add < value else add <= value
@@ -4270,14 +4311,14 @@ void MonitorExtractor::buildKeySegmentCompareExpr(BuildMonitorState & buildState
             //common this up...
             args.append(*createValue(no_nullptr, makeVoidType()));
             args.append(*LINK(address));
-            translator.callProcedure(subctx, addRangeId, args);
+            translator.callProcedure(subctx, addRangeFunc, args);
             subctx.selectElse(cond);
             args.append(*LINK(targetVar));
         }
         subctx.addQuoted(StringBuffer().appendf("%s->addAll();", targetSet));
         args.append(*LINK(address));
         args.append(*createValue(no_nullptr, makeVoidType()));
-        translator.callProcedure(subctx, killRangeId, args);
+        translator.callProcedure(subctx, killRangeFunc, args);
         break;
     case no_ge:
         // d) no_ge.  If isExact add >= value else add > value
@@ -4289,19 +4330,19 @@ void MonitorExtractor::buildKeySegmentCompareExpr(BuildMonitorState & buildState
             subctx.addQuoted(StringBuffer().appendf("%s->addAll();", targetSet));
             args.append(*createValue(no_nullptr, makeVoidType()));
             args.append(*LINK(address));
-            translator.callProcedure(subctx, killRangeId, args);
+            translator.callProcedure(subctx, killRangeFunc, args);
             subctx.selectElse(cond);
             args.append(*LINK(targetVar));
         }
         args.append(*LINK(address));
         args.append(*createValue(no_nullptr, makeVoidType()));
-        translator.callProcedure(subctx, addRangeId, args);
+        translator.callProcedure(subctx, addRangeFunc, args);
         break;
     case no_gt:
         subctx.addQuoted(StringBuffer().appendf("%s->addAll();", targetSet));
         args.append(*createValue(no_nullptr, makeVoidType()));
         args.append(*LINK(address));
-        translator.callProcedure(subctx, killRangeId, args);
+        translator.callProcedure(subctx, killRangeFunc, args);
         break;
     case no_between:
     case no_notbetween:
@@ -4321,11 +4362,11 @@ void MonitorExtractor::buildKeySegmentCompareExpr(BuildMonitorState & buildState
             translator.ensureHasAddress(subctx, rhs2);
             args.append(*getPointer(rhs2.expr));
             if (op == no_between)
-                translator.callProcedure(subctx, addRangeId, args);
+                translator.callProcedure(subctx, addRangeFunc, args);
             else
             {
                 subctx.addQuoted(StringBuffer().appendf("%s->addAll();", targetSet));
-                translator.callProcedure(subctx, killRangeId, args);
+                translator.callProcedure(subctx, killRangeFunc, args);
             }
             break;
         }
@@ -4371,7 +4412,7 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
     case no_notin:
         {
             if (!targetSet)
-                targetSet = buildState.getSetName();
+                targetSet = buildState.getSetName(createValueSets);
             buildKeySegmentInExpr(buildState, selectorInfo, ctx, targetSet, thisKey, filterKind);
             break;
         }
@@ -4391,7 +4432,7 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
             unsigned numMatches = matches.ordinality();
 
             if (!targetSet && numMatches > 1)
-                targetSet = buildState.getSetName();
+                targetSet = buildState.getSetName(createValueSets);
 
             IHqlStmt * ifStmt = NULL;
             if (invariant)
@@ -4405,10 +4446,13 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
             for (unsigned i=1; i< numMatches; i++)
             {
                 IHqlExpression & cur = matches.item(i);
-                const char * curTarget = buildState.getSetName();
+                const char * curTarget = buildState.getSetName(createValueSets);
                 BuildCtx childctx(subctx);
                 buildKeySegmentExpr(buildState, selectorInfo, childctx, curTarget, cur, MonitorFilterSkipAll);
-                childctx.addQuotedF("%s.setown(rtlIntersectSet(%s,%s));", targetSet, targetSet, curTarget);
+                if (createValueSets)
+                    childctx.addQuotedF("%s->intersectSet(%s);", targetSet, curTarget);
+                else
+                    childctx.addQuotedF("%s.setown(rtlIntersectSet(%s,%s));", targetSet, targetSet, curTarget);
                 buildState.popSetName();
             }
 
@@ -4416,7 +4460,7 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
             {
                 subctx.selectElse(ifStmt);
                 if (targetSet)
-                    createStringSet(subctx, targetSet, curSize, selectorInfo.selector->queryType());
+                    createStringSet(subctx, targetSet, curSize, selectorInfo.selector);
                 else
                     buildEmptyKeySegment(buildState, subctx, selectorInfo);
             }
@@ -4441,7 +4485,7 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
                     IHqlStmt * ifStmt = translator.buildFilterViaExpr(subctx, invariant);
                     if (targetSet)
                     {
-                        createStringSet(subctx, targetSet, curSize, selectorInfo.selector->queryType());
+                        createStringSet(subctx, targetSet, curSize, selectorInfo.selector);
                         OwnedHqlExpr targetVar = createVariable(targetSet, makeVoidType());
                         callAddAll(subctx, targetVar);
                     }
@@ -4451,16 +4495,19 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
             
             appendCtx = &subctx;
             if (!targetSet && numMatches > 1)
-                targetSet = buildState.getSetName();
+                targetSet = buildState.getSetName(createValueSets);
 
             buildKeySegmentExpr(buildState, selectorInfo, subctx, targetSet, matches.item(0), NoMonitorFilter);
             for (unsigned i=1; i < numMatches; i++)
             {
                 IHqlExpression & cur = matches.item(i);
-                const char * curTarget = buildState.getSetName();
+                const char * curTarget = buildState.getSetName(createValueSets);
                 BuildCtx childctx(subctx);
                 buildKeySegmentExpr(buildState, selectorInfo, childctx, curTarget, cur, MonitorFilterSkipEmpty);
-                childctx.addQuotedF("%s.setown(rtlUnionSet(%s, %s));", targetSet, targetSet, curTarget);
+                if (createValueSets)
+                    childctx.addQuotedF("%s->unionSet(%s);", targetSet, curTarget);
+                else
+                    childctx.addQuotedF("%s.setown(rtlUnionSet(%s, %s));", targetSet, targetSet, curTarget);
                 buildState.popSetName();
             }
             break;
@@ -4471,7 +4518,7 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
             {
                 if (buildSingleKeyMonitor(createMonitorText, selectorInfo, subctx, thisKey))
                     break;
-                targetSet = buildState.getSetName();
+                targetSet = buildState.getSetName(createValueSets);
             }
             buildKeySegmentCompareExpr(buildState, selectorInfo, ctx, targetSet, thisKey);
             break;
@@ -4479,7 +4526,7 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
     default:
         {
             if (!targetSet)
-                targetSet = buildState.getSetName();
+                targetSet = buildState.getSetName(createValueSets);
             buildKeySegmentCompareExpr(buildState, selectorInfo, ctx, targetSet, thisKey);
             break;
         }
@@ -4487,14 +4534,23 @@ void MonitorExtractor::buildKeySegmentExpr(BuildMonitorState & buildState, KeySe
 
     if (targetSet && !requiredSet)
     {
-        createMonitorText.appendf("createKeySegmentMonitor(%s, %s.getClear(), %u, %u, %u)",
-                                  boolToText(selectorInfo.keyedKind != KeyedYes), targetSet, selectorInfo.fieldIdx, selectorInfo.offset, selectorInfo.size);
+        if (createValueSets)
+            createMonitorText.appendf("createFieldFilter(%u, %s.getClear())", selectorInfo.fieldIdx, targetSet);
+        else
+            createMonitorText.appendf("createKeySegmentMonitor(%s, %s.getClear(), %u, %u, %u)",
+                                      boolToText(selectorInfo.keyedKind != KeyedYes), targetSet, selectorInfo.fieldIdx, selectorInfo.offset, selectorInfo.size);
+
 
         buildState.popSetName();
     }
 
     if (createMonitorText.length())
-        appendCtx->addQuotedF("%s->append(%s);", buildState.listName, createMonitorText.str());
+    {
+        if (createValueSets)
+            appendCtx->addQuotedF("%s->append(%s, %s);", buildState.listName, selectorInfo.getFFOptions(), createMonitorText.str());
+        else
+            appendCtx->addQuotedF("%s->append(%s);", buildState.listName, createMonitorText.str());
+    }
 }
 
 
@@ -4540,36 +4596,51 @@ bool MonitorExtractor::buildSingleKeyMonitor(StringBuffer & createMonitorText, K
     if (compare)
         return false;
 
-    ITypeInfo * type = selectorInfo.expandedSelector->queryType();
-    type_t tc = type->getTypeCode();
-    if ((tc == type_int) || (tc == type_swapint))
+    if (createValueSets)
     {
-        if (isLittleEndian(type))
-        {
-            if (type->isSigned())
-                funcName.append("createSingleLittleSignedKeySegmentMonitor");
-            else if (type->getSize() != 1)
-                funcName.append("createSingleLittleKeySegmentMonitor");
-        }
-        else
+        StringBuffer type;
+        translator.buildRtlFieldType(type, selectorInfo.selector->queryChild(1), queryRecord(selectorInfo.selector->queryChild(0)));
+
+        //MORE: Need to ensure it is exactly the correct format - e.g. variable length strings are length prefixed
+        OwnedHqlExpr address = getMonitorValueAddress(subctx, normalized);
+        StringBuffer addrText;
+        translator.generateExprCpp(addrText, address);
+
+        createMonitorText.appendf("createFieldFilter(%u, %s, %s)", selectorInfo.fieldIdx, type.str(), addrText.str());
+    }
+    else
+    {
+        ITypeInfo * type = selectorInfo.expandedSelector->queryType();
+        type_t tc = type->getTypeCode();
+        if ((tc == type_int) || (tc == type_swapint))
         {
-            if (type->isSigned())
-                funcName.append("createSingleBigSignedKeySegmentMonitor");
+            if (isLittleEndian(type))
+            {
+                if (type->isSigned())
+                    funcName.append("createSingleLittleSignedKeySegmentMonitor");
+                else if (type->getSize() != 1)
+                    funcName.append("createSingleLittleKeySegmentMonitor");
+            }
             else
-                funcName.append("createSingleKeySegmentMonitor");
+            {
+                if (type->isSigned())
+                    funcName.append("createSingleBigSignedKeySegmentMonitor");
+                else
+                    funcName.append("createSingleKeySegmentMonitor");
+            }
         }
-    }
-    
-    if (!funcName.length())
-        funcName.append("createSingleKeySegmentMonitor");
 
-    OwnedHqlExpr address = getMonitorValueAddress(subctx, normalized);
-    StringBuffer addrText;
-    translator.generateExprCpp(addrText, address);
+        if (!funcName.length())
+            funcName.append("createSingleKeySegmentMonitor");
 
-    createMonitorText.append(funcName)
-                     .appendf("(%s, %u, %u, %u, %s)",
-                              boolToText(selectorInfo.keyedKind != KeyedYes), selectorInfo.fieldIdx, selectorInfo.offset, selectorInfo.size, addrText.str());
+        OwnedHqlExpr address = getMonitorValueAddress(subctx, normalized);
+        StringBuffer addrText;
+        translator.generateExprCpp(addrText, address);
+
+        createMonitorText.append(funcName)
+                         .appendf("(%s, %u, %u, %u, %s)",
+                                  boolToText(selectorInfo.keyedKind != KeyedYes), selectorInfo.fieldIdx, selectorInfo.offset, selectorInfo.size, addrText.str());
+    }
     return true;
 }
 
@@ -4593,7 +4664,14 @@ KeyedKind getKeyedKind(HqlCppTranslator & translator, KeyConditionArray & matche
 void MonitorExtractor::buildEmptyKeySegment(BuildMonitorState & buildState, BuildCtx & ctx, KeySelectorInfo & selectorInfo)
 {
     StringBuffer s;
-    ctx.addQuoted(s.appendf("%s->append(createEmptyKeySegmentMonitor(%s, %u, %u, %u));", buildState.listName, boolToText(selectorInfo.keyedKind != KeyedYes), selectorInfo.fieldIdx, selectorInfo.offset, selectorInfo.size));
+    if (createValueSets)
+    {
+        StringBuffer type;
+        translator.buildRtlFieldType(type, selectorInfo.selector->queryChild(1), queryRecord(selectorInfo.selector->queryChild(0)));
+        ctx.addQuoted(s.appendf("%s->append(%s, createEmptyFieldFilter(%u, %s));", buildState.listName, selectorInfo.getFFOptions(), selectorInfo.fieldIdx, type.str()));
+    }
+    else
+        ctx.addQuoted(s.appendf("%s->append(createEmptyKeySegmentMonitor(%s, %u, %u, %u));", buildState.listName, boolToText(selectorInfo.keyedKind != KeyedYes), selectorInfo.fieldIdx, selectorInfo.offset, selectorInfo.size));
 }
 
 

+ 7 - 2
ecl/hqlcpp/hqlsource.ipp

@@ -92,7 +92,7 @@ struct BuildMonitorState
     inline bool wildPending() { return wildOffset != (unsigned)-1; }
     inline void clearWild() { wildOffset = (unsigned) -1; }
 
-    const char * getSetName();
+    const char * getSetName(bool createValueSets);
     void popSetName();
 
 //Constant while building monitors
@@ -126,6 +126,8 @@ public:
         size = _size;
     }
 
+    const char * getFFOptions();
+
     IHqlExpression * selector;
     IHqlExpression * expandedSelector;
     unsigned fieldIdx;
@@ -171,7 +173,7 @@ protected:
     IHqlExpression * castToFieldAndBack(IHqlExpression * left, IHqlExpression * right);
     bool containsTableSelects(IHqlExpression * expr);
     IHqlExpression * createRangeCompare(IHqlExpression * selector, IHqlExpression * value, IHqlExpression * lengthExpr, bool compareEqual);
-    void createStringSet(BuildCtx & ctx, const char * target, unsigned size, ITypeInfo * type);
+    void createStringSet(BuildCtx & ctx, const char * target, unsigned size, IHqlExpression * selector);
     KeyCondition * createTranslatedCondition(IHqlExpression * cond, KeyedKind keyedKind);
     bool extractBoolFieldFilter(KeyConditionInfo & matches, IHqlExpression * selector, KeyedKind keyedKind, bool compareValue);
     bool extractFilters(KeyConditionInfo & matches, IHqlExpression * filter, KeyedKind keyedKind);
@@ -233,6 +235,9 @@ protected:
     bool cleanlyKeyedExplicitly;
     bool keyedExplicitly;
     bool allowDynamicFormatChange;
+    bool createValueSets;
+    IIdAtom * addRangeFunc;
+    IIdAtom * killRangeFunc;
 };
 
 //---------------------------------------------------------------------------

+ 5 - 0
ecl/hthor/hthor.cpp

@@ -8391,6 +8391,11 @@ void CHThorBinaryDiskReadBase::append(IKeySegmentMonitor *segment)
     }
 }
 
+void CHThorBinaryDiskReadBase::append(FFoption option, IFieldFilter * filter)
+{
+    UNIMPLEMENTED;
+}
+
 unsigned CHThorBinaryDiskReadBase::ordinality() const
 {
     return segMonitors.length();

+ 1 - 0
ecl/hthor/hthor.ipp

@@ -2307,6 +2307,7 @@ public:
     virtual void append(IKeySegmentMonitor *segment);
     virtual unsigned ordinality() const;
     virtual IKeySegmentMonitor *item(unsigned idx) const;
+    virtual void append(FFoption option, IFieldFilter * filter);
 
 protected:
     virtual void verifyRecordFormatCrc() { ::verifyFormatCrcSuper(helper.getFormatCrc(), ldFile?ldFile->queryDistributedFile():NULL, false, true); }

+ 5 - 0
roxie/ccd/ccdactivities.cpp

@@ -962,6 +962,11 @@ public:
         }
     }
 
+    virtual void append(FFoption option, IFieldFilter * filter)
+    {
+        UNIMPLEMENTED;
+    }
+
     virtual unsigned ordinality() const
     {
         return cursor ? cursor->ordinality() : 0;

+ 10 - 0
roxie/ccd/ccdkey.cpp

@@ -291,6 +291,11 @@ public:
         totalCount += segment->getSize();
     }
 
+    virtual void append(FFoption option, IFieldFilter * filter)
+    {
+        UNIMPLEMENTED;
+    }
+
     virtual unsigned ordinality() const
     {
         return segMonitors.length();
@@ -1813,6 +1818,11 @@ public:
         segment->Release();
     }
 
+    virtual void append(FFoption option, IFieldFilter * filter)
+    {
+        UNIMPLEMENTED;
+    }
+
     virtual unsigned ordinality() const
     {
         return segMonitors.length();

+ 5 - 0
roxie/ccd/ccdserver.cpp

@@ -21569,6 +21569,11 @@ public:
         }
     }
 
+    virtual void append(FFoption option, IFieldFilter * filter)
+    {
+        UNIMPLEMENTED;
+    }
+
     virtual unsigned ordinality() const
     {
         return cursor ? cursor->ordinality() : 0;

+ 142 - 0
rtl/eclrtl/rtlkey.hpp

@@ -128,12 +128,21 @@ interface IBlobCreator
     virtual unsigned __int64 createBlob(size32_t _size, const void * _ptr) = 0;
 };
 
+enum FFoption : byte
+{
+    FFkeyed = 1,
+    FFopt = 1,
+    FFpost = 2,
+};
+
+interface IFieldFilter;
 interface IIndexReadContext
 {
 public:
     virtual void append(IKeySegmentMonitor *segment) = 0;
     virtual unsigned ordinality() const = 0;
     virtual IKeySegmentMonitor *item(unsigned idx) const = 0;
+    virtual void append(FFoption option, IFieldFilter * filter) = 0;
 };
 
 ECLRTL_API IStringSet *createRtlStringSet(size32_t size);
@@ -160,4 +169,137 @@ ECLRTL_API IOverrideableKeySegmentMonitor *createOverrideableKeySegmentMonitor(I
 ECLRTL_API IKeySegmentMonitor *deserializeKeySegmentMonitor(MemoryBuffer &mb);
 ECLRTL_API void deserializeSet(IStringSet & set, size32_t minRecordSize, const RtlTypeInfo * fieldType, const char * filter);
 
+//---------------------------------------------------------------------------------------------------------------------
+
+enum TransitionMask : byte
+{
+    CMPlt = 0x01,
+    CMPeq = 0x02,
+    CMPgt = 0x04,
+    CMPmin = 0x08,      //minimum possible value
+    CMPmax = 0x10,      //maximum possible value
+    CMPle = CMPlt | CMPeq,
+    CMPge = CMPgt | CMPeq,
+    CMPminmask = CMPgt|CMPmin,
+    CMPmaxmask = CMPlt|CMPmax,
+};
+
+class ValueTransition;
+interface RtlTypeInfo;
+class RtlRow;
+
+/*
+ * The IValueSet interface represents a set of ranges of values.
+ *
+ * The transitions always come in pairs - an upper and lower bound.  Each bound can be inclusive or exclusive.
+ */
+interface IValueSet : public IInterface
+{
+//The following methods are used for creating a valueset
+    virtual ValueTransition * createTransition(TransitionMask mask, unsigned __int64 value) const = 0;
+    virtual ValueTransition * createStringTransition(TransitionMask mask, size32_t len, const char * value) const = 0;
+    virtual ValueTransition * createUtf8Transition(TransitionMask mask, size32_t len, const char * value) const = 0;
+    virtual void addRange(ValueTransition * loval, ValueTransition * hival) = 0;
+    virtual void addAll() = 0;
+    virtual void killRange(ValueTransition * loval, ValueTransition * hival) = 0;
+    virtual void reset() = 0;
+    virtual void invertSet() = 0;
+    virtual void unionSet(const IValueSet *) = 0;
+    virtual void excludeSet(const IValueSet *) = 0;
+    virtual void intersectSet(const IValueSet *) = 0;
+    virtual StringBuffer & serialize(StringBuffer & out) const= 0;
+
+//following are primarily for use from the code generator
+    virtual ValueTransition * createRawTransition(TransitionMask mask, const void * value) const = 0;
+    virtual void addRawRange(const void * lower, const void * upper) = 0;
+    virtual void killRawRange(const void * lower, const void * upper) = 0;
+
+//The following methods are for use once the value set has been created.
+    virtual unsigned numRanges() const = 0;
+
+    //find the last range where the lower bound <= the field, returns 0 if the field matches the lower bound, > 0 otherwise.
+    //matchRange is set to the range number, set to numRanges if there is no possible match.  Uses a binary chop
+    virtual int findCandidateRange(const byte * field, unsigned & matchRange) const = 0;
+
+    //find the last range where the lower bound <= the field, returns 0 if the field matches the lower bound, > 0 otherwise.
+    //starts searching from curRange (which is likely to match).  Uses a sequential search.
+    virtual int checkCandidateRange(const byte * field, unsigned & curRange) const = 0;
+
+    // Does this field match any range?
+    virtual bool matches(const byte * field) const = 0;
+
+    // Does this field match this particular range?
+    virtual bool matches(const byte * field, unsigned range) const = 0;
+
+    virtual const RtlTypeInfo & queryType() const = 0;
+};
+extern ECLRTL_API IValueSet * createValueSet(const RtlTypeInfo & type);
+
+interface ISetCreator
+{
+public:
+    virtual void addRange(TransitionMask lowerMask, const StringBuffer & lower, TransitionMask upperMask, const StringBuffer & upperString) = 0;
+};
+
+/*
+ * Read the textual representation of a set.
+ * The format of the set is an optional comma-separated sequence of ranges.
+ * Each range is specified as paren lower, upper paren, where the paren is either ( or [ depending
+ * on whether the specified bound is inclusive or exclusive.
+ * If only one bound is specified then it is used for both upper and lower bound (only meaningful with [] )
+ *
+ * ( A means values > A - exclusive
+ * [ means values >= A - inclusive
+ * A ) means values < A - exclusive
+ * A ] means values <= A - inclusive
+ * For example:
+ * [A] matches just A
+ * (,A),(A,) matches all but A
+ * (A] of [A) are both empty ranges
+ * [A,B) means A*
+ * Values use the ECL syntax for constants. String constants are always utf8. Binary use d'xx' format (hexpairs)
+ *
+ * @param creator   The interface that wraps the set that is being created
+ * @param filter    The textual representation of the set.
+ */
+extern ECLRTL_API void deserializeSet(ISetCreator & creator, const char * filter);
+
+/*
+ * Read the textual representation of a set.
+ *
+ * @param set       The target set to be updated.
+ * @param filter    The textual representation of the set.
+ */
+extern ECLRTL_API void deserializeSet(IValueSet & set, const char * filter);
+
+
+/*
+ * This interface represents a filter on a particular field of the row - similar to a segment monitor.
+ *
+ * Example implementations include single value, sets of ranges, regex or wildcard
+ */
+interface IFieldFilter : public IInterface
+{
+public:
+//Simple row matching
+    virtual bool matches(const RtlRow & row) const = 0;
+
+    virtual int compareRow(const RtlRow & left, const RtlRow & right) const = 0;
+
+    //MORE to come to support index lookups.
+    virtual unsigned numRanges() const = 0;
+    virtual int findCandidateRange(const RtlRow & row, unsigned & matchRange) const = 0;
+    virtual int checkCandidateRange(const RtlRow & row, unsigned & curRange) const = 0;
+    virtual bool withinUpperRange(const RtlRow & row, unsigned range) const = 0; // is the row within the current upper limit?
+    virtual bool matches(const RtlRow & row, unsigned range) const = 0;
+};
+
+//More types of IFieldFilter to come later
+extern ECLRTL_API IFieldFilter * createEmptyFieldFilter(unsigned _fieldId, const RtlTypeInfo & type);
+extern ECLRTL_API IFieldFilter * createFieldFilter(unsigned fieldId, const RtlTypeInfo & type, const void * value);
+extern ECLRTL_API IFieldFilter * createFieldFilter(unsigned fieldId, IValueSet * values);
+extern ECLRTL_API IFieldFilter * createWildFieldFilter(unsigned fieldId);
+
+
+
 #endif

+ 32 - 0
rtl/eclrtl/rtlnewkey.cpp

@@ -722,6 +722,25 @@ public:
 
     }
 
+    virtual ValueTransition * createRawTransition(TransitionMask mask, const void * value) const override
+    {
+        return new ValueTransition(mask, type, value);
+    }
+
+    virtual void addRawRange(const void * lower, const void * upper) override
+    {
+        Owned<ValueTransition> lowerBound = lower ? createRawTransition(CMPge, lower) : nullptr;
+        Owned<ValueTransition> upperBound = upper ? createRawTransition(CMPle, upper) : nullptr;
+        addRange(lowerBound, upperBound);
+    }
+
+    virtual void killRawRange(const void * lower, const void * upper) override
+    {
+        Owned<ValueTransition> lowerBound = lower ? createRawTransition(CMPge, lower) : nullptr;
+        Owned<ValueTransition> upperBound = upper ? createRawTransition(CMPle, upper) : nullptr;
+        killRange(lowerBound, upperBound);
+    }
+
 // Methods for using a value set
     virtual unsigned numRanges() const override;
 
@@ -992,6 +1011,19 @@ IFieldFilter * createFieldFilter(unsigned fieldId, IValueSet * values)
     return new SetFieldFilter(fieldId, values);
 }
 
+IFieldFilter * createEmptyFieldFilter(unsigned fieldId, const RtlTypeInfo & type)
+{
+    Owned<IValueSet> values = createValueSet(type);
+    return new SetFieldFilter(fieldId, values);
+}
+
+IFieldFilter * createFieldFilter(unsigned fieldId, const RtlTypeInfo & type, const void * value)
+{
+    Owned<IValueSet> values = createValueSet(type);
+    values->addRawRange(value, value);
+    return new SetFieldFilter(fieldId, values);
+}
+
 //---------------------------------------------------------------------------------------------------------------------
 
 /*

+ 2 - 121
rtl/eclrtl/rtlnewkey.hpp

@@ -19,128 +19,9 @@
 #define RTLNEWKEY_INCL
 #include "eclrtl.hpp"
 
-enum TransitionMask : byte
-{
-    CMPlt = 0x01,
-    CMPeq = 0x02,
-    CMPgt = 0x04,
-    CMPmin = 0x08,      //minimum possible value
-    CMPmax = 0x10,      //maximum possible value
-    CMPle = CMPlt | CMPeq,
-    CMPge = CMPgt | CMPeq,
-    CMPminmask = CMPgt|CMPmin,
-    CMPmaxmask = CMPlt|CMPmax,
-};
-BITMASK_ENUM(TransitionMask);
-
-class ValueTransition;
-interface RtlTypeInfo;
-class RtlRow;
-
-/*
- * The IValueSet interface represents a set of ranges of values.
- *
- * The transitions always come in pairs - an upper and lower bound.  Each bound can be inclusive or exclusive.
- */
-interface IValueSet : public IInterface
-{
-//The following methods are used for creating a valueset
-    virtual ValueTransition * createTransition(TransitionMask mask, unsigned __int64 value) const = 0;
-    virtual ValueTransition * createStringTransition(TransitionMask mask, size32_t len, const char * value) const = 0;
-    virtual ValueTransition * createUtf8Transition(TransitionMask mask, size32_t len, const char * value) const = 0;
-    virtual void addRange(ValueTransition * loval, ValueTransition * hival) = 0;
-    virtual void addAll() = 0;
-    virtual void killRange(ValueTransition * loval, ValueTransition * hival) = 0;
-    virtual void reset() = 0;
-    virtual void invertSet() = 0;
-    virtual void unionSet(const IValueSet *) = 0;
-    virtual void excludeSet(const IValueSet *) = 0;
-    virtual void intersectSet(const IValueSet *) = 0;
-    virtual StringBuffer & serialize(StringBuffer & out) const= 0;
-
-//The following methods are for use once the value set has been created.
-    virtual unsigned numRanges() const = 0;
+#include "rtlkey.hpp"
 
-    //find the last range where the lower bound <= the field, returns 0 if the field matches the lower bound, > 0 otherwise.
-    //matchRange is set to the range number, set to numRanges if there is no possible match.  Uses a binary chop
-    virtual int findCandidateRange(const byte * field, unsigned & matchRange) const = 0;
-
-    //find the last range where the lower bound <= the field, returns 0 if the field matches the lower bound, > 0 otherwise.
-    //starts searching from curRange (which is likely to match).  Uses a sequential search.
-    virtual int checkCandidateRange(const byte * field, unsigned & curRange) const = 0;
-
-    // Does this field match any range?
-    virtual bool matches(const byte * field) const = 0;
-
-    // Does this field match this particular range?
-    virtual bool matches(const byte * field, unsigned range) const = 0;
-
-    virtual const RtlTypeInfo & queryType() const = 0;
-};
-extern ECLRTL_API IValueSet * createValueSet(const RtlTypeInfo & type);
-
-interface ISetCreator
-{
-public:
-    virtual void addRange(TransitionMask lowerMask, const StringBuffer & lower, TransitionMask upperMask, const StringBuffer & upperString) = 0;
-};
-
-/*
- * Read the textual representation of a set.
- * The format of the set is an optional comma-separated sequence of ranges.
- * Each range is specified as paren lower, upper paren, where the paren is either ( or [ depending
- * on whether the specified bound is inclusive or exclusive.
- * If only one bound is specified then it is used for both upper and lower bound (only meaningful with [] )
- *
- * ( A means values > A - exclusive
- * [ means values >= A - inclusive
- * A ) means values < A - exclusive
- * A ] means values <= A - inclusive
- * For example:
- * [A] matches just A
- * (,A),(A,) matches all but A
- * (A] of [A) are both empty ranges
- * [A,B) means A*
- * Values use the ECL syntax for constants. String constants are always utf8. Binary use d'xx' format (hexpairs)
- *
- * @param creator   The interface that wraps the set that is being created
- * @param filter    The textual representation of the set.
- */
-extern ECLRTL_API void deserializeSet(ISetCreator & creator, const char * filter);
-
-/*
- * Read the textual representation of a set.
- *
- * @param set       The target set to be updated.
- * @param filter    The textual representation of the set.
- */
-extern ECLRTL_API void deserializeSet(IValueSet & set, const char * filter);
-
-
-/*
- * This interface represents a filter on a particular field of the row - similar to a segment monitor.
- *
- * Example implementations include single value, sets of ranges, regex or wildcard
- */
-interface IFieldFilter : public IInterface
-{
-public:
-//Simple row matching
-    virtual bool matches(const RtlRow & row) const = 0;
-
-    virtual int compareRow(const RtlRow & left, const RtlRow & right) const = 0;
-
-    //MORE to come to support index lookups.
-    virtual unsigned numRanges() const = 0;
-    virtual int findCandidateRange(const RtlRow & row, unsigned & matchRange) const = 0;
-    virtual int checkCandidateRange(const RtlRow & row, unsigned & curRange) const = 0;
-    virtual bool withinUpperRange(const RtlRow & row, unsigned range) const = 0; // is the row within the current upper limit?
-    virtual bool matches(const RtlRow & row, unsigned range) const = 0;
-};
-
-//More types of IFieldFilter to come later
-extern ECLRTL_API IFieldFilter * createFieldFilter(unsigned fieldId, IValueSet * values);
-extern ECLRTL_API IFieldFilter * createWildFieldFilter(unsigned fieldId);
+BITMASK_ENUM(TransitionMask);
 
 /*
  * The RowFilter class represents a multiple-field filter of a row.

+ 11 - 0
system/jhtree/jhtree.cpp

@@ -288,6 +288,11 @@ void SegMonitorList::append(IKeySegmentMonitor *segment)
     segMonitors.append(*segment);
 }
 
+void SegMonitorList::append(FFoption option, IFieldFilter * filter)
+{
+    UNIMPLEMENTED;
+}
+
 bool SegMonitorList::matched(void *keyBuffer, unsigned &lastMatch) const
 {
     lastMatch = 0;
@@ -597,6 +602,12 @@ public:
         activitySegs->append(segment);
     }
 
+
+    virtual void append(FFoption option, IFieldFilter * filter)
+    {
+        UNIMPLEMENTED;
+    }
+
     virtual unsigned ordinality() const 
     {
         return activitySegs->ordinality();

+ 5 - 4
system/jhtree/jhtree.hpp

@@ -165,7 +165,7 @@ class jhtree_decl SegMonitorList : implements IInterface, implements IIndexReadC
     unsigned keySegCount;
 
 public:
-    IMPLEMENT_IINTERFACE;
+    IMPLEMENT_IINTERFACE_O;
     SegMonitorList(const RtlRecord &_recInfo, bool _needWild);
     IArrayOf<IKeySegmentMonitor> segMonitors;
 
@@ -187,9 +187,10 @@ public:
     void serialize(MemoryBuffer &mb) const;
 
     // interface IIndexReadContext
-    virtual void append(IKeySegmentMonitor *segment);
-    virtual unsigned ordinality() const;
-    virtual IKeySegmentMonitor *item(unsigned i) const;
+    virtual void append(IKeySegmentMonitor *segment) override;
+    virtual unsigned ordinality() const override;
+    virtual IKeySegmentMonitor *item(unsigned i) const override;
+    virtual void append(FFoption option, IFieldFilter * filter) override;
 };
 
 class IRecordLayoutTranslator;

+ 8 - 3
thorlcr/activities/diskread/thdiskreadslave.cpp

@@ -87,7 +87,7 @@ public:
     }
 
 // IIndexReadContext impl.
-    void append(IKeySegmentMonitor *segment)
+    virtual void append(IKeySegmentMonitor *segment)
     {
         if (segment->isWild())
             segment->Release();
@@ -99,12 +99,17 @@ public:
         }
     }
 
-    unsigned ordinality() const
+    virtual void append(FFoption option, IFieldFilter * filter)
+    {
+        UNIMPLEMENTED;
+    }
+
+    virtual unsigned ordinality() const
     {
         return segMonitors.length();
     }
 
-    IKeySegmentMonitor *item(unsigned idx) const
+    virtual IKeySegmentMonitor *item(unsigned idx) const
     {
         if (segMonitors.isItem(idx))
             return &segMonitors.item(idx);