public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(metadata);
            Contracts.AssertValue(binding);

            UnaryOpNode unaryOpNode = node.AsUnaryOpLit();

            if (unaryOpNode == null)
            {
                return(false);
            }

            if (!IsValidNode(node, binding))
            {
                return(false);
            }

            IOpDelegationStrategy opDelStrategy = _function.GetOpDelegationStrategy(unaryOpNode.Op);
            var unaryOpDelStrategy = (opDelStrategy as UnaryOpDelegationStrategy).VerifyValue();

            Contracts.Assert(unaryOpDelStrategy.Op == unaryOpNode.Op);

            if ((unaryOpNode.Child.Kind != NodeKind.FirstName) && !opDelStrategy.IsOpSupportedByTable(metadata, node, binding))
            {
                var telemetryMessage = string.Format("{0} operator not supported at table level", unaryOpNode.Op.ToString());
                SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);
                TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.UnaryOpNotSupportedByTable, node, binding, _function, DelegationTelemetryInfo.CreateUnaryOpNoSupportedInfoTelemetryInfo(unaryOpNode.Op));
                return(false);
            }

            return(IsSupportedNode(unaryOpNode.Child, metadata, binding, opDelStrategy));
        }
Example #2
0
        protected bool IsValidNode(TexlNode node, TexlBinding binding)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(binding);

            bool isAsync = binding.IsAsync(node);
            bool isPure  = binding.IsPure(node);


            if (node is DottedNameNode &&
                ((binding.GetType(node.AsDottedName().Left).Kind == DKind.OptionSet && binding.GetType(node).Kind == DKind.OptionSetValue) ||
                 (binding.GetType(node.AsDottedName().Left).Kind == DKind.View && binding.GetType(node).Kind == DKind.ViewValue)))
            {
                // OptionSet and View Access are delegable despite being async
                return(true);
            }

            if (node is CallNode && (binding.IsBlockScopedConstant(node) ||
                                     (binding.GetInfo(node as CallNode).Function is AsTypeFunction)))
            {
                // AsType is delegable despite being async
                return(true);
            }

            // Async predicates and impure nodes are not supported.
            // Let CallNodes for User() be marked as being Valid to allow
            // expressions with User() calls to be delegated
            if (!(IsUserCallNodeDelegable(node, binding)) && (isAsync || !isPure))
            {
                var telemetryMessage = string.Format("Kind:{0}, isAsync:{1}, isPure:{2}", node.Kind, isAsync, isPure);
                SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);

                if (isAsync)
                {
                    TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.AsyncPredicate, node, binding, _function);
                }

                if (!isPure)
                {
                    TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.ImpureNode, node, binding, _function, DelegationTelemetryInfo.CreateImpureNodeTelemetryInfo(node, binding));
                }

                return(false);
            }

            return(true);
        }
        public virtual bool IsSupportedOpNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(metadata);
            Contracts.AssertValue(binding);

            BinaryOpNode binaryOpNode = node.AsBinaryOp();

            if (binaryOpNode == null)
            {
                return(false);
            }

            IOpDelegationStrategy opDelStrategy = _function.GetOpDelegationStrategy(binaryOpNode.Op, binaryOpNode);
            var binaryOpDelStrategy             = (opDelStrategy as BinaryOpDelegationStrategy).VerifyValue();

            Contracts.Assert(binaryOpNode.Op == binaryOpDelStrategy.Op);

            // Check if binaryOp is supported by datasource in the context of filter operation.
            // If this is not allowed then there is no point in checking lhs and rhs
            // It's only safe to do so if lhs and rhs is first/dotted name node as columns (FirstName/DottedName node) can have additional capabilities defined.
            if (!(binaryOpNode.Left is FirstNameNode || binaryOpNode.Left is DottedNameNode) &&
                !(binaryOpNode.Right is FirstNameNode || binaryOpNode.Right is DottedNameNode) &&
                !opDelStrategy.IsOpSupportedByTable(metadata, node, binding))
            {
                var telemetryMessage = string.Format("{0} operator not supported at table level", binaryOpNode.Op.ToString());
                SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);
                TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.BinaryOpNoSupported, node, binding, _function, DelegationTelemetryInfo.CreateBinaryOpNoSupportedInfoTelemetryInfo(binaryOpNode.Op));
                return(false);
            }

            if (!ODataFunctionMappings.BinaryOpToOperatorMap.Value.ContainsKey(binaryOpNode.Op))
            {
                SuggestDelegationHint(node, binding);
                return(false);
            }

            if (!IsSupportedNode(binaryOpNode.Left, metadata, binding, opDelStrategy, false))
            {
                SuggestDelegationHint(node, binding);
                return(false);
            }

            if (!IsSupportedNode(binaryOpNode.Right, metadata, binding, opDelStrategy, true))
            {
                SuggestDelegationHint(node, binding);
                return(false);
            }

            DType leftType  = binding.GetType(binaryOpNode.Left);
            DType rightType = binding.GetType(binaryOpNode.Right);

            if ((leftType.IsPolymorphic && rightType.IsRecord) || (leftType.IsRecord && rightType.IsPolymorphic))
            {
                return(true);
            }

            if (!DoCoercionCheck(binaryOpNode, metadata, binding))
            {
                SuggestDelegationHint(node, binding);
                return(false);
            }

            return(true);
        }
Example #4
0
        // Verifies if provided column node supports delegation.
        protected bool IsDelegatableColumnNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy, DelegationCapability capability)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(binding);
            Contracts.AssertValueOrNull(opDelStrategy);
            Contracts.Assert(binding.IsRowScope(node));

            FirstNameInfo firstNameInfo = binding.GetInfo(node.AsFirstName());

            if (firstNameInfo == null)
            {
                return(false);
            }

            IDelegationMetadata metadata = GetCapabilityMetadata(firstNameInfo);

            // This means that this row scoped field is from some parent scope which is non-delegatable. That should deny delegation at that point.
            // For this scope, this means that value will be provided from some other source.
            // For example, AddColumns(CDS, "Column1", LookUp(CDS1, Name in FirstName))
            // CDS - *[Name:s], CDS1 - *[FirstName:s]
            if (metadata == null)
            {
                return(true);
            }

            var columnName = firstNameInfo.Name;

            Contracts.AssertValid(columnName);

            var columnPath = DPath.Root.Append(columnName);

            if (!metadata.FilterDelegationMetadata.IsDelegationSupportedByColumn(columnPath, capability))
            {
                var safeColumnName = CharacterUtils.MakeSafeForFormatString(columnName.Value);
                var message        = string.Format(StringResources.Get(TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn), safeColumnName);
                SuggestDelegationHintAndAddTelemetryMessage(node, binding, message, TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn, safeColumnName);
                TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.NoDelSupportByColumn, node, binding, _function, DelegationTelemetryInfo.CreateNoDelSupportByColumnTelemetryInfo(firstNameInfo));
                return(false);
            }

            // If there is any operator applied on this node then check if column supports operation.
            if (opDelStrategy != null && !opDelStrategy.IsOpSupportedByColumn(metadata.FilterDelegationMetadata, node.AsFirstName(), columnPath, binding))
            {
                return(false);
            }

            return(true);
        }
Example #5
0
        public bool IsValidCallNode(CallNode node, TexlBinding binding, OperationCapabilityMetadata metadata)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(binding);
            Contracts.AssertValue(metadata);

            if (!IsValidNode(node, binding))
            {
                SuggestDelegationHint(node, binding);
                return(false);
            }

            // If the node is not row scoped and it's valid then it can be delegated.
            var isRowScoped = binding.IsRowScope(node);

            if (!isRowScoped)
            {
                return(true);
            }

            CallInfo callInfo = binding.GetInfo(node);

            if (callInfo?.Function != null && ((TexlFunction)callInfo.Function).IsRowScopedServerDelegatable(node, binding, metadata))
            {
                return(true);
            }

            var telemetryMessage = string.Format("Kind:{0}, isRowScoped:{1}", node.Kind, isRowScoped);

            SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);
            TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.UndelegatableFunction, node, binding, _function, DelegationTelemetryInfo.CreateUndelegatableFunctionTelemetryInfo((TexlFunction)callInfo?.Function));
            return(false);
        }
Example #6
0
        public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(binding);
            Contracts.AssertValueOrNull(opDelStrategy);

            var isRowScoped = binding.IsRowScope(node);

            if (!isRowScoped)
            {
                return(IsValidNode(node, binding));
            }

            bool isRowScopedDelegationExempted;

            if (!IsValidRowScopedDottedNameNode(node, binding, metadata, out isRowScopedDelegationExempted))
            {
                var telemetryMessage = string.Format("Kind:{0}, isRowScoped:{1}",
                                                     node.Kind, isRowScoped);

                SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);
                return(false);
            }

            if (isRowScopedDelegationExempted)
            {
                binding.SetBlockScopedConstantNode(node);
                return(true);
            }

            if (binding.TryGetFullRecordRowScopeAccessInfo(node, out var firstNameInfo))
            {
                // This means that this row scoped field is from some parent scope which is non-delegatable. That should deny delegation at that point.
                // For this scope, this means that value will be provided from some other source.
                // For example, AddColumns(CDS As Left, "Column1", LookUp(CDS1, Left.Name in FirstName))
                // CDS - *[Name:s], CDS1 - *[FirstName:s]
                if (GetCapabilityMetadata(firstNameInfo) == null)
                {
                    return(true);
                }
            }

            if (!binding.GetType(node.Left).HasExpandInfo)
            {
                DPath columnPath;
                if (!BinderUtils.TryConvertNodeToDPath(binding, node, out columnPath) || !metadata.IsDelegationSupportedByColumn(columnPath, _function.FunctionDelegationCapability))
                {
                    var safeColumnName = CharacterUtils.MakeSafeForFormatString(columnPath.ToDottedSyntax());
                    var message        = string.Format(StringResources.Get(TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn), safeColumnName);
                    SuggestDelegationHintAndAddTelemetryMessage(node, binding, message, TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn, safeColumnName);
                    TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.NoDelSupportByColumn, node, binding, _function, DelegationTelemetryInfo.CreateNoDelSupportByColumnTelemetryInfo(columnPath.ToDottedSyntax()));
                    return(false);
                }

                // If there is any operator applied on this node then check if column supports operation.
                return(opDelStrategy?.IsOpSupportedByColumn(metadata, node, columnPath, binding) ?? true);
            }

            // If there is an entity reference then we need to do additional verification.
            IExpandInfo info           = binding.GetType(node.Left).ExpandInfo.VerifyValue();
            var         dataSourceInfo = info.ParentDataSource;

            IDataEntityMetadata entityMetadata;

            if (!dataSourceInfo.DataEntityMetadataProvider.TryGetEntityMetadata(info.Identity, out entityMetadata))
            {
                var telemetryMessage = string.Format("Kind:{0}, isRowScoped:{1}, no metadata found for entity {2}",
                                                     node.Kind, isRowScoped, CharacterUtils.MakeSafeForFormatString(info.Identity));

                SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);
                return(false);
            }

            OperationCapabilityMetadata entityCapabilityMetadata = GetScopedOperationCapabilityMetadata(entityMetadata.DelegationMetadata);
            string maybeLogicalName;
            DName  columnName = node.Right.Name;

            if (entityMetadata.DisplayNameMapping.TryGetFromSecond(node.Right.Name.Value, out maybeLogicalName))
            {
                columnName = new DName(maybeLogicalName);
            }

            var entityColumnPath = DPath.Root.Append(columnName);

            if (!entityCapabilityMetadata.IsDelegationSupportedByColumn(entityColumnPath, _function.FunctionDelegationCapability))
            {
                var safeColumnName = CharacterUtils.MakeSafeForFormatString(columnName.Value);
                var message        = string.Format(StringResources.Get(TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn), safeColumnName);
                SuggestDelegationHintAndAddTelemetryMessage(node, binding, message, TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn, safeColumnName);
                TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.NoDelSupportByColumn, node, binding, _function, DelegationTelemetryInfo.CreateNoDelSupportByColumnTelemetryInfo(columnName));
                return(false);
            }

            // If there is any operator applied on this node then check if column supports operation.
            return(opDelStrategy?.IsOpSupportedByColumn(entityCapabilityMetadata, node, entityColumnPath, binding) ?? true);
        }
Example #7
0
        private bool IsValidSortOrderNode(TexlNode node, SortOpMetadata metadata, TexlBinding binding, DPath columnPath)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(metadata);
            Contracts.AssertValue(binding);
            Contracts.AssertValid(columnPath);

            if (binding.IsAsync(node))
            {
                AddSuggestionMessageToTelemetry("Async sortorder node.", node, binding);
                DelegationTrackerCore.SetDelegationTrackerStatus(DelegationStatus.AsyncSortOrder, node, binding, this, DelegationTelemetryInfo.CreateEmptyDelegationTelemetryInfo());
                return(false);
            }

            string sortOrder;

            switch (node.Kind)
            {
            case NodeKind.FirstName:
            case NodeKind.StrLit:
                return(_sortOrderValidator.TryGetValidValue(node, binding, out sortOrder) &&
                       IsSortOrderSuppportedByColumn(node, binding, sortOrder, metadata, columnPath));

            case NodeKind.DottedName:
            case NodeKind.Call:
                if (_sortOrderValidator.TryGetValidValue(node, binding, out sortOrder) &&
                    IsSortOrderSuppportedByColumn(node, binding, sortOrder, metadata, columnPath))
                {
                    return(true);
                }

                // If both ascending and descending are supported then we can support this.
                return(IsSortOrderSuppportedByColumn(node, binding, LanguageConstants.DescendingSortOrderString, metadata, columnPath) &&
                       IsSortOrderSuppportedByColumn(node, binding, LanguageConstants.AscendingSortOrderString, metadata, columnPath));

            default:
                AddSuggestionMessageToTelemetry("Unsupported sortorder node kind.", node, binding);
                return(false);
            }
        }
Example #8
0
        private bool IsSortOrderSuppportedByColumn(TexlNode node, TexlBinding binding, string order, SortOpMetadata metadata, DPath columnPath)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(binding);
            Contracts.AssertValue(order);
            Contracts.AssertValue(metadata);
            Contracts.AssertValid(columnPath);

            var result = IsSortOrderSuppportedByColumn(order, metadata, columnPath);

            if (!result)
            {
                DelegationTrackerCore.SetDelegationTrackerStatus(DelegationStatus.SortOrderNotSupportedByColumn, node, binding, this, DelegationTelemetryInfo.CreateEmptyDelegationTelemetryInfo());
            }

            return(result);
        }
Example #9
0
        public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding)
        {
            Contracts.AssertValue(callNode);
            Contracts.AssertValue(binding);

            if (!CheckArgsCount(callNode, binding))
            {
                return(false);
            }

            SortOpMetadata      metadata           = null;
            IDelegationMetadata delegationMetadata = null;
            IExternalDataSource dataSource;

            if (TryGetEntityMetadata(callNode, binding, out delegationMetadata))
            {
                if (!binding.Document.Properties.EnabledFeatures.IsEnhancedDelegationEnabled ||
                    !TryGetValidDataSourceForDelegation(callNode, binding, DelegationCapability.ArrayLookup, out _))
                {
                    SuggestDelegationHint(callNode, binding);
                    return(false);
                }

                metadata = delegationMetadata.SortDelegationMetadata.VerifyValue();
            }
            else
            {
                if (!TryGetValidDataSourceForDelegation(callNode, binding, DelegationCapability.Sort, out dataSource))
                {
                    return(false);
                }

                metadata = dataSource.DelegationMetadata.SortDelegationMetadata;
            }

            TexlNode[] args = callNode.Args.Children.VerifyValue();
            TexlNode   arg1 = args[1].VerifyValue();

            // For now, we are only supporting delegation for Sort operations where second argument is column name.
            // For example, Sort(CDS, Value)
            FirstNameNode firstName = arg1.AsFirstName();

            if (firstName == null)
            {
                SuggestDelegationHint(arg1, binding);
                AddSuggestionMessageToTelemetry("Arg1 is not a FirstName node.", arg1, binding);
                DelegationTrackerCore.SetDelegationTrackerStatus(DelegationStatus.UnSupportedSortArg, arg1, binding, this, DelegationTelemetryInfo.CreateEmptyDelegationTelemetryInfo());
                return(false);
            }

            FirstNameInfo firstNameInfo = binding.GetInfo(firstName);

            if (firstNameInfo == null)
            {
                return(false);
            }

            DPath columnName = DPath.Root.Append(firstNameInfo.Name);

            if (!metadata.IsDelegationSupportedByColumn(columnName, DelegationCapability.Sort))
            {
                SuggestDelegationHint(firstName, binding);
                DelegationTrackerCore.SetDelegationTrackerStatus(DelegationStatus.NoDelSupportByColumn, firstName, binding, this, DelegationTelemetryInfo.CreateNoDelSupportByColumnTelemetryInfo(firstNameInfo));
                return(false);
            }

            const string defaultSortOrder = LanguageConstants.AscendingSortOrderString;
            int          cargs            = args.Count();

            // Verify that the third argument (If present) is an Enum or string literal.
            if (cargs < 3 && IsSortOrderSuppportedByColumn(callNode, binding, defaultSortOrder, metadata, columnName))
            {
                return(true);
            }

            // TASK: 6237100 - Binder: Propagate errors in subtree of the callnode to the call node itself
            // Only FirstName, DottedName and StrLit non-async nodes are supported for arg2.
            TexlNode arg2 = args[2].VerifyValue();

            if (!IsValidSortOrderNode(arg2, metadata, binding, columnName))
            {
                SuggestDelegationHint(arg2, binding);
                return(false);
            }

            return(true);
        }
        public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding)
        {
            Contracts.AssertValue(callNode);
            Contracts.AssertValue(binding);

            if (FunctionDelegationCapability.Capabilities == DelegationCapability.None)
            {
                return(false);
            }

            if (!CheckArgsCount(callNode, binding))
            {
                return(false);
            }

            IExternalDataSource dataSource;

            if (!TryGetValidDataSourceForDelegation(callNode, binding, FunctionDelegationCapability, out dataSource))
            {
                if (dataSource != null && dataSource.IsDelegatable)
                {
                    binding.ErrorContainer.EnsureError(DocumentErrorSeverity.Warning, callNode, TexlStrings.OpNotSupportedByServiceSuggestionMessage_OpNotSupportedByService, Name);
                }

                return(false);
            }

            var args = callNode.Args.Children.VerifyValue();

            if (binding.GetType(args[0]).HasExpandInfo ||
                (!binding.IsFullRecordRowScopeAccess(args[1]) && args[1].Kind != NodeKind.FirstName) ||
                !binding.IsRowScope(args[1]) ||
                binding.GetType(args[1]) != DType.Number ||
                ExpressionContainsView(callNode, binding))
            {
                SuggestDelegationHint(callNode, binding);

                if (binding.GetType(args[1]) != DType.Number)
                {
                    DelegationTrackerCore.SetDelegationTrackerStatus(DelegationStatus.NotANumberArgType, callNode, binding, this, DelegationTelemetryInfo.CreateEmptyDelegationTelemetryInfo());
                }
                else
                {
                    DelegationTrackerCore.SetDelegationTrackerStatus(DelegationStatus.InvalidArgType, callNode, binding, this, DelegationTelemetryInfo.CreateEmptyDelegationTelemetryInfo());
                }

                return(false);
            }

            if (binding.IsFullRecordRowScopeAccess(args[1]))
            {
                return(GetDottedNameNodeDelegationStrategy().IsValidDottedNameNode(args[1].AsDottedName(), binding, null, null));
            }

            var firstNameStrategy = GetFirstNameNodeDelegationStrategy().VerifyValue();

            return(firstNameStrategy.IsValidFirstNameNode(args[1].AsFirstName(), binding, null));
        }