Przeglądaj źródła

Merge pull request #7961 from afishbeck/publishDfsUpdates

HPCC-14528 Refactor DFS update during publish/add-pkgmap/copy-query

Reviewed-By: Stuart Ort <stuart.ort@lexisnexis.com> 
Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 9 lat temu
rodzic
commit
ae01e4ec40

+ 13 - 13
common/workunit/referencedfilelist.cpp

@@ -167,8 +167,8 @@ public:
             ep.set(NULL);
         return ep;
     }
-    virtual void cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool overwrite, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
-    void cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *user, INode *remote, bool overwrite);
+    virtual void cloneInfo(unsigned updateFlags, IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
+    void cloneSuperInfo(unsigned updateFlags, ReferencedFileList *list, IUserDescriptor *user, INode *remote);
     virtual const char *queryPackageId() const {return pkgid.get();}
     virtual __int64 getFileSize()
     {
@@ -229,12 +229,12 @@ public:
     void addFilesFromPackage(IPropertyTree &package, const char *_daliip, const char *srcCluster, const char *_remotePrefix);
 
     virtual IReferencedFileIterator *getFiles();
-    virtual void cloneFileInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
+    virtual void cloneFileInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
 
     virtual void cloneRelationships();
-    virtual void cloneAllInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
+    virtual void cloneAllInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
     {
-        cloneFileInfo(helper, overwrite, cloneSuperInfo, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
+        cloneFileInfo(updateFlags, helper, cloneSuperInfo, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
         cloneRelationships();
     }
     virtual void resolveFiles(const char *process, const char *remoteIP, const char *_remotePrefix, const char *srcCluster, bool checkLocalFirst, bool addSubFiles, bool trackSubFiles, bool resolveForeign=false);
@@ -415,7 +415,7 @@ void ReferencedFile::resolve(const char *dstCluster, const char *srcCluster, IUs
         resolveLocal(dstCluster, srcCluster, user, subfiles);
 }
 
-void ReferencedFile::cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool overwrite, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
+void ReferencedFile::cloneInfo(unsigned updateFlags, IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
 {
     if ((flags & RefFileCloned) || (flags & RefFileSuper) || (flags & RefFileInPackage))
         return;
@@ -432,7 +432,7 @@ void ReferencedFile::cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const
         if (filePrefix.length())
             srcLFN.append(filePrefix.str()).append("::");
         srcLFN.append(logicalName.str());
-        helper->cloneRoxieSubFile(srcLFN, srcCluster, logicalName, dstCluster, filePrefix, redundancy, channelsPerNode, replicateOffset, defReplicateFolder, user, daliip, overwrite);
+        helper->cloneRoxieSubFile(srcLFN, srcCluster, logicalName, dstCluster, filePrefix, redundancy, channelsPerNode, replicateOffset, defReplicateFolder, user, daliip, updateFlags);
         flags |= RefFileCloned;
     }
     catch (IException *e)
@@ -448,7 +448,7 @@ void ReferencedFile::cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const
     }
 }
 
-void ReferencedFile::cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *user, INode *remote, bool overwrite)
+void ReferencedFile::cloneSuperInfo(unsigned updateFlags, ReferencedFileList *list, IUserDescriptor *user, INode *remote)
 {
     if ((flags & RefFileCloned) || (flags & RefFileInPackage) || !(flags & RefFileSuper) || !(flags & RefFileRemote))
         return;
@@ -463,7 +463,7 @@ void ReferencedFile::cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *u
         Owned<IDistributedFile> df = dir.lookup(logicalName.str(), user);
         if(df)
         {
-            if (!overwrite)
+            if (!(updateFlags & DALI_UPDATEF_SUPERFILES))
                 return;
             df->detach();
             df.clear();
@@ -480,7 +480,7 @@ void ReferencedFile::cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *u
                 //ensure superfile in superfile is cloned, before add
                 ReferencedFile *subref = list->map.getValue(name);
                 if (subref)
-                    subref->cloneSuperInfo(list, user, remote, overwrite);
+                    subref->cloneSuperInfo(updateFlags, list, user, remote);
             }
             if (name && *name)
                 superfile->addSubFile(name, false, NULL, false);
@@ -713,14 +713,14 @@ void ReferencedFileList::resolveFiles(const char *_process, const char *remoteIP
         resolveSubFiles(subfiles, checkLocalFirst, trackSubFiles, resolveForeign);
 }
 
-void ReferencedFileList::cloneFileInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
+void ReferencedFileList::cloneFileInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
 {
     ReferencedFileIterator files(this);
     ForEach(files)
-        files.queryObject().cloneInfo(helper, user, process, srcCluster, overwrite, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
+        files.queryObject().cloneInfo(updateFlags, helper, user, process, srcCluster, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
     if (cloneSuperInfo)
         ForEach(files)
-            files.queryObject().cloneSuperInfo(this, user, remote, overwrite);
+            files.queryObject().cloneSuperInfo(updateFlags, this, user, remote);
 }
 
 void ReferencedFileList::cloneRelationships()

+ 2 - 2
common/workunit/referencedfilelist.hpp

@@ -61,8 +61,8 @@ interface IReferencedFileList : extends IInterface
 
     virtual IReferencedFileIterator *getFiles()=0;
     virtual void resolveFiles(const char *process, const char *remoteIP, const char * remotePrefix, const char *srcCluster, bool checkLocalFirst, bool addSubFiles, bool trackSubFiles, bool resolveForeign=false)=0;
-    virtual void cloneAllInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
-    virtual void cloneFileInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
+    virtual void cloneAllInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
+    virtual void cloneFileInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
     virtual void cloneRelationships()=0;
 };
 

+ 199 - 20
dali/dfu/dfuutil.cpp

@@ -139,13 +139,13 @@ public:
     StringAttr cluster2;
     Owned<IGroup> grp2;
     ClusterPartDiskMapSpec spec2;
-    bool overwrite;
+    unsigned overwriteFlags;
     IDistributedFileDirectory *fdir;
     bool repeattlk;
     bool copyphysical;
     unsigned level;
 
-
+    CFileCloner() : overwriteFlags(0) {}
 
     void physicalCopyFile(IFileDescriptor *srcdesc,IFileDescriptor *dstdesc)
     {
@@ -405,21 +405,22 @@ public:
             throw afor2.exc.getClear();
     }
 
-    void updateCloneFrom(IFileDescriptor *srcfdesc, IFileDescriptor *dstfdesc, INode *srcdali, const char *srcCluster)
+    void updateCloneFrom(const char *lfn, IPropertyTree &attrs, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
     {
+        DBGLOG("updateCloneFrom %s", lfn);
         if (!srcdali || srcdali->endpoint().isNull())
-            dstfdesc->queryProperties().setProp("@cloneFromPeerCluster", srcCluster);
+            attrs.setProp("@cloneFromPeerCluster", srcCluster);
         else
         {
             StringBuffer s;
-            dstfdesc->queryProperties().setProp("@cloneFrom", srcdali->endpoint().getUrlStr(s).str());
-            dstfdesc->queryProperties().setProp("@cloneFromDir", srcfdesc->queryDefaultDir());
+            attrs.setProp("@cloneFrom", srcdali->endpoint().getUrlStr(s).str());
+            attrs.setProp("@cloneFromDir", srcfdesc->queryDefaultDir());
             if (srcCluster && *srcCluster) //where to copy from has been explicity set to a remote location, don't copy from local sources
-                dstfdesc->queryProperties().setProp("@cloneFromPeerCluster", "-");
+                attrs.setProp("@cloneFromPeerCluster", "-");
             if (prefix.length())
-                dstfdesc->queryProperties().setProp("@cloneFromPrefix", prefix.get());
+                attrs.setProp("@cloneFromPrefix", prefix.get());
 
-            while(dstfdesc->queryProperties().removeProp("cloneFromGroup"));
+            while(attrs.removeProp("cloneFromGroup"));
 
             unsigned numClusters = srcfdesc->numClusters();
             for (unsigned clusterNum = 0; clusterNum < numClusters; clusterNum++)
@@ -432,13 +433,25 @@ public:
                 groupInfo->setProp("@groupName", sourceGroup);
                 ClusterPartDiskMapSpec &spec = srcfdesc->queryPartDiskMapping(clusterNum);
                 spec.toProp(groupInfo);
-                dstfdesc->queryProperties().addPropTree("cloneFromGroup", groupInfo.getClear());
+                attrs.addPropTree("cloneFromGroup", groupInfo.getClear());
             }
         }
     }
+    void updateCloneFrom(IDistributedFile *dfile, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
+    {
+        DistributedFilePropertyLock lock(dfile);
+        IPropertyTree &attrs = lock.queryAttributes();
+        updateCloneFrom(dfile->queryLogicalName(), attrs, srcfdesc, srcdali, srcCluster);
+    }
+    void updateCloneFrom(const char *lfn, IFileDescriptor *dstfdesc, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
+    {
+        updateCloneFrom(lfn, dstfdesc->queryProperties(), srcfdesc, srcdali, srcCluster);
+    }
 
     void cloneSubFile(IPropertyTree *ftree,const char *destfilename, INode *srcdali, const char *srcCluster)   // name already has prefix added
     {
+        DBGLOG("cloneSubFile %s", destfilename);
+
         Owned<IFileDescriptor> srcfdesc = deserializeFileDescriptorTree(ftree, NULL, 0);
         const char * kind = srcfdesc->queryProperties().queryProp("@kind");
         bool iskey = kind&&(strcmp(kind,"key")==0);
@@ -481,16 +494,39 @@ public:
         }
 
         if (copyphysical) {
+            DBGLOG("copyphysical dst=%s", destfilename);
             physicalCopyFile(srcfdesc,dstfdesc);
             physicalReplicateFile(dstfdesc,destfilename);
         }
 
-        updateCloneFrom(srcfdesc, dstfdesc, srcdali, srcCluster);
+        updateCloneFrom(destfilename, dstfdesc, srcfdesc, srcdali, srcCluster);
 
         Owned<IDistributedFile> dstfile = fdir->createNew(dstfdesc);
         dstfile->attach(destfilename,userdesc);
     }
 
+    void addCluster(IPropertyTree *ftree,const char *destfilename)
+    {
+        CDfsLogicalFileName dstlfn;
+        if (!dstlfn.setValidate(destfilename,true))
+            throw MakeStringException(-1,"Logical name %s invalid",destfilename);
+        Owned<IDistributedFile> dfile = fdir->lookup(dstlfn,userdesc,true);
+        if (dfile) {
+            ClusterPartDiskMapSpec spec = spec1;
+            const char * kind = ftree->queryProp("Attr/@kind");
+            bool iskey = kind&&(strcmp(kind,"key")==0);
+            if (iskey&&repeattlk)
+                spec.setRepeatedCopies(CPDMSRP_lastRepeated,false);
+            dfile->addCluster(cluster1,spec);
+            if (iskey&&!cluster2.isEmpty())
+                dfile->addCluster(cluster2,spec2);
+            if (copyphysical) {
+                Owned<IFileDescriptor> fdesc=dfile->getFileDescriptor();
+                physicalReplicateFile(fdesc,destfilename);
+            }
+        }
+    }
+
     void extendSubFile(IPropertyTree *ftree,const char *destfilename)
     {
         CDfsLogicalFileName dstlfn;
@@ -532,7 +568,7 @@ public:
             foreignuserdesc.set(_foreignuserdesc);
         else if (_userdesc)
             foreignuserdesc.set(_userdesc);
-        overwrite = _overwrite;
+        overwriteFlags = _overwrite ? DALI_UPDATEF_REPLACE_FILE : 0;
         copyphysical = _copyphysical;
         nameprefix.set(_nameprefix);
         repeattlk = _repeattlk;
@@ -576,7 +612,7 @@ public:
                 spec2.setDefaultBaseDir(defdir2.str());
         }
     }
-
+    inline bool checkOverwrite(unsigned flag) { return (overwriteFlags & flag)!=0; }
     void cloneSuperFile(const char *filename, CDfsLogicalFileName &dlfn)
     {
         level++;
@@ -633,7 +669,7 @@ public:
         // first see if target exists (and remove if does and overwrite specified)
         Owned<IDistributedFile> dfile = fdir->lookup(dlfn,userdesc,true);
         if (dfile) {
-            if (!overwrite)
+            if (!checkOverwrite(DALI_UPDATEF_REPLACE_FILE))
                 throw MakeStringException(-1,"Destination file %s already exists",dlfn.get());
             dfile->detach();
             dfile.clear();
@@ -709,7 +745,7 @@ public:
         // first see if target exists (and remove if does and overwrite specified)
         Owned<IDistributedFile> dfile = fdir->lookup(dlfn,userdesc,true);
         if (dfile) {
-            if (!overwrite)
+            if (!checkOverwrite(DALI_UPDATEF_REPLACE_FILE))
                 throw MakeStringException(-1,"Destination file %s already exists",dlfn.get());
 
             IPropertyTree &attloc = dfile->queryAttributes();
@@ -719,7 +755,7 @@ public:
             {
                 Owned<IFileDescriptor> dstfdesc=dfile->getFileDescriptor();
                 Owned<IFileDescriptor> srcfdesc = deserializeFileDescriptorTree(ftree, NULL, 0);
-                updateCloneFrom(srcfdesc, dstfdesc, srcdali, srcCluster);
+                updateCloneFrom(filename, dstfdesc, srcfdesc, srcdali, srcCluster);
                 return;
             }
 
@@ -730,6 +766,148 @@ public:
         level--;
     }
 
+    inline const char *getDaliEndPointStr(INode *daliNode, StringBuffer &s)
+    {
+        if (!daliNode)
+            return "(local)";
+        return daliNode->endpoint().getUrlStr(s).str();
+    }
+    inline bool checkHasCluster(IDistributedFile *dfile)
+    {
+        StringArray clusterNames;
+        dfile->getClusterNames(clusterNames);
+        return clusterNames.find(cluster1)!=NotFound;
+    }
+    inline bool checkIsKey(IPropertyTree *ftree)
+    {
+        const char * kind = ftree->queryProp("Attr/@kind");
+        return kind && streq(kind, "key");
+    }
+    void addCluster(IDistributedFile *dfile, IPropertyTree *srcFtree)
+    {
+        if (dfile)
+        {
+            ClusterPartDiskMapSpec spec = spec1;
+            if (checkIsKey(srcFtree) && repeattlk)
+                spec.setRepeatedCopies(CPDMSRP_lastRepeated, false);
+            DBGLOG("addCluster %s to file %s", cluster1.str(), dfile->queryLogicalName());
+            dfile->addCluster(cluster1, spec);
+        }
+    }
+    bool checkFileChanged(IDistributedFile *dfile, IPropertyTree *srcftree, IPropertyTree *srcAttrs)
+    {
+        IPropertyTree &dstAttrs = dfile->queryAttributes();
+        return (dfile->numParts() != (unsigned) srcftree->getPropInt("@numparts") ||
+                srcAttrs->getPropInt("@eclCRC") != dstAttrs.getPropInt("@eclCRC") ||
+                srcAttrs->getPropInt("@totalCRC") != dstAttrs.getPropInt64("@totalCRC"));
+    }
+    inline bool checkValueChanged(const char *s1, const char *s2)
+    {
+        if (s1 && s2)
+            return !streq(s1, s2);
+        return ((s1 && *s1) || (s2 && *s2));
+    }
+    bool checkCloneFromChanged(IDistributedFile *dfile, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
+    {
+        if (!srcdali || srcdali->endpoint().isNull())
+        {
+            return checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromPeerCluster"), srcCluster);
+        }
+        else
+        {
+            StringBuffer s;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFrom"), srcdali->endpoint().getUrlStr(s).str()))
+                return true;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromDir"), srcfdesc->queryDefaultDir()))
+                return true;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromPeerCluster"), (srcCluster && *srcCluster) ? "-" : NULL))
+                return true;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromPrefix"), prefix.get()))
+                return true;
+
+            unsigned groupCount = dfile->queryAttributes().getCount("cloneFromGroup");
+            if (srcCluster && *srcCluster && groupCount!=1)
+                return true;
+
+            unsigned numSrcClusters = srcfdesc->numClusters();
+            if (numSrcClusters != groupCount)
+                return true;
+            StringArray clusterNames;
+            dfile->getClusterNames(clusterNames);
+            for (unsigned clusterNum = 0; clusterNum < numSrcClusters; clusterNum++)
+            {
+                StringBuffer sourceGroup;
+                srcfdesc->getClusterGroupName(clusterNum, sourceGroup, NULL);
+                if (!clusterNames.contains(sourceGroup.str()))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    void cloneRoxieFile(const char *filename, const char *destfilename)
+    {
+        Linked<INode> srcdali = foreigndalinode;
+        CDfsLogicalFileName srcLFN;
+        srcLFN.set(filename);
+        if (srcLFN.isForeign())
+        {
+            SocketEndpoint ep;
+            srcLFN.getEp(ep);
+            srcLFN.clearForeign();
+            srcdali.setown(createINode(ep));
+        }
+        StringBuffer s;
+        Owned<IPropertyTree> ftree = fdir->getFileTree(srcLFN.get(), foreignuserdesc, srcdali, FOREIGN_DALI_TIMEOUT, false);
+        if (!ftree.get())
+            throw MakeStringException(-1,"Source file %s could not be found in Dali %s",srcLFN.get(), getDaliEndPointStr(srcdali, s));
+        IPropertyTree *attsrc = ftree->queryPropTree("Attr");
+        if (!attsrc)
+            throw MakeStringException(-1,"Attributes for source file %s could not be found in Dali %s",srcLFN.get(), getDaliEndPointStr(srcdali, s));
+
+        CDfsLogicalFileName dlfn;
+        dlfn.set(destfilename);
+        if (!streq(ftree->queryName(),queryDfsXmlBranchName(DXB_File)))
+            throw MakeStringException(-1,"Source file %s in Dali %s is not a simple file",filename, getDaliEndPointStr(srcdali, s));
+
+        if (!srcdali.get()||queryCoven().inCoven(srcdali))
+        {
+            // if dali is local and filenames same
+            if (streq(srcLFN.get(), dlfn.get()))
+            {
+                extendSubFile(ftree,dlfn.get());
+                return;
+            }
+        }
+
+        //see if target already exists
+        Owned<IDistributedFile> dfile = fdir->lookup(dlfn, userdesc, true);
+        if (dfile)
+        {
+            if (!checkOverwrite(DALI_UPDATEF_SUBFILE_MASK))
+                throw MakeStringException(-1, "Destination file %s already exists", dlfn.get());
+
+            if (checkOverwrite(DALI_UPDATEF_REPLACE_FILE) && checkFileChanged(dfile, ftree, attsrc)) //complete overwrite
+            {
+                DBGLOG("replacing file %s", destfilename);
+                dfile->detach();
+                dfile.clear();
+            }
+            else
+            {
+                if (checkOverwrite(DALI_UPDATEF_APPEND_CLUSTER) && !checkHasCluster(dfile))
+                    addCluster(dfile, ftree);
+                if (checkOverwrite(DALI_UPDATEF_CLONE_FROM))
+                {
+                    Owned<IFileDescriptor> srcfdesc = deserializeFileDescriptorTree(ftree, NULL, 0);
+                    if (checkCloneFromChanged(dfile, srcfdesc, srcdali, srcCluster))
+                        updateCloneFrom(dfile, srcfdesc, srcdali, srcCluster);
+                }
+                return;
+            }
+        }
+        cloneSubFile(ftree,dlfn.get(),srcdali, srcCluster);
+    }
 
 };
 
@@ -1052,18 +1230,19 @@ public:
                          const char *defReplicateFolder,
                          IUserDescriptor *userdesc,                // user desc for local dali
                          const char *foreigndali,                  // can be omitted if srcname foreign or local
-                         bool overwrite                            // overwrite destination if exists
+                         unsigned overwriteFlags                            // overwrite destination if exists
                          )
     {
-        DBGLOG("cloneRoxieSubFile src=%s@%s, dst=%s@%s, prefix=%s, ow=%d, docopy=false", srcLFN, srcCluster, dstLFN, dstCluster, prefix, overwrite);
+        DBGLOG("cloneRoxieSubFile src=%s@%s, dst=%s@%s, prefix=%s, ow=%d, docopy=false", srcLFN, srcCluster, dstLFN, dstCluster, prefix, overwriteFlags);
         CFileCloner cloner;
-        cloner.init(dstCluster, DFUcpdm_c_replicated_by_d, true, NULL, userdesc, foreigndali, NULL, NULL, overwrite, false);
+        cloner.init(dstCluster, DFUcpdm_c_replicated_by_d, true, NULL, userdesc, foreigndali, NULL, NULL, false, false);
+        cloner.overwriteFlags = overwriteFlags;
         cloner.spec1.setRoxie(redundancy, channelsPerNode, replicateOffset);
         if (defReplicateFolder)
             cloner.spec1.setDefaultReplicateDir(defReplicateFolder);
         cloner.srcCluster.set(srcCluster);
         cloner.prefix.set(prefix);
-        cloner.cloneFile(srcLFN, dstLFN);
+        cloner.cloneRoxieFile(srcLFN, dstLFN);
     }
 
 

+ 9 - 1
dali/dfu/dfuutil.hpp

@@ -29,6 +29,14 @@ interface IDfuFileCopier: extends IInterface
     virtual bool wait()=0; // waits for all outstanding copies to complete
 };
 
+#define DALI_UPDATEF_REPLACE_FILE   0x0001
+#define DALI_UPDATEF_CLONE_FROM     0x0002
+#define DALI_UPDATEF_APPEND_CLUSTER 0x0004
+#define DALI_UPDATEF_SUPERFILES     0x0008
+#define DALI_UPDATEF_PACKAGEMAP     0x0100
+
+#define DALI_UPDATEF_SUBFILE_MASK (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_APPEND_CLUSTER)
+#define DALI_UPDATEF_MASK (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_APPEND_CLUSTER | DALI_UPDATEF_SUPERFILES | DALI_UPDATEF_PACKAGEMAP)
 
 interface IDFUhelper: extends IInterface
 {
@@ -82,7 +90,7 @@ interface IDFUhelper: extends IInterface
                          const char *defReplicateFolder,
                          IUserDescriptor *userdesc,                // user desc for local dali
                          const char *foreigndali,                  // can be omitted if srcname foreign or local
-                         bool overwrite                            // overwrite destination if exists
+                         unsigned overwriteFlags                   // overwrite destination options
                          ) = 0;
 
     virtual void cloneFileRelationships(

+ 32 - 11
ecl/ecl-package/ecl-package.cpp

@@ -481,7 +481,8 @@ private:
 class EclCmdPackageAdd : public EclCmdCommon
 {
 public:
-    EclCmdPackageAdd() : optActivate(false), optOverWrite(false), optGlobalScope(false), optAllowForeign(false), optPreloadAll(false)
+    EclCmdPackageAdd() : optActivate(false), optOverWrite(false), optGlobalScope(false), optAllowForeign(false), optPreloadAll(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false), optReplacePackagemap(false)
     {
     }
     virtual bool parseCommandLineOptions(ArgvIterator &iter)
@@ -515,6 +516,14 @@ public:
                 continue;
             if (iter.matchFlag(optOverWrite, ECLOPT_OVERWRITE)||iter.matchFlag(optOverWrite, ECLOPT_OVERWRITE_S))
                 continue;
+            if (iter.matchFlag(optReplacePackagemap, ECLOPT_REPLACE))
+                continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (iter.matchFlag(optGlobalScope, ECLOPT_GLOBAL_SCOPE))
                 continue;
             if (iter.matchFlag(optAllowForeign, ECLOPT_ALLOW_FOREIGN))
@@ -577,6 +586,10 @@ public:
         request->setSourceProcess(optSourceProcess);
         request->setAllowForeignFiles(optAllowForeign);
         request->setPreloadAllPackages(optPreloadAll);
+        request->setReplacePackageMap(optReplacePackagemap);
+        request->setUpdateSuperFiles(optUpdateSuperfiles);
+        request->setUpdateCloneFrom(optUpdateCloneFrom);
+        request->setAppendCluster(!optDontAppendCluster);
 
         Owned<IClientAddPackageResponse> resp = packageProcessClient->AddPackage(request);
         if (resp->getExceptions().ordinality())
@@ -601,17 +614,21 @@ public:
                     "The 'add' command will add the package map information to dali \n"
                     "\n"
                     "ecl packagemap add [options] <target> <filename>\n"
+                    "   <target>                 Name of target to use when adding package map information\n"
+                    "   <filename>               Name of file containing package map information\n"
                     " Options:\n"
-                    "   -O, --overwrite             Overwrite existing information\n"
-                    "   -A, --activate              Activate the package information\n"
-                    "   --daliip=<ip>               IP of the remote dali to use for logical file lookups\n"
-                    "   --pmid                      Identifier of package map - defaults to filename if not specified\n"
-                    "   --global-scope              The specified packagemap can be shared across multiple targets\n"
-                    "   --source-process            Process cluster to copy files from\n"
-                    "   --allow-foreign             Do not fail if foreign files are used in packagemap\n"
-                    "   --preload-all               Set preload files option for all packages\n"
-                    "   <target>                    Name of target to use when adding package map information\n"
-                    "   <filename>                  Name of file containing package map information\n",
+                    "   -O, --overwrite          Replace existing packagemap and file information (dangerous)\n"
+                    "   -A, --activate           Activate the package information\n"
+                    "   --daliip=<ip>            IP of the remote dali to use for logical file lookups\n"
+                    "   --pmid                   Identifier of package map - defaults to filename if not specified\n"
+                    "   --global-scope           The specified packagemap can be shared across multiple targets\n"
+                    "   --source-process         Process cluster to copy files from\n"
+                    "   --allow-foreign          Do not fail if foreign files are used in packagemap\n"
+                    "   --preload-all            Set preload files option for all packages\n"
+                    "   --replace                Replace existing packagmap"
+                    "   --update-super-files     Update local DFS super-files if remote DALI has changed\n"
+                    "   --update-clone-from      Update local clone from location if remote DALI has changed\n"
+                    "   --dont-append-cluster    Only use to avoid locking issues due to adding cluster to file\n",
                     stdout);
 
         EclCmdCommon::usage();
@@ -626,6 +643,10 @@ private:
     StringAttr optSourceProcess;
     bool optActivate;
     bool optOverWrite;
+    bool optReplacePackagemap;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
     bool optGlobalScope;
     bool optAllowForeign;
     bool optPreloadAll;

+ 4 - 0
ecl/eclcmd/eclcmd_common.hpp

@@ -61,6 +61,7 @@ typedef IEclCommand *(*EclCommandFactory)(const char *cmdname);
 
 #define ECLOPT_NORELOAD "--no-reload"
 #define ECLOPT_OVERWRITE "--overwrite"
+#define ECLOPT_REPLACE "--replace"
 #define ECLOPT_OVERWRITE_S "-O"
 #define ECLOPT_OVERWRITE_INI "overwriteDefault"
 #define ECLOPT_OVERWRITE_ENV NULL
@@ -95,6 +96,9 @@ typedef IEclCommand *(*EclCommandFactory)(const char *cmdname);
 #define ECLOPT_CHECK_ALL_NODES "--check-all-nodes"
 #define ECLOPT_CHECK_ALL_NODES_INI "checkAllNodes"
 #define ECLOPT_CHECK_ALL_NODES_ENV "CHECK_ALL_NODES"
+#define ECLOPT_UPDATE_SUPER_FILES "--update-super-files"
+#define ECLOPT_UPDATE_CLONE_FROM "--update-clone-from"
+#define ECLOPT_DONT_APPEND_CLUSTER "--dont-append-cluster"
 
 #define ECLOPT_MAIN "--main"
 #define ECLOPT_MAIN_S "-main"  //eclcc compatible format

+ 18 - 2
ecl/eclcmd/eclcmd_core.cpp

@@ -280,7 +280,8 @@ class EclCmdPublish : public EclCmdWithEclTarget
 {
 public:
     EclCmdPublish() : optNoActivate(false), optSuspendPrevious(false), optDeletePrevious(false),
-        activateSet(false), optNoReload(false), optDontCopyFiles(false), optMsToWait(10000), optAllowForeign(false), optUpdateDfs(false)
+        activateSet(false), optNoReload(false), optDontCopyFiles(false), optMsToWait(10000), optAllowForeign(false), optUpdateDfs(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false)
     {
         optObj.accept = eclObjWuid | eclObjArchive | eclObjSharedObject;
         optTimeLimit = (unsigned) -1;
@@ -337,6 +338,12 @@ public:
                 continue;
             if (iter.matchFlag(optUpdateDfs, ECLOPT_UPDATE_DFS))
                 continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (EclCmdWithEclTarget::matchCommandLineOption(iter, true)!=EclCmdOptionMatch)
                 return false;
         }
@@ -413,6 +420,9 @@ public:
         req->setDontCopyFiles(optDontCopyFiles);
         req->setAllowForeignFiles(optAllowForeign);
         req->setUpdateDfs(optUpdateDfs);
+        req->setUpdateSuperFiles(optUpdateSuperfiles);
+        req->setUpdateCloneFrom(optUpdateCloneFrom);
+        req->setAppendCluster(!optDontAppendCluster);
 
         if (optTimeLimit != (unsigned) -1)
             req->setTimeLimit(optTimeLimit);
@@ -470,7 +480,10 @@ public:
             "   --no-files             Do not copy DFS file information for referenced files\n"
             "   --allow-foreign        Do not fail if foreign files are used in query (roxie)\n"
             "   --daliip=<IP>          The IP of the DALI to be used to locate remote files\n"
-            "   --update-dfs           Update local DFS info if remote DALI has changed\n"
+            "   -O, --overwrite        Completely replace existing DFS file information (dangerous)\n"
+            "   --update-super-files   Update local DFS super-files if remote DALI has changed\n"
+            "   --update-clone-from    Update local clone from location if remote DALI has changed\n"
+            "   --dont-append-cluster  Only use to avoid locking issues due to adding cluster to file\n"
             "   --source-process       Process cluster to copy files from\n"
             "   --timeLimit=<ms>       Value to set for query timeLimit configuration\n"
             "   --warnTimeLimit=<ms>   Value to set for query warnTimeLimit configuration\n"
@@ -501,6 +514,9 @@ private:
     bool optDeletePrevious;
     bool optAllowForeign;
     bool optUpdateDfs;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
 };
 
 class EclCmdRun : public EclCmdWithEclTarget

+ 42 - 4
ecl/eclcmd/queries/ecl-queries.cpp

@@ -406,7 +406,8 @@ private:
 class EclCmdQueriesCopy : public EclCmdCommon
 {
 public:
-    EclCmdQueriesCopy() : optActivate(false), optNoReload(false), optMsToWait(10000), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false)
+    EclCmdQueriesCopy() : optActivate(false), optNoReload(false), optMsToWait(10000), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false)
     {
         optTimeLimit = (unsigned) -1;
         optWarnTimeLimit = (unsigned) -1;
@@ -462,6 +463,18 @@ public:
                 continue;
             if (iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE)||iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE_S))
                 continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (iter.matchOption(optName, ECLOPT_NAME)||iter.matchOption(optName, ECLOPT_NAME_S))
                 continue;
             if (EclCmdCommon::matchCommandLineOption(iter, true)!=EclCmdOptionMatch)
@@ -503,6 +516,9 @@ public:
         req->setSourceProcess(optSourceProcess);
         req->setActivate(optActivate);
         req->setOverwrite(optOverwrite);
+        req->setUpdateSuperFiles(optUpdateSuperfiles);
+        req->setUpdateCloneFrom(optUpdateCloneFrom);
+        req->setAppendCluster(!optDontAppendCluster);
         req->setDontCopyFiles(optDontCopyFiles);
         req->setWait(optMsToWait);
         req->setNoReload(optNoReload);
@@ -554,7 +570,10 @@ public:
             "   --source-process       Process cluster to copy files from\n"
             "   -A, --activate         Activate the new query\n"
             "   --no-reload            Do not request a reload of the (roxie) cluster\n"
-            "   -O, --overwrite        Overwrite existing files\n"
+            "   -O, --overwrite        Completely replace existing DFS file information (dangerous)\n"
+            "   --update-super-files   Update local DFS super-files if remote DALI has changed\n"
+            "   --update-clone-from    Update local clone from location if remote DALI has changed\n"
+            "   --dont-append-cluster  Only use to avoid locking issues due to adding cluster to file\n"
             "   --allow-foreign        Do not fail if foreign files are used in query (roxie)\n"
             "   --wait=<ms>            Max time to wait in milliseconds\n"
             "   --timeLimit=<sec>      Value to set for query timeLimit configuration\n"
@@ -585,6 +604,9 @@ private:
     bool optActivate;
     bool optNoReload;
     bool optOverwrite;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
     bool optDontCopyFiles;
     bool optAllowForeign;
 };
@@ -592,7 +614,8 @@ private:
 class EclCmdQueriesCopyQueryset : public EclCmdCommon
 {
 public:
-    EclCmdQueriesCopyQueryset() : optCloneActiveState(false), optAllQueries(false), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false)
+    EclCmdQueriesCopyQueryset() : optCloneActiveState(false), optAllQueries(false), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false)
     {
     }
     virtual bool parseCommandLineOptions(ArgvIterator &iter)
@@ -630,6 +653,12 @@ public:
                 continue;
             if (iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE)||iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE_S))
                 continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (EclCmdCommon::matchCommandLineOption(iter, true)!=EclCmdOptionMatch)
                 return false;
         }
@@ -658,6 +687,9 @@ public:
         req->setSourceProcess(optSourceProcess);
         req->setCloneActiveState(optCloneActiveState);
         req->setOverwriteDfs(optOverwrite);
+        req->setUpdateSuperFiles(optUpdateSuperfiles);
+        req->setUpdateCloneFrom(optUpdateCloneFrom);
+        req->setAppendCluster(!optDontAppendCluster);
         req->setCopyFiles(!optDontCopyFiles);
         req->setAllowForeignFiles(optAllowForeign);
 
@@ -708,7 +740,10 @@ public:
             "   --daliip=<ip>          Remote Dali DFS to use for copying file information\n"
             "   --source-process       Process cluster to copy files from\n"
             "   --clone-active-state   Make copied queries active if active on source\n"
-            "   -O, --overwrite        Overwrite existing DFS file information\n"
+            "   -O, --overwrite        Completely replace existing DFS file information (dangerous)\n"
+            "   --update-super-files   Update local DFS super-files if remote DALI has changed\n"
+            "   --update-clone-from    Update local clone from location if remote DALI has changed\n"
+            "   --dont-append-cluster  Only use to avoid locking issues due to adding cluster to file\n"
             "   --allow-foreign        Do not fail if foreign files are used in query (roxie)\n"
             " Common Options:\n",
             stdout);
@@ -721,6 +756,9 @@ private:
     StringAttr optSourceProcess;
     bool optCloneActiveState;
     bool optOverwrite;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
     bool optDontCopyFiles;
     bool optAllowForeign;
     bool optAllQueries;

+ 5 - 1
esp/scm/ws_packageprocess.ecm

@@ -29,7 +29,7 @@ ESPrequest AddPackageRequest
 {
     string Info;
     boolean Activate;
-    boolean OverWrite;
+    boolean OverWrite; //use flags below unless you really want to overwrite the actual file metadata and packagemap
     string Target;
     string PackageMap;
     string Process;
@@ -38,6 +38,10 @@ ESPrequest AddPackageRequest
     string SourceProcess;
     bool AllowForeignFiles(true);
     [min_ver("1.02")] bool PreloadAllPackages(false);
+    bool ReplacePackageMap(false);
+    bool UpdateSuperFiles(false); //usually wouldn't be needed, packagemap referencing superfiles?
+    bool UpdateCloneFrom(false); //explicitly wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 

+ 9 - 0
esp/scm/ws_workunits.ecm

@@ -1215,6 +1215,9 @@ ESPrequest [nil_remove] WUPublishWorkunitRequest
     string SourceProcess;
     bool AllowForeignFiles(false);
     bool UpdateDfs(false);
+    bool UpdateSuperFiles(false); //update content of superfiles if changed
+    bool UpdateCloneFrom(false); //explicity wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 ESPresponse [exceptions_inline] WUPublishWorkunitResponse
@@ -1579,6 +1582,9 @@ ESPrequest [nil_remove] WUQuerySetCopyQueryRequest
     string SourceProcess;
     string DestName;
     bool AllowForeignFiles(true);
+    bool UpdateSuperFiles(false); //usually wouldn't be needed, packagemap referencing superfiles?
+    bool UpdateCloneFrom(false); //explicity wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 ESPresponse [exceptions_inline] WUQuerySetCopyQueryResponse
@@ -1598,6 +1604,9 @@ ESPrequest [nil_remove] WUCopyQuerySetRequest
     bool CopyFiles(true);
     bool OverwriteDfs(false);
     string SourceProcess;
+    bool UpdateSuperFiles(false); //usually wouldn't be needed, packagemap referencing superfiles?
+    bool UpdateCloneFrom(false); //explicity wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 ESPresponse [exceptions_inline] WUCopyQuerySetResponse

+ 24 - 11
esp/services/ws_packageprocess/ws_packageprocessService.cpp

@@ -124,7 +124,7 @@ bool isFileKnownOnCluster(const char *logicalname, const char *target, IUserDesc
     return isFileKnownOnCluster(logicalname, clusterInfo, userdesc);
 }
 
-void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, IConstWUClusterInfo *dstInfo, const char *srcCluster, const char *remotePrefix, bool overWrite, IUserDescriptor* userdesc, bool allowForeignFiles)
+void cloneFileInfoToDali(unsigned updateFlags, StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, IConstWUClusterInfo *dstInfo, const char *srcCluster, const char *remotePrefix, IUserDescriptor* userdesc, bool allowForeignFiles)
 {
     StringBuffer user;
     StringBuffer password;
@@ -139,13 +139,13 @@ void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const
     wufiles->addFilesFromPackageMap(packageMap);
     SCMStringBuffer processName;
     dstInfo->getRoxieProcess(processName);
-    wufiles->resolveFiles(processName.str(), lookupDaliIp, remotePrefix, srcCluster, !overWrite, false, false);
+    wufiles->resolveFiles(processName.str(), lookupDaliIp, remotePrefix, srcCluster, !(updateFlags & (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM)), false, false);
 
     StringBuffer defReplicateFolder;
     getConfigurationDirectory(NULL, "data2", "roxie", processName.str(), defReplicateFolder);
 
     Owned<IDFUhelper> helper = createIDFUhelper();
-    wufiles->cloneAllInfo(helper, overWrite, true, false, dstInfo->getRoxieRedundancy(), dstInfo->getChannelsPerNode(), dstInfo->getRoxieReplicateOffset(), defReplicateFolder);
+    wufiles->cloneAllInfo(updateFlags, helper, true, false, dstInfo->getRoxieRedundancy(), dstInfo->getChannelsPerNode(), dstInfo->getRoxieReplicateOffset(), defReplicateFolder);
 
     Owned<IReferencedFileIterator> iter = wufiles->getFiles();
     ForEach(*iter)
@@ -156,13 +156,13 @@ void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const
     }
 }
 
-void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, const char *dstCluster, const char *srcCluster, const char *prefix, bool overWrite, IUserDescriptor* userdesc, bool allowForeignFiles)
+void cloneFileInfoToDali(unsigned updateFlags, StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, const char *dstCluster, const char *srcCluster, const char *prefix, IUserDescriptor* userdesc, bool allowForeignFiles)
 {
     Owned<IConstWUClusterInfo> clusterInfo = getTargetClusterInfo(dstCluster);
     if (!clusterInfo)
         throw MakeStringException(PKG_TARGET_NOT_DEFINED, "Could not find information about target cluster %s ", dstCluster);
 
-    cloneFileInfoToDali(notFound, packageMap, lookupDaliIp, clusterInfo, srcCluster, prefix, overWrite, userdesc, allowForeignFiles);
+    cloneFileInfoToDali(updateFlags, notFound, packageMap, lookupDaliIp, clusterInfo, srcCluster, prefix, userdesc, allowForeignFiles);
 }
 
 void makePackageActive(IPropertyTree *pkgSet, IPropertyTree *psEntryNew, const char *target, bool activate)
@@ -184,7 +184,7 @@ void makePackageActive(IPropertyTree *pkgSet, IPropertyTree *psEntryNew, const c
 
 //////////////////////////////////////////////////////////
 
-void addPackageMapInfo(const char *xml, StringArray &filesNotFound, const char *process, const char *target, const char *pmid, const char *packageSetName, const char *lookupDaliIp, const char *srcCluster, const char *prefix, bool activate, bool overWrite, IUserDescriptor* userdesc, bool allowForeignFiles, bool preloadAll)
+void addPackageMapInfo(unsigned updateFlags, const char *xml, StringArray &filesNotFound, const char *process, const char *target, const char *pmid, const char *packageSetName, const char *lookupDaliIp, const char *srcCluster, const char *prefix, bool activate, IUserDescriptor* userdesc, bool allowForeignFiles, bool preloadAll)
 {
     if (!xml || !*xml)
         throw MakeStringExceptionDirect(PKG_INFO_NOT_DEFINED, "PackageMap info not provided");
@@ -246,10 +246,10 @@ void addPackageMapInfo(const char *xml, StringArray &filesNotFound, const char *
     Owned<IPropertyTree> pkgSet = getPkgSetRegistry(process, false);
     IPropertyTree *psEntry = pkgSet->queryPropTree(xpath);
 
-    if (!overWrite && (psEntry || pmExisting))
-        throw MakeStringException(PKG_NAME_EXISTS, "Package name %s already exists, either delete it or specify overwrite", pmid);
+    if (!(updateFlags & DALI_UPDATEF_PACKAGEMAP) && (psEntry || pmExisting))
+        throw MakeStringException(PKG_NAME_EXISTS, "Package name %s already exists, either delete it or specify replace packagemap", pmid);
 
-    cloneFileInfoToDali(filesNotFound, pmTree, lookupDaliIp, clusterInfo, srcCluster, prefix, overWrite, userdesc, allowForeignFiles);
+    cloneFileInfoToDali(updateFlags, filesNotFound, pmTree, lookupDaliIp, clusterInfo, srcCluster, prefix, userdesc, allowForeignFiles);
 
     if (pmExisting)
         packageMaps->removeTree(pmExisting);
@@ -538,13 +538,26 @@ bool CWsPackageProcessEx::onAddPackage(IEspContext &context, IEspAddPackageReque
     if (name.isEmpty())
         throw MakeStringExceptionDirect(PKG_MISSING_PARAM, "PackageMap name parameter required");
 
+    DBGLOG("%s adding packagemap %s to target %s", context.queryUserId(), name.str(), target.str());
+
     StringBuffer pmid;
     if (!req.getGlobalScope())
         pmid.append(target).append("::");
     pmid.append(name.get());
 
     bool activate = req.getActivate();
-    bool overWrite = req.getOverWrite();
+    unsigned updateFlags = 0;
+    if (req.getOverWrite())
+        updateFlags |= (DALI_UPDATEF_PACKAGEMAP | DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES);
+    if (req.getReplacePackageMap())
+        updateFlags |= DALI_UPDATEF_PACKAGEMAP;
+    if (req.getUpdateCloneFrom())
+        updateFlags |= DALI_UPDATEF_CLONE_FROM;
+    if (req.getUpdateSuperFiles())
+        updateFlags |= DALI_UPDATEF_SUPERFILES;
+    if (req.getAppendCluster())
+        updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
+
     StringAttr processName(req.getProcess());
 
     Owned<IUserDescriptor> userdesc;
@@ -565,7 +578,7 @@ bool CWsPackageProcessEx::onAddPackage(IEspContext &context, IEspAddPackageReque
     buildPkgSetId(pkgSetId, processName.get());
 
     StringArray filesNotFound;
-    addPackageMapInfo(req.getInfo(), filesNotFound, processName, target, pmid, pkgSetId, daliip, srcCluster, prefix, activate, overWrite, userdesc, req.getAllowForeignFiles(), req.getPreloadAllPackages());
+    addPackageMapInfo(updateFlags, req.getInfo(), filesNotFound, processName, target, pmid, pkgSetId, daliip, srcCluster, prefix, activate, userdesc, req.getAllowForeignFiles(), req.getPreloadAllPackages());
     resp.setFilesNotFound(filesNotFound);
 
     StringBuffer msg;

+ 48 - 12
esp/services/ws_workunits/ws_workunitsQuerySets.cpp

@@ -671,7 +671,7 @@ static inline void updateQueryPriority(IPropertyTree *queryTree, const char *val
     }
 }
 
-void copyQueryFilesToCluster(IEspContext &context, IConstWorkUnit *cw, const char *remoteIP, const char *remotePrefix, const char *target, const char *srcCluster, const char *queryname, bool overwrite, bool allowForeignFiles)
+void copyQueryFilesToCluster(unsigned updateFlags, IEspContext &context, IConstWorkUnit *cw, const char *remoteIP, const char *remotePrefix, const char *target, const char *srcCluster, const char *queryname, bool allowForeignFiles)
 {
     if (!target || !*target)
         return;
@@ -689,11 +689,11 @@ void copyQueryFilesToCluster(IEspContext &context, IConstWorkUnit *cw, const cha
         if (queryname && *queryname)
             queryname = queryid.append(queryname).append(".0").str(); //prepublish dummy version number to support fuzzy match like queries="myquery.*" in package
         wufiles->addFilesFromQuery(cw, (ps) ? ps->queryActiveMap(target) : NULL, queryname);
-        wufiles->resolveFiles(process.str(), remoteIP, remotePrefix, srcCluster, !overwrite, true, false, true);
+        wufiles->resolveFiles(process.str(), remoteIP, remotePrefix, srcCluster, !(updateFlags & (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES)), true, false, true);
         StringBuffer defReplicateFolder;
         getConfigurationDirectory(NULL, "data2", "roxie", process.str(), defReplicateFolder);
         Owned<IDFUhelper> helper = createIDFUhelper();
-        wufiles->cloneAllInfo(helper, overwrite, true, true, clusterInfo->getRoxieRedundancy(), clusterInfo->getChannelsPerNode(), clusterInfo->getRoxieReplicateOffset(), defReplicateFolder);
+        wufiles->cloneAllInfo(updateFlags, helper, true, true, clusterInfo->getRoxieRedundancy(), clusterInfo->getChannelsPerNode(), clusterInfo->getRoxieReplicateOffset(), defReplicateFolder);
     }
 }
 
@@ -766,6 +766,9 @@ bool CWsWorkunitsEx::onWUPublishWorkunit(IEspContext &context, IEspWUPublishWork
         throw MakeStringException(ECLWATCH_MISSING_PARAMS, "Cluster name not defined for publishing workunit %s", wuid.str());
     if (!isValidCluster(target.str()))
         throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "Invalid cluster name: %s", target.str());
+
+    DBGLOG("%s publishing wuid %s to target %s as query %s", context.queryUserId(), wuid.str(), target.str(), queryName.str());
+
     StringBuffer daliIP;
     StringBuffer srcCluster;
     StringBuffer srcPrefix;
@@ -776,9 +779,18 @@ bool CWsWorkunitsEx::onWUPublishWorkunit(IEspContext &context, IEspWUPublishWork
         if (!isProcessCluster(daliIP, srcCluster))
             throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "Process cluster %s not found on %s DALI", srcCluster.str(), daliIP.length() ? daliIP.str() : "local");
     }
+    unsigned updateFlags = 0;
+    if (req.getUpdateDfs())
+        updateFlags |= (DALI_UPDATEF_SUPERFILES | DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM);
+    if (req.getUpdateCloneFrom())
+        updateFlags |= DALI_UPDATEF_CLONE_FROM;
+    if (req.getUpdateSuperFiles())
+        updateFlags |= DALI_UPDATEF_SUPERFILES;
+    if (req.getAppendCluster())
+        updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
 
     if (!req.getDontCopyFiles())
-        copyQueryFilesToCluster(context, cw, daliIP, srcPrefix, target.str(), srcCluster, queryName.str(), req.getUpdateDfs(), req.getAllowForeignFiles());
+        copyQueryFilesToCluster(updateFlags, context, cw, daliIP, srcPrefix, target.str(), srcCluster, queryName.str(), req.getAllowForeignFiles());
 
     WorkunitUpdate wu(&cw->lock());
     if (req.getUpdateWorkUnitName() && notEmpty(req.getJobName()))
@@ -2097,7 +2109,7 @@ class QueryCloner
 {
 public:
     QueryCloner(IEspContext *_context, const char *address, const char *source, const char *_target) :
-        context(_context), cloneFilesEnabled(false), target(_target), overwriteDfs(false), srcAddress(address)
+        context(_context), cloneFilesEnabled(false), target(_target), updateFlags(0), srcAddress(address)
     {
         if (srcAddress.length())
             srcQuerySet.setown(fetchRemoteQuerySetInfo(context, srcAddress, source));
@@ -2272,10 +2284,10 @@ public:
         else
             cloneAllLocal(cloneActiveState);
     }
-    void enableFileCloning(const char *dfsServer, const char *destProcess, const char *sourceProcess, bool _overwriteDfs, bool allowForeign)
+    void enableFileCloning(unsigned _updateFlags, const char *dfsServer, const char *destProcess, const char *sourceProcess, bool allowForeign)
     {
         cloneFilesEnabled = true;
-        overwriteDfs = _overwriteDfs;
+        updateFlags = _updateFlags;
         splitDerivedDfsLocation(dfsServer, srcCluster, dfsIP, srcPrefix, sourceProcess, sourceProcess, NULL, NULL);
         wufiles.setown(createReferencedFileList(context->queryUserId(), context->queryPassword(), allowForeign, false));
         Owned<IHpccPackageSet> ps = createPackageSet(destProcess);
@@ -2287,7 +2299,7 @@ public:
     {
         if (cloneFilesEnabled)
         {
-            wufiles->resolveFiles(process, dfsIP, srcPrefix, srcCluster, !overwriteDfs, true, false, true);
+            wufiles->resolveFiles(process, dfsIP, srcPrefix, srcCluster, !(updateFlags & (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM)), true, false, true);
             Owned<IDFUhelper> helper = createIDFUhelper();
             Owned <IConstWUClusterInfo> cl = getTargetClusterInfo(target);
             if (cl)
@@ -2295,7 +2307,7 @@ public:
                 SCMStringBuffer process;
                 StringBuffer defReplicateFolder;
                 getConfigurationDirectory(NULL, "data2", "roxie", cl->getRoxieProcess(process).str(), defReplicateFolder);
-                wufiles->cloneAllInfo(helper, overwriteDfs, true, true, cl->getRoxieRedundancy(), cl->getChannelsPerNode(), cl->getRoxieReplicateOffset(), defReplicateFolder);
+                wufiles->cloneAllInfo(updateFlags, helper, true, true, cl->getRoxieRedundancy(), cl->getChannelsPerNode(), cl->getRoxieReplicateOffset(), defReplicateFolder);
             }
         }
     }
@@ -2313,7 +2325,7 @@ private:
     StringAttr target;
     StringAttr process;
     bool cloneFilesEnabled;
-    bool overwriteDfs;
+    unsigned updateFlags;
 
 public:
     StringArray existingQueryIds;
@@ -2339,6 +2351,8 @@ bool CWsWorkunitsEx::onWUCopyQuerySet(IEspContext &context, IEspWUCopyQuerySetRe
     if (!isValidCluster(target))
         throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "Invalid destination target name: %s", target);
 
+    DBGLOG("%s copying queryset %s from %s target %s", context.queryUserId(), target, srcAddress.str(), srcTarget.str());
+
     QueryCloner cloner(&context, srcAddress, srcTarget, target);
 
     SCMStringBuffer process;
@@ -2350,7 +2364,17 @@ bool CWsWorkunitsEx::onWUCopyQuerySet(IEspContext &context, IEspWUCopyQuerySetRe
             clusterInfo->getRoxieProcess(process);
             if (!process.length())
                 throw MakeStringException(ECLWATCH_INVALID_CLUSTER_INFO, "DFS process cluster not found for destination target %s", target);
-            cloner.enableFileCloning(req.getDfsServer(), process.str(), req.getSourceProcess(), req.getOverwriteDfs(), req.getAllowForeignFiles());
+            unsigned updateFlags = 0;
+            if (req.getOverwriteDfs())
+                updateFlags |= (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES);
+            if (req.getUpdateCloneFrom())
+                updateFlags |= DALI_UPDATEF_CLONE_FROM;
+            if (req.getUpdateSuperFiles())
+                updateFlags |= DALI_UPDATEF_SUPERFILES;
+            if (req.getAppendCluster())
+                updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
+
+            cloner.enableFileCloning(updateFlags, req.getDfsServer(), process.str(), req.getSourceProcess(), req.getAllowForeignFiles());
         }
     }
 
@@ -2391,6 +2415,8 @@ bool CWsWorkunitsEx::onWUQuerysetCopyQuery(IEspContext &context, IEspWUQuerySetC
     Owned<IClientWUQuerySetDetailsResponse> sourceQueryInfoResp;
     IConstQuerySetQuery *srcInfo=NULL;
 
+    DBGLOG("%s copying query %s to target %s from %s target %s", context.queryUserId(), srcQuery.str(), target, srcAddress.str(), srcQuerySet.str());
+
     StringBuffer remoteIP;
     StringBuffer wuid;
     if (srcAddress.length())
@@ -2437,7 +2463,17 @@ bool CWsWorkunitsEx::onWUQuerysetCopyQuery(IEspContext &context, IEspWUQuerySetC
         StringBuffer srcCluster;
         StringBuffer srcPrefix;
         splitDerivedDfsLocation(req.getDaliServer(), srcCluster, daliIP, srcPrefix, req.getSourceProcess(), req.getSourceProcess(), remoteIP.str(), NULL);
-        copyQueryFilesToCluster(context, cw, daliIP.str(), srcPrefix, target, srcCluster, targetQueryName.get(), req.getOverwrite(), req.getAllowForeignFiles());
+        unsigned updateFlags = 0;
+        if (req.getOverwrite())
+            updateFlags |= (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES);
+        if (req.getUpdateCloneFrom())
+            updateFlags |= DALI_UPDATEF_CLONE_FROM;
+        if (req.getUpdateSuperFiles())
+            updateFlags |= DALI_UPDATEF_SUPERFILES;
+        if (req.getAppendCluster())
+            updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
+
+        copyQueryFilesToCluster(updateFlags, context, cw, daliIP.str(), srcPrefix, target, srcCluster, targetQueryName.get(), req.getAllowForeignFiles());
     }
 
     WorkunitUpdate wu(&cw->lock());

+ 3 - 3
initfiles/etc/bash_completion/ecl

@@ -89,7 +89,7 @@ _ecl_opts_queries()
             echo "--help list copy config"
             ;;
         copy)
-            echo -n "--no-reload --allow-foreign --wait= --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= "
+            echo -n "--no-reload --allow-foreign --wait= --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= --update-super-files --update-clone-from --dont-append-cluster "
             _ecl_opts_common
             ;;
         config)
@@ -140,7 +140,7 @@ _ecl_opts_packagemap()
             then
                 _ecl_opts_file
             else
-                echo -n "-O --overwrite --allow-foreign "
+                echo -n "-O --overwrite --allow-foreign --update-super-files --update-clone-from --dont-append-cluster --replace "
                 _ecl_opts_common
             fi
             ;;
@@ -170,7 +170,7 @@ _ecl_opts_core_file()
                 _ecl_opts_common
                 ;;
             publish)
-                echo -n "-A --activate --no-reload --allow-foreign --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= "
+                echo -n "-A --activate --no-reload --allow-foreign --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= --update-super-files --update-clone-from --dont-append-cluster "
                 _ecl_opts_deploy
                 _ecl_opts_common
                 ;;