Browse Source

Merge remote-tracking branch 'origin/candidate-3.8.x'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 13 years ago
parent
commit
019d132fe3

+ 1 - 1
cmake_modules/dependencies/lucid.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.40.0, libicu42, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.40.0, libicu42, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1")

+ 1 - 1
cmake_modules/dependencies/natty.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.42.0, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.42.0, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1")

+ 24 - 10
common/remote/hooks/libarchive/archive.cpp

@@ -369,18 +369,32 @@ static IFile *createIFileInArchive(const char *containedFileName)
     StringBuffer fname(containedFileName);
     assertex(fname.length());
     removeTrailingPathSepChar(fname);
-    StringBuffer dirPath, dirTail;
-    splitFilename(fname.str(), &dirPath, &dirPath, &dirTail, &dirTail);
-
-    Owned<IDirectoryIterator> dir = createArchiveDirectoryIterator(dirPath.str(), dirTail.str(), false, true);
-    if (dir->first())
-    {
-        Linked<IFile> file = &dir->query();
-        assertex(!dir->next());
-        return file.getClear();
+    StringAttr container, option, relpath;
+    splitArchivedFileName(fname.str(), container, option, relpath);
+    if (relpath.length())
+    {
+        StringBuffer dirPath, dirTail;
+        dirPath.append(container).append(option);
+        splitFilename(relpath, &dirPath, &dirPath, &dirTail, &dirTail);
+        Owned<IDirectoryIterator> dir = createArchiveDirectoryIterator(dirPath.str(), dirTail.str(), false, true);
+        if (dir->first())
+        {
+            Linked<IFile> file = &dir->query();
+            assertex(!dir->next());
+            return file.getClear();
+        }
+        else
+            return new ArchiveFile(containedFileName, NULL);
     }
     else
-        return new ArchiveFile(containedFileName, NULL);
+    {
+        // Create an IFile representing the root of the archive as a directory
+        struct archive_entry *rootEntry = archive_entry_new();
+        archive_entry_set_pathname(rootEntry, ".");
+        archive_entry_set_mode(rootEntry, __S_IFDIR);
+        archive_entry_set_size(rootEntry, 0);
+        return new ArchiveFile(containedFileName, new ArchiveEntry(rootEntry));
+    }
 }
 
 class ArchiveDirectoryIterator : public CInterface, implements IDirectoryIterator

+ 36 - 0
ecl/hql/hqlutil.cpp

@@ -7752,3 +7752,39 @@ bool userPreventsSort(IHqlExpression * noSortAttr, node_operator side)
         return name == rightAtom;
     throwUnexpected();
 }
+
+//-------------------------------------------------------------------------------------------------
+
+IHqlExpression * queryTransformAssign(IHqlExpression * transform, IHqlExpression * searchField)
+{
+    ForEachChild(i, transform)
+    {
+        IHqlExpression * cur = transform->queryChild(i);
+        switch (cur->getOperator())
+        {
+        case no_assignall:
+            {
+                IHqlExpression * ret = queryTransformAssign(cur, searchField);
+                if (ret)
+                    return ret;
+                break;
+            }
+        case no_assign:
+            {
+                IHqlExpression * lhs = cur->queryChild(0)->queryChild(1);
+                if (lhs == searchField)
+                    return cur;
+                break;
+            }
+        }
+    }
+    return NULL;
+}
+
+IHqlExpression * queryTransformAssignValue(IHqlExpression * transform, IHqlExpression * searchField)
+{
+    IHqlExpression * value = queryTransformAssign(transform, searchField);
+    if (value)
+        return value->queryChild(1);
+    return NULL;
+}

+ 2 - 0
ecl/hql/hqlutil.hpp

@@ -644,5 +644,7 @@ extern HQL_API IECLError * annotateExceptionWithLocation(IException * e, IHqlExp
 extern HQL_API IHqlExpression * convertAttributeToQuery(IHqlExpression * expr, HqlLookupContext & ctx);
 extern HQL_API StringBuffer & appendLocation(StringBuffer & s, IHqlExpression * location, const char * suffix = NULL);
 extern HQL_API bool userPreventsSort(IHqlExpression * noSortAttr, node_operator side);
+extern HQL_API IHqlExpression * queryTransformAssign(IHqlExpression * transform, IHqlExpression * searchField);
+extern HQL_API IHqlExpression * queryTransformAssignValue(IHqlExpression * transform, IHqlExpression * searchField);
 
 #endif

+ 1 - 0
ecl/hqlcpp/hqlcpp.ipp

@@ -1490,6 +1490,7 @@ public:
     void addDependency(BuildCtx & ctx, ABoundActivity * element, ABoundActivity * dependent, _ATOM kind, const char * label=NULL, int controlId = 0);
     void addFileDependency(IHqlExpression * name, ABoundActivity * whoAmI);
 
+    void doBuildClearAggregateRecord(BuildCtx & ctx, IHqlExpression * record, IHqlExpression * self, IHqlExpression * transform);
     void doBuildAggregateClearFunc(BuildCtx & ctx, IHqlExpression * expr);
     void doBuildAggregateFirstFunc(BuildCtx & ctx, IHqlExpression * expr);
     void doBuildAggregateNextFunc(BuildCtx & ctx, IHqlExpression * expr);

+ 28 - 26
ecl/hqlcpp/hqlhtcpp.cpp

@@ -11880,6 +11880,33 @@ ABoundActivity * HqlCppTranslator::doBuildActivitySelectNth(BuildCtx & ctx, IHql
 
 //---------------------------------------------------------------------------
 
+void HqlCppTranslator::doBuildClearAggregateRecord(BuildCtx & ctx, IHqlExpression * record, IHqlExpression * self, IHqlExpression * transform)
+{
+    ForEachChild(i, record)
+    {
+        IHqlExpression * cur = record->queryChild(i);
+        switch (cur->getOperator())
+        {
+        case no_record:
+            doBuildClearAggregateRecord(ctx, cur, self, transform);
+            break;
+        case no_field:
+            {
+                OwnedHqlExpr target = createSelectExpr(LINK(self), LINK(cur));
+                IHqlExpression * value = queryTransformAssignValue(transform, cur);
+                assertex(value);
+                if (value->isConstant())
+                    buildAssign(ctx, target, value);
+                else
+                    buildClear(ctx, target);
+                break;
+            }
+        case no_ifblock:
+            throwUnexpected();
+        }
+    }
+}
+
 void HqlCppTranslator::doBuildAggregateClearFunc(BuildCtx & ctx, IHqlExpression * expr)
 {
     IHqlExpression * tgtRecord = expr->queryChild(1);
@@ -11900,32 +11927,7 @@ void HqlCppTranslator::doBuildAggregateClearFunc(BuildCtx & ctx, IHqlExpression
 
     ensureRowAllocated(funcctx, "crSelf");
 
-    unsigned numAggregates = transform->numChildren();
-    unsigned idx;
-    OwnedHqlExpr self = getSelf(tgtRecord);
-    for (idx = 0; idx < numAggregates; idx++)
-    {
-        IHqlExpression * cur = transform->queryChild(idx);
-        OwnedHqlExpr target = selfRow->bindToRow(cur->queryChild(0), self);
-        IHqlExpression * src = cur->queryChild(1);
-
-        switch (src->getOperator())
-        {
-        case no_countgroup:
-        case no_maxgroup:
-        case no_mingroup:
-        case no_sumgroup:
-        case no_existsgroup:
-            buildClear(funcctx, target);
-            break;
-        default:
-            if (src->isConstant())
-                buildAssign(funcctx, target, src);
-            else
-                buildClear(funcctx, target);
-            break;
-        }
-    }
+    doBuildClearAggregateRecord(funcctx, transform->queryRecord(), selfRow->querySelector(), transform);
     buildReturnRecordSize(funcctx, selfRow);
 }
 

+ 4 - 32
ecl/hqlcpp/hqliproj.cpp

@@ -58,34 +58,6 @@ enum
 
 //-------------------------------------------------------------------------------------------------
 
-IHqlExpression * queryTransformAssign(IHqlExpression * transform, IHqlExpression * searchField)
-{
-    ForEachChild(i, transform)
-    {
-        IHqlExpression * cur = transform->queryChild(i);
-        switch (cur->getOperator())
-        {
-        case no_assignall:
-            {
-                IHqlExpression * ret = queryTransformAssign(cur, searchField);
-                if (ret)
-                    return ret;
-                break;
-            }
-        case no_assign:
-            {
-                IHqlExpression * lhs = cur->queryChild(0)->queryChild(1);
-                if (lhs == searchField)
-                    return cur->queryChild(1);
-                break;
-            }
-        }
-    }
-    return NULL;
-}
-
-//-------------------------------------------------------------------------------------------------
-
 void UsedFieldSet::addUnique(IHqlExpression * field)
 {
     //MORE: Add if (!all test to short-circuit contains)
@@ -556,7 +528,7 @@ void UsedFieldSet::gatherTransformValuesUsed(HqlExprArray * selfSelects, SelectU
             }
             if (values)
             {
-                IHqlExpression * transformValue = queryTransformAssign(transform, &cur);
+                IHqlExpression * transformValue = queryTransformAssignValue(transform, &cur);
                 //If no transform value is found then we almost certainly have an invalid query (e.g, LEFT inside a
                 //global).  Don't add the value - you'll definitely get a later follow on error
                 assertex(transformValue);
@@ -573,7 +545,7 @@ void UsedFieldSet::gatherTransformValuesUsed(HqlExprArray * selfSelects, SelectU
         {
             IHqlExpression * field = curNested.field;
             OwnedHqlExpr selected = selector ? createSelectExpr(LINK(selector), LINK(field)) : NULL;
-            IHqlExpression * transformValue = queryTransformAssign(transform, field);
+            IHqlExpression * transformValue = queryTransformAssignValue(transform, field);
             assertex(transformValue);
             bool includeThis = true;
             if (!curNested.includeAll() && transformValue->isPure())
@@ -3243,9 +3215,9 @@ IHqlExpression * ImplicitProjectTransformer::updateSelectors(IHqlExpression * ne
 
 const SelectUsedArray & ImplicitProjectTransformer::querySelectsUsedForField(IHqlExpression * transform, IHqlExpression * field)
 {
-    IHqlExpression * transformValues = queryTransformAssign(transform, field);
+    IHqlExpression * transformValues = queryTransformAssignValue(transform, field);
     if (!transformValues)
-         transformValues = queryTransformAssign(transform, field);
+         transformValues = queryTransformAssignValue(transform, field);
     return querySelectsUsed(transformValues);
 }
 

+ 33 - 3
plugins/dbconnectors/ecljdbc/src/main/java/com/hpccsystems/ecljdbc/EclConnection.java

@@ -23,7 +23,9 @@ import java.util.Properties;
  * @author rpastrana
  */
 
-public class EclConnection implements Connection {
+public class EclConnection implements Connection
+{
+	public static final String ECLRESULTLIMDEFAULT = "100";
     private boolean closed;
     private EclDatabaseMetaData metadata;
     private Properties props;
@@ -71,8 +73,36 @@ public class EclConnection implements Connection {
 		if (!this.props.containsKey("password"))
 			this.props.setProperty("password", "");
 
-		if (!this.props.containsKey("EclLimit"))
-			this.props.setProperty("EclLimit", "100");
+		boolean setdefaultreslim = false;
+		if (this.props.containsKey("EclResultLimit"))
+		{
+			String eclreslim = this.props.getProperty("EclResultLimit").trim();
+			try
+			{
+				if(Utils.isNumeric(eclreslim))
+				{
+					if (Integer.valueOf(eclreslim).intValue() <= 0)
+						setdefaultreslim = true;
+				}
+				else
+				{
+					if(!eclreslim.equalsIgnoreCase("ALL"))
+						setdefaultreslim = true;
+				}
+			}
+			catch (Exception e)
+			{
+				setdefaultreslim = true;
+			}
+		}
+		else
+			setdefaultreslim = true;
+
+		if (setdefaultreslim)
+		{
+			this.props.setProperty("EclResultLimit", ECLRESULTLIMDEFAULT);
+			System.out.println("Invalid Numeric EclResultLimit value detected, using default value: " + ECLRESULTLIMDEFAULT);
+		}
 
 		// basicAuth
 		String userPassword = this.props.getProperty("username") + ":"

+ 6 - 11
plugins/dbconnectors/ecljdbc/src/main/java/com/hpccsystems/ecljdbc/EclDriver.java

@@ -100,22 +100,17 @@ public class EclDriver implements Driver
 		try
 		{
 			Properties info = new Properties();
-			info.put("ServerAddress", "192.168.124.128"); //Mine
-			//info.put("ServerAddress", "10.239.20.80"); //clo
-			//info.put("ServerAddress", "10.239.219.10"); //fishbeck
-			//info.put("ServerAddress", "172.25.237.145"); //arjuna
+			info.put("ServerAddress", "192.168.124.128");
 
 			info.put("Cluster", "thor");
 			info.put("WsECLWatchPort", "8010");
-			info.put("EclLimit", "10");
+			info.put("EclResultLimit", "ALL");
 			info.put("WsECLPort", "8002");
 			info.put("WsECLDirectPort", "8008");
-			info.put("username", "_rpastrana");
-			info.put("password", "a");
+			info.put("username", "myhpccusername");
+			info.put("password", "myhpccpass");
 
-			//conn = (EclConnection) d.connect("url:jdbc:ecl;ServerAddress=192.168.124.128;Cluster=myroxie",info);
-			//conn = (EclConnection) d.connect("url:jdbc:ecl;ServerAddress=10.239.20.80;Cluster=thor;EclLimit=8",info);
-			//conn = (EclConnection) d.connect("url:jdbc:ecl;ServerAddress=10.239.219.10;Cluster=thor;EclLimit=8",info);
+			//conn = (EclConnection) d.connect("url:jdbc:ecl;ServerAddress=10.239.219.10;Cluster=thor;EclResultLimit=8",info);
 			conn = (EclConnection) d.connect("url:jdbc:ecl;",info);
 
 			PreparedStatement p = conn.prepareStatement(
@@ -136,7 +131,7 @@ public class EclDriver implements Driver
 			//"select count(city)  from tutorial::rp::tutorialperson where zip = '33445'"//where zip = '33445'"
 			//"select * from enron::final where tos = 'randy.young@enron.com' limit 1000"
 			//"select count(*), zip from tutorial::rp::tutorialperson where zip = '33445' "
-			"select * from tutorial::rp::tutorialperson where zip > '32605'"
+			"select zip from tutorial::rp::tutorialperson where zip < '32605' group by zip"
 			//"select MAX(firstname), lastname from tutorial::rp::tutorialperson  limit 1000"
 			//"select 1"
 			//"select zip, city from tutorial::rp::tutorialperson where city = 'ABBEVILLE' "

+ 7 - 3
plugins/dbconnectors/ecljdbc/src/main/java/com/hpccsystems/ecljdbc/EclEngine.java

@@ -530,18 +530,22 @@ public class EclEngine
 					if (!parameters.containsKey("GROUPBY"))
 					{
 						if (parameters.containsKey("COUNTFN"))
+						{
 							sb.append("scalarout := COUNT(idxds");
+							sb.append(", KEYED);\n");
+						}
 						if (parameters.containsKey("MAXFN"))
 						{
 							sb.append("scalarout := MAX(idxds, fileds.");
 							sb.append(parameters.get("FNCOLS"));
+							sb.append(", KEYED);\n");
 						}
 						if (parameters.containsKey("MINFN"))
 						{
 							sb.append("scalarout := MIN(idxds, fileds.");
 							sb.append(parameters.get("FNCOLS"));
+							sb.append(", KEYED);\n");
 						}
-						sb.append(", KEYED);\n");
 					}
 
 					if (parameters.containsKey("SCALAROUTNAME"))
@@ -648,8 +652,8 @@ public class EclEngine
 				sb.append(",");
 				if (parameters.containsKey("LIMIT"))
 					sb.append(parameters.get("LIMIT"));
-				else
-					sb.append( props.getProperty("EclLimit"));
+				else 
+					sb.append( props.getProperty("EclResultLimit"));;
 				sb.append("),NAMED(\'ECLJDBCSelectOutput\'));");
 			}
 

+ 159 - 6
roxie/roxiemem/roxierow.cpp

@@ -100,11 +100,11 @@ bool isRowCheckValid(unsigned allocatorId, const void * row)
 {
     switch (allocatorId & ALLOCATORID_CHECK_MASK)
     {
-    case NoCheckingHelper::allocatorCheckFlag:
+    case NoCheckingHelper::allocatorCheckFlag & ALLOCATORID_CHECK_MASK:
         return true;
-    case Crc16CheckingHelper::allocatorCheckFlag:
+    case Crc16CheckingHelper::allocatorCheckFlag & ALLOCATORID_CHECK_MASK:
         return Crc16CheckingHelper::isValid(row);
-    case Sum16CheckingHelper::allocatorCheckFlag:
+    case Sum16CheckingHelper::allocatorCheckFlag & ALLOCATORID_CHECK_MASK:
         return Sum16CheckingHelper::isValid(row);
     default:
         UNIMPLEMENTED;
@@ -222,7 +222,7 @@ public:
         unsigned flags = packed ? roxiemem::RHFpacked : roxiemem::RHFnone;
         if (meta.needsDestruct() || CHECKER::allocatorCheckFlag)
             flags |= roxiemem::RHFhasdestructor;
-        heap.setown(rowManager.createFixedRowHeap(meta.getFixedSize(), allocatorId | ACTIVITY_FLAG_ISREGISTERED | CHECKER::allocatorCheckFlag, (roxiemem::RoxieHeapFlags)flags));
+        heap.setown(rowManager.createFixedRowHeap(meta.getFixedSize()+CHECKER::extraSize, allocatorId | ACTIVITY_FLAG_ISREGISTERED | CHECKER::allocatorCheckFlag, (roxiemem::RoxieHeapFlags)flags));
     }
 
     virtual void * createRow()
@@ -277,12 +277,20 @@ public:
     virtual void * createRow(size32_t & allocatedSize)
     {
         const size32_t allocSize = meta.getInitialSize();
-        return heap->allocate(allocSize+CHECKER::extraSize, allocatedSize);
+        void * row = heap->allocate(allocSize+CHECKER::extraSize, allocatedSize);
+        //This test should get constant folded to avoid the decrement when not checked.
+        if (CHECKER::extraSize)
+            allocatedSize -= CHECKER::extraSize;
+        return row;
     }
 
     virtual void * resizeRow(size32_t newSize, void * row, size32_t & size)
     {
-        return heap->resizeRow(row, size, newSize+CHECKER::extraSize, size);
+        size32_t oldsize = size;  // don't need to include the extra checking bytes
+        void * newrow = heap->resizeRow(row, oldsize, newSize+CHECKER::extraSize, size);
+        if (CHECKER::extraSize)
+            size -= CHECKER::extraSize;
+        return newrow;
     }
 
     virtual void * finalizeRow(size32_t finalSize, void * row, size32_t oldSize)
@@ -314,3 +322,148 @@ IEngineRowAllocator * createCrcRoxieRowAllocator(roxiemem::IRowManager & rowMana
     else
         return new RoxieEngineVariableRowAllocator<Crc16CheckingHelper>(rowManager, meta, activityId, allocatorId, packed);
 }
+
+
+#ifdef _USE_CPPUNIT
+#include <cppunit/extensions/HelperMacros.h>
+#define ASSERT(a) { if (!(a)) CPPUNIT_ASSERT(a); }
+
+namespace roxiemem {
+
+class RoxieRowAllocatorTests : public CppUnit::TestFixture
+{
+    CPPUNIT_TEST_SUITE( RoxieRowAllocatorTests );
+        CPPUNIT_TEST(testChecking);
+    CPPUNIT_TEST_SUITE_END();
+    const IContextLogger &logctx;
+
+public:
+    RoxieRowAllocatorTests() : logctx(queryDummyContextLogger())
+    {
+        setTotalMemoryLimit(40*HEAP_ALIGNMENT_SIZE, 0, NULL);
+    }
+
+    ~RoxieRowAllocatorTests()
+    {
+        releaseRoxieHeap();
+    }
+
+protected:
+    class CheckingRowAllocatorCache : public IRowAllocatorCache
+    {
+    public:
+        CheckingRowAllocatorCache() { numFailures = 0; }
+        virtual unsigned getActivityId(unsigned cacheId) const { return 0; }
+        virtual StringBuffer &getActivityDescriptor(unsigned cacheId, StringBuffer &out) const { return out.append(cacheId); }
+        virtual void onDestroy(unsigned cacheId, void *row) const
+        {
+            if (!RoxieRowCheckValid(cacheId, row))
+                ++numFailures;
+        }
+        virtual void checkValid(unsigned cacheId, const void *row) const
+        {
+            if (!RoxieRowCheckValid(cacheId, row))
+                ++numFailures;
+        }
+
+        mutable unsigned numFailures;
+    };
+
+    class DummyOutputMeta : public IOutputMetaData, public CInterface
+    {
+    public:
+        DummyOutputMeta(size32_t _minSize, size32_t _fixedSize) : minSize(_minSize), fixedSize(_fixedSize) {}
+        IMPLEMENT_IINTERFACE
+
+        virtual size32_t getRecordSize(const void *rec) { return minSize; }
+        virtual size32_t getFixedSize() const { return fixedSize; }
+        virtual size32_t getMinRecordSize() const { return minSize; }
+        virtual void toXML(const byte * self, IXmlWriter & out) {}
+        virtual unsigned getVersion() const { return 0; }
+        virtual unsigned getMetaFlags() { return 0; }
+        virtual IOutputMetaData * querySerializedMeta() { return this; }
+
+        virtual void destruct(byte * self) {}
+        virtual IOutputRowSerializer * createRowSerializer(ICodeContext * ctx, unsigned activityId) { return NULL; }
+        virtual IOutputRowDeserializer * createRowDeserializer(ICodeContext * ctx, unsigned activityId) { return NULL; }
+        virtual ISourceRowPrefetcher * createRowPrefetcher(ICodeContext * ctx, unsigned activityId) { return NULL; }
+        virtual void walkIndirectMembers(const byte * self, IIndirectMemberVisitor & visitor) {}
+
+        size32_t minSize;
+        size32_t fixedSize;
+    };
+
+    void testAllocator(IOutputMetaData * meta, bool packed, unsigned low, unsigned high, int modify, bool checking)
+    {
+        CheckingRowAllocatorCache cache;
+        Owned<IRowManager> rm = createRowManager(0, NULL, logctx, &cache);
+        Owned<IEngineRowAllocator> alloc = checking ? createCrcRoxieRowAllocator(*rm, meta, 0, 0, packed) : createRoxieRowAllocator(*rm, meta, 0, 0, packed);
+
+        for (unsigned size=low; size <= high; size++)
+        {
+            unsigned capacity;
+            unsigned prevFailures = cache.numFailures;
+            void * row = alloc->createRow(capacity);
+            if (low != high)
+                row = alloc->resizeRow(size, row, capacity);
+            for (unsigned i1=0; i1 < capacity; i1++)
+                ((byte *)row)[i1] = i1;
+            const void * final = alloc->finalizeRow(capacity, row, capacity);
+            for (unsigned i2=0; i2 < capacity; i2++)
+            {
+                ASSERT(((byte *)row)[i2] == i2);
+            }
+
+            if (modify != 0)
+            {
+                if (modify < 0)
+                    ((byte *)row)[0]++;
+                else
+                    ((byte *)row)[size-1]++;
+            }
+            ReleaseRoxieRow(row);
+            if (modify == 0)
+            {
+                ASSERT(prevFailures == cache.numFailures);
+            }
+            else
+            {
+                ASSERT(prevFailures+1 == cache.numFailures);
+            }
+        }
+    }
+
+    void testAllocator(IOutputMetaData * meta, bool packed, unsigned low, unsigned high)
+    {
+        testAllocator(meta, packed, low, high, 0, false);
+        testAllocator(meta, packed, low, high, 0, true);
+        testAllocator(meta, packed, low, high, -1, true);
+        testAllocator(meta, packed, low, high, +1, true);
+    }
+
+    void testChecking()
+    {
+        Owned<IRowManager> rm = createRowManager(0, NULL, logctx, NULL);
+
+        for (unsigned fixedSize=1; fixedSize<64; fixedSize++)
+        {
+            DummyOutputMeta meta(fixedSize, fixedSize);
+            testAllocator(&meta, false, fixedSize, fixedSize);
+            testAllocator(&meta, true, fixedSize, fixedSize);
+        }
+
+        for (unsigned varSize=1; varSize<64; varSize++)
+        {
+            DummyOutputMeta meta(varSize, 0);
+            testAllocator(&meta, false, varSize, varSize);
+            testAllocator(&meta, false, 1, varSize);
+        }
+    }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( RoxieRowAllocatorTests );
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( RoxieRowAllocatorTests, "RoxieRowAllocatorTests" );
+
+} // namespace roxiemem
+#endif