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)); }
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); }
// 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); }
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); }
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); }
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); } }
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); }
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)); }