Przeglądaj źródła

Merge branch 'candidate-7.8.x' into candidate-7.10.x

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 4 lat temu
rodzic
commit
b59c1e7563

+ 1 - 0
common/fileview2/fverror.hpp

@@ -56,6 +56,7 @@
 #define FVERR_PluginMismatch                    6728
 #define FVERR_RowTooLarge                       6729
 #define FVERR_CouldNotProcessSchema             6730
+#define FVERR_MaxLengthExceeded                 6731
 
 #define FVERR_CouldNotResolveX_Text             "Could not resolve file '%s' in DFS"
 #define FVERR_NoRecordDescription_Text          "DFS did not contain a record description for '%s'"

+ 20 - 0
common/fileview2/fvsource.cpp

@@ -16,6 +16,7 @@
 ############################################################################## */
 
 #include "platform.h"
+#include "limits.h"
 #include "jliball.hpp"
 #include "eclrtl.hpp"
 #include "rtlds_imp.hpp"
@@ -594,6 +595,18 @@ void DataSourceMetaData::serialize(MemoryBuffer & buffer) const
 
 size32_t DataSourceMetaData::getRecordSize(const void *rec)
 {
+    return calcRecordSize(UINT_MAX, rec);
+}
+
+static void checkReadPastEnd(size32_t offset, size32_t delta, size32_t maxLength)
+{
+    //Check for reading past the end of the buffer, or for the size wrapping 32bits.
+    if ((offset > maxLength) || (offset + delta < offset))
+        throw makeStringException(FVERR_MaxLengthExceeded, "Read past the end of a variable size block (MAXLENGTH exceeded)");
+}
+
+size32_t DataSourceMetaData::calcRecordSize(size32_t maxLength, const void *rec)
+{
     if (isStoredFixedWidth)
         return minRecordSize;
     if (!rec)
@@ -616,21 +629,27 @@ size32_t DataSourceMetaData::getRecordSize(const void *rec)
             case type_string:
             case type_table:
             case type_groupedtable:
+                checkReadPastEnd(curOffset, sizeof(unsigned), maxLength);
                 size = *((unsigned *)cur) + sizeof(unsigned);
                 break;
             case type_set:
+                checkReadPastEnd(curOffset, sizeof(bool) + sizeof(unsigned), maxLength);
                 size = *((unsigned *)(cur + sizeof(bool))) + sizeof(unsigned) + sizeof(bool);
                 break;
             case type_qstring:
+                checkReadPastEnd(curOffset, sizeof(unsigned), maxLength);
                 size = rtlQStrSize(*((unsigned *)cur)) + sizeof(unsigned);
                 break;
             case type_unicode:
+                checkReadPastEnd(curOffset, sizeof(unsigned), maxLength);
                 size = *((unsigned *)cur)*2 + sizeof(unsigned);
                 break;
             case type_utf8:
+                checkReadPastEnd(curOffset, sizeof(unsigned), maxLength);
                 size = sizeof(unsigned) + rtlUtf8Size(*(unsigned *)cur, cur+sizeof(unsigned));
                 break;
             case type_varstring:
+                //buffer overflow checking for the following will wait until code is reimplemented
                 size = strlen((char *)cur)+1;
                 break;
             case type_varunicode:
@@ -660,6 +679,7 @@ size32_t DataSourceMetaData::getRecordSize(const void *rec)
         else
             bitsRemaining = 0;
 
+        checkReadPastEnd(curOffset, size, maxLength);
         curOffset += size;
     }
     return curOffset;

+ 2 - 1
common/fileview2/fvsource.ipp

@@ -137,12 +137,13 @@ public:
     virtual size32_t getFixedSize() const;
     virtual size32_t getRecordSize(unsigned maxLength, const void *rec)
     {
-        return getRecordSize(rec);
+        return calcRecordSize(maxLength, rec);
     }
     virtual size32_t getMinRecordSize() const;
 
 protected:
     void addSimpleField(const char * name, const char * xpath, ITypeInfo * type, unsigned flag=FVFFnone);
+    size32_t calcRecordSize(unsigned maxLength, const void *rec);
     void gatherFields(IHqlExpression * expr, bool isConditional, bool *pMixedContent);
     void gatherChildFields(IHqlExpression * expr, bool isConditional, bool *pMixedContent);
     void gatherAttributes();

+ 2 - 0
ecl/ecl-bundle/ecl-bundle.cpp

@@ -1024,6 +1024,8 @@ protected:
         unsigned retCode = doPipeCommand(output, queryEclccPath(optVerbose), "--nologfile -showpaths", NULL);
         if (retCode == START_FAILURE)
             throw makeStringExceptionV(0, "FATAL: Could not locate eclcc command");
+        if (optVerbose)
+            printf("eclcc output:\n%s\n", output.str());
         extractValueFromEnvOutput(bundlePath, output, ECLCC_ECLBUNDLE_PATH);
         extractValueFromEnvOutput(hooksPath, output, HPCC_FILEHOOKS_PATH);
     }

+ 3 - 3
ecl/eclcc/eclcc.cpp

@@ -59,7 +59,7 @@
 #include "reservedwords.hpp"
 #include "eclcc.hpp"
 
-#ifndef CONTAINERIZED
+#ifndef _CONTAINERIZED
 #include "environment.hpp"
 #endif
 
@@ -2329,7 +2329,7 @@ bool EclCC::checkDaliConnected() const
 unsigned EclCC::lookupClusterSize() const
 {
     CriticalBlock b(dfsCrit);  // Overkill at present but maybe one day codegen will start threading? If it does the stack is also iffy!
-#ifndef CONTAINERIZED
+#ifndef _CONTAINERIZED
     if (!optDFS || disconnectReported || !checkDaliConnected())
         return 0;
 #endif
@@ -2340,7 +2340,7 @@ unsigned EclCC::lookupClusterSize() const
         prevClusterSize = 0;
     else
     {
-#ifdef CONTAINERIZED
+#ifdef _CONTAINERIZED
         VStringBuffer xpath("queues[@name=\"%s\"]", cluster);
         IPropertyTree * queue = configuration->queryPropTree(xpath);
         if (queue)

+ 12 - 0
ecl/hql/hqlexpr.cpp

@@ -9193,6 +9193,18 @@ bool CHqlMergedScope::isPlugin() const
     return false;
 }
 
+bool CHqlMergedScope::isEquivalentScope(const IHqlScope & other) const
+{
+    if (this == &other)
+        return true;
+
+    ForEachItemIn(i, mergedScopes)
+    {
+        if (other.isEquivalentScope(mergedScopes.item(i)))
+            return true;
+    }
+    return false;
+}
 
 
 

+ 1 - 0
ecl/hql/hqlexpr.hpp

@@ -1092,6 +1092,7 @@ interface IHqlScope : public IInterface
     virtual ISourcePath * querySourcePath() const = 0;
     virtual bool hasBaseClass(IHqlExpression * searchBase) = 0;
     virtual bool allBasesFullyBound() const = 0;
+    virtual bool isEquivalentScope(const IHqlScope & other) const = 0;
 
     virtual IHqlScope * clone(HqlExprArray & children, HqlExprArray & symbols) = 0;
     virtual IHqlScope * queryConcreteScope() = 0;

+ 4 - 0
ecl/hql/hqlexpr.ipp

@@ -1155,6 +1155,7 @@ public:
     virtual ISourcePath * querySourcePath() const override { throwUnexpected(); }
     virtual bool hasBaseClass(IHqlExpression * searchBase) override;
     virtual bool allBasesFullyBound() const override { return false; } // Assume the worst
+    virtual bool isEquivalentScope(const IHqlScope & other) const override { return this == &other; }
 
     virtual void ensureSymbolsDefined(HqlLookupContext & ctx) override { }
 
@@ -1217,6 +1218,7 @@ public:
     virtual const char * queryFullName() const override  { return fullName; }
     virtual IIdAtom * queryFullContainerId() const override { return containerId; }
     virtual ISourcePath * querySourcePath() const override { return text ? text->querySourcePath() : NULL; }
+    virtual bool isEquivalentScope(const IHqlScope & other) const override { return this == &other; }
 
     virtual void ensureSymbolsDefined(HqlLookupContext & ctx) override { }
     virtual bool isImplicit() const override { return false; }
@@ -1390,6 +1392,7 @@ public:
     virtual bool allBasesFullyBound() const override;
     virtual bool isImplicit() const override;
     virtual bool isPlugin() const override;
+    virtual bool isEquivalentScope(const IHqlScope & other) const override;
     virtual IHqlScope * queryConcreteScope() override { return this; }
 
 protected:
@@ -1614,6 +1617,7 @@ public:
     virtual ISourcePath * querySourcePath() const override { return NULL; }
     virtual bool hasBaseClass(IHqlExpression * searchBase) override;
     virtual bool allBasesFullyBound() const override { return false; }
+    virtual bool isEquivalentScope(const IHqlScope & other) const override { return this == &other; }
 
     virtual IHqlScope * clone(HqlExprArray & children, HqlExprArray & symbols) override;
     virtual IHqlScope * queryConcreteScope() override;

+ 1 - 1
ecl/hql/hqlgram2.cpp

@@ -3786,7 +3786,7 @@ IHqlExpression *HqlGram::lookupSymbol(IIdAtom * searchName, const attribute& err
                 else
                     reportError(ERR_OBJ_NOSUCHFIELD, errpos, "Object does not have a member named '%s'", str(searchName));
             }
-            else if ((modScope != containerScope) && !isExported(resolved))
+            else if (!isExported(resolved) && !modScope->isEquivalentScope(*containerScope))
                 reportError(HQLERR_CannotAccessShared, errpos, "Cannot access SHARED symbol '%s' in another module", str(searchName));
             return resolved.getClear();
         }

+ 2 - 0
ecl/hql/hqlir.cpp

@@ -762,6 +762,7 @@ inline type_t getRequiredTypeCode(node_operator op)
     case no_type:
     case no_libraryscopeinstance:
     case no_forwardscope:
+    case no_mergedscope:
         return type_alias; // type is an alias of itself.
     }
     return type_none;
@@ -2037,6 +2038,7 @@ id_t ExpressionIRPlayer::doProcessExpr(IHqlExpression * expr)
         break;
     case no_virtualscope:
     case no_concretescope:
+    case no_mergedscope:
         {
             HqlExprArray scopeSymbols;
             expr->queryScope()->getSymbols(scopeSymbols);

+ 12 - 17
ecl/hql/hqlutil.cpp

@@ -9118,7 +9118,7 @@ extern HQL_API bool allParametersHaveDefaults(IHqlExpression * function)
     return true;
 }
 
-extern HQL_API bool expandMissingDefaultsAsStoreds(HqlExprArray & args, IHqlExpression * function)
+extern HQL_API bool expandParametersAsStoreds(HqlExprArray & args, IHqlExpression * function)
 {
     assertex(function->isFunction());
     IHqlExpression * formals = queryFunctionParameters(function);
@@ -9128,21 +9128,16 @@ extern HQL_API bool expandMissingDefaultsAsStoreds(HqlExprArray & args, IHqlExpr
         ForEachChild(idx, formals)
         {
             IHqlExpression *formal = formals->queryChild(idx);
-            IHqlExpression * defvalue = queryDefaultValue(defaults, idx);
-            if (defvalue)
-            {
-                args.append(*LINK(defvalue));
-            }
-            else
-            {
-                OwnedHqlExpr nullValue = createNullExpr(formal->queryType());
-                OwnedHqlExpr storedName = createConstant(str(formal->queryName()));
-                OwnedHqlExpr stored = createValue(no_stored, makeVoidType(), storedName.getClear());
-                HqlExprArray colonArgs;
-                colonArgs.append(*LINK(nullValue));
-                colonArgs.append(*LINK(stored));
-                args.append(*createWrapper(no_colon, formal->queryType(), colonArgs));
-            }
+            Linked<IHqlExpression> defvalue = queryDefaultValue(defaults, idx);
+            if (!defvalue)
+                defvalue.setown(createNullExpr(formal->queryType()));
+
+            OwnedHqlExpr storedName = createConstant(str(formal->queryId()));
+            OwnedHqlExpr stored = createValue(no_stored, makeVoidType(), storedName.getClear());
+            HqlExprArray colonArgs;
+            colonArgs.append(*LINK(defvalue));
+            colonArgs.append(*LINK(stored));
+            args.append(*createWrapper(no_colon, formal->queryType(), colonArgs));
         }
     }
     catch (IException * e)
@@ -9759,7 +9754,7 @@ static IHqlExpression * transformAttributeToQuery(IHqlExpression * expr, HqlLook
         HqlExprArray actuals;
         if (!allParametersHaveDefaults(expr))
         {
-            if (!expandMissingDefaultsAsStoreds(actuals, expr))
+            if (!expandParametersAsStoreds(actuals, expr))
             {
                 //For each parameter that doesn't have a default, create a stored variable of the appropriate type
                 //with a null value as the default value, and use that.

+ 24 - 0
ecl/regress/issue24482.ecl

@@ -0,0 +1,24 @@
+<Archive build="internal_7.10.3-closedown0Debug"
+         eclVersion="7.11.0"
+         legacyImport="0"
+         legacyWhen="0">
+ <Query attributePath="B"/>
+ <Module key="" name="">
+  <Attribute key="b"
+             name="B"
+             sourcePath="/home/gavin/temp/24482/B.ecl"
+             ts="1595584384000000">
+   import $;
+EXPORT B := $.A;&#10;&#10;
+  </Attribute>
+  <Attribute key="a"
+             name="a"
+             sourcePath="/home/gavin/temp/24482/./A.ecl"
+             ts="1595584417000000">
+   SHARED A := 42;&#10;&#10;
+  </Attribute>
+ </Module>
+ <Option name="debugquery" value="1"/>
+ <Option name="savecpptempfiles" value="1"/>
+ <Option name="eclcc_compiler_version" value="7.11.0"/>
+</Archive>

+ 27 - 0
ecl/regress/issue24485.eclxml

@@ -0,0 +1,27 @@
+<Archive>
+<!--
+
+    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+ <Module name="myModule">
+  <Attribute name="myAttr">
+rec := { unsigned age; };
+EXPORT myAttr(string myName, unsigned age = 99, SET OF unsigned ids = [], DATASET(rec) AGES) := FUNCTION
+    RETURN output ('Hello ' + myName + ', age ' + (string)age + ' (' + (string)count(ids) + ':' + (string)count(ages) + ')');
+END;
+    </Attribute>
+  </Module>
+ <Query attributePath="myModule.myAttr"/>
+</Archive>

+ 118 - 1
plugins/py3embed/py3embed.cpp

@@ -48,6 +48,10 @@
   #define Py_TPFLAGS_HAVE_ITER 0
 #endif
 
+#if PY_MINOR_VERSION < 7
+  #define USE_CUSTOM_NAMEDTUPLES
+#endif
+
 static const char * compatibleVersions[] = {
     "Python3.x Embed Helper 1.0.0",
     NULL };
@@ -146,6 +150,105 @@ static void checkPythonError()
     }
 }
 
+#ifdef USE_CUSTOM_NAMEDTUPLES
+// In Python3.6 we can't use the standard namedtuple if we have > 255 params. So we need to use our own based on similar techniques.
+// The code below is copied from the Python 3.6 collections module source, with minor modifications:
+// - pass in the initial tuple values as a tuple rather than as separate parameters, to avoid the 256 param limit
+// - remove some options we were not using
+
+static constexpr const char * _py36code = R"!!(
+import sys as _sys
+from keyword import iskeyword as _iskeyword
+_class_template = """\
+from builtins import property as _property, tuple as _tuple
+from operator import itemgetter as _itemgetter
+from collections import OrderedDict
+class {typename}(tuple):
+    '{typename}({arg_list})'
+    __slots__ = ()
+    _fields = {field_names!r}
+    def __new__(_cls, t):
+        'Create new instance of {typename}({arg_list})'
+        return _tuple.__new__(_cls, t)
+    @classmethod
+    def _make(cls, iterable, new=tuple.__new__, len=len):
+        'Make a new {typename} object from a sequence or iterable'
+        result = new(cls, iterable)
+        if len(result) != {num_fields:d}:
+            raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result))
+        return result
+    def _replace(_self, **kwds):
+        'Return a new {typename} object replacing specified fields with new values'
+        result = _self._make(map(kwds.pop, {field_names!r}, _self))
+        if kwds:
+            raise ValueError('Got unexpected field names: %r' % list(kwds))
+        return result
+    def __repr__(self):
+        'Return a nicely formatted representation string'
+        return self.__class__.__name__ + '({repr_fmt})' % self
+    def _asdict(self):
+        'Return a new OrderedDict which maps field names to their values.'
+        return OrderedDict(zip(self._fields, self))
+    def __getnewargs__(self):
+        'Return self as a plain tuple.  Used by copy and pickle.'
+        return tuple(self)
+{field_defs}
+"""
+
+_repr_template = '"{name}=%r";'
+
+_field_template = '''\
+    {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}')
+'''
+
+def namedtuple(typename, field_names):
+
+    # Validate the field names, and replace any invalid ones with a valid name.
+    field_names = field_names.replace(',', ' ').split()
+    field_names = list(map(str, field_names))
+    typename = str(typename)
+    for index, name in enumerate(field_names):
+        if (not name.isidentifier()
+            or _iskeyword(name)
+            or name.startswith('_')):
+            field_names[index] = '_%d' % index
+
+    # Fill-in the class template
+    class_definition = _class_template.format(
+        typename = typename,
+        field_names = tuple(field_names),
+        num_fields = len(field_names),
+        arg_list = repr(tuple(field_names)).replace("'", "")[1:-1],
+        repr_fmt = ', '.join(_repr_template.format(name=name)
+                             for name in field_names),
+        field_defs = '\n'.join(_field_template.format(index=index, name=name)
+                               for index, name in enumerate(field_names))
+    )
+
+    # Execute the template string in a temporary namespace and support
+    # tracing utilities by setting a value for frame.f_globals['__name__']
+    namespace = dict(__name__='namedtuple_%s' % typename)
+    exec(class_definition, namespace)
+    result = namespace[typename]
+
+    # For pickling to work, the __module__ variable needs to be set to the frame
+    # where the named tuple is created.  Bypass this step in environments where
+    # sys._getframe is not defined (Jython for example) or sys._getframe is not
+    # defined for arguments greater than 0 (IronPython), or where the user has
+    # specified a particular module.
+    module = None
+    try:
+        module = _sys._getframe(1).f_globals.get('__name__', '__main__')
+    except (AttributeError, ValueError):
+        pass
+    if module is not None:
+        result.__module__ = module
+
+    return result
+)!!";
+
+#endif
+
 // The Python Global Interpreter Lock (GIL) won't know about C++-created threads, so we need to
 // call PyGILState_Ensure() and PyGILState_Release at the start and end of every function.
 // Wrapping them in a class like this ensures that the release always happens even if
@@ -408,10 +511,19 @@ public:
         if (!namedtuple)
         {
             namedtupleTypes.setown(PyDict_New());
+#ifdef USE_CUSTOM_NAMEDTUPLES
+            OwnedPyObject temp_namespace(PyDict_New());
+            PyDict_SetItemString(temp_namespace, "__builtins__",  PyEval_GetBuiltins());  // required for import to work
+            checkPythonError();
+            OwnedPyObject ran = PyRun_String(_py36code, Py_file_input, temp_namespace, temp_namespace);
+            checkPythonError();
+            namedtuple.set(PyDict_GetItemString(temp_namespace, "namedtuple"));   // NOTE - returns borrowed reference
+#else
             OwnedPyObject pName = PyUnicode_FromString("collections");
             OwnedPyObject collections = PyImport_Import(pName);
             checkPythonError();
             namedtuple.setown(PyObject_GetAttrString(collections, "namedtuple"));
+#endif
             checkPythonError();
             assertex(PyCallable_Check(namedtuple));
         }
@@ -762,7 +874,6 @@ static void getStringResult(const RtlFieldInfo *field, PyObject *obj, size32_t &
             size_t lenBytes = PyBytes_Size(obj);
             rtlStrToStrX(chars, result, lenBytes, text);
         }
-        return;
     }
     typeError("string", field);
 }
@@ -1211,7 +1322,13 @@ public:
     PyObject *getTuple(const RtlTypeInfo *type)
     {
         OwnedPyObject mynamedtupletype = sharedCtx ? sharedCtx->getNamedTupleType(type) : globalState.getNamedTupleType(type);
+#ifdef USE_CUSTOM_NAMEDTUPLES
+        OwnedPyObject argsTuple = PyTuple_New(1);
+        Py_INCREF(args);
+        PyTuple_SET_ITEM((PyTupleObject *) argsTuple.get(), 0, args);
+#else
         OwnedPyObject argsTuple = PyList_AsTuple(args);
+#endif
         OwnedPyObject mynamedtuple = PyObject_CallObject(mynamedtupletype, argsTuple);  // Creates a namedtuple from the supplied tuple
         checkPythonError();
         return mynamedtuple.getClear();

+ 7 - 0
system/jlib/jutil.cpp

@@ -2747,6 +2747,13 @@ const char * queryCurrentProcessPath()
         default:
             break;
         }
+        const char *canon = realpath(processPath.str(), nullptr);
+        if (canon)
+        {
+            processPath.clear();
+            processPath.set(canon);
+            free((void *) canon);
+        }
 #else
         char path[PATH_MAX + 1];
         ssize_t len = readlink("/proc/self/exe", path, PATH_MAX);