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)); }
public bool IsValidFirstNameNode(FirstNameNode node, TexlBinding binding, IOpDelegationStrategy opDelStrategy) { Contracts.AssertValue(node); Contracts.AssertValue(binding); Contracts.AssertValueOrNull(opDelStrategy); var isRowScoped = binding.IsRowScope(node); var isValid = IsValidNode(node, binding); if (isValid && !isRowScoped) { return(true); } // If invalid node then return immediately. if (!isValid) { return(false); } return(IsDelegatableColumnNode(node, binding, opDelStrategy, _function.FunctionDelegationCapability)); }
public override bool IsRowScopedServerDelegatable(CallNode callNode, TexlBinding binding, OperationCapabilityMetadata metadata) { Contracts.AssertValue(callNode); Contracts.AssertValue(binding); Contracts.AssertValue(metadata); if (binding.ErrorContainer.HasErrors(callNode) || !CheckArgsCount(callNode, binding) || !binding.IsRowScope(callNode)) { return(false); } TexlNode[] args = callNode.Args.Children.VerifyValue(); NodeKind argKind = args[0].VerifyValue().Kind; var opStrategy = GetOpDelegationStrategy(UnaryOp.Not); var firstNameStrategy = GetFirstNameNodeDelegationStrategy(); var dottedStrategy = GetDottedNameNodeDelegationStrategy(); var cNodeStrategy = GetCallNodeDelegationStrategy(); switch (argKind) { case NodeKind.FirstName: return(firstNameStrategy.IsValidFirstNameNode(args[0].AsFirstName(), binding, opStrategy)); case NodeKind.Call: { if (!opStrategy.IsOpSupportedByTable(metadata, callNode, binding)) { return(false); } return(cNodeStrategy.IsValidCallNode(args[0].AsCall(), binding, metadata)); } case NodeKind.BinaryOp: { if (!opStrategy.IsOpSupportedByTable(metadata, callNode, binding)) { return(false); } BinaryOpNode opNode = args[0].AsBinaryOp(); IOpDelegationStrategy binaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op, opNode); return(binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding)); } case NodeKind.UnaryOp: { if (!opStrategy.IsOpSupportedByTable(metadata, callNode, binding)) { return(false); } UnaryOpNode opNode = args[0].AsUnaryOpLit(); var unaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op); return(unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding)); } case NodeKind.DottedName: return(dottedStrategy.IsValidDottedNameNode(args[0].AsDottedName(), binding, metadata, opStrategy)); default: return(argKind == NodeKind.BoolLit); } }
// Verifies if given kind of node is supported by function delegation. private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool isRHSNode) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); Contracts.AssertValue(binding); Contracts.AssertValue(opDelStrategy); if (!binding.IsRowScope(node)) { // Check whether this is - // 1) in operator delegation and // 2) it is verifying if RHS node is supported and // 3) it is not an async node and // 4) it is a single column table and // 5) metadata belongs to cds datasource that supports delegation of CdsIn // If this check fails, verify if it is simply a valid node.. // Eg of valid delegation functions - // Filter(Accounts, 'Account Name' in ["Foo", Bar"]) - Direct table use // Set(Names, ["Foo", Bar"]); Filter(Accounts, 'Account Name' in Names) - Using variable of type table // ClearCollect(Names, Accounts); Filter(Accounts, 'Account Name' in Names.'Account Name') - using column from collection. // This won't be delegated - Filter(Accounts, 'Account Name' in Accounts.'Account Name') as Accounts.'Account Name' is async. if ((binding.Document.Properties.EnabledFeatures.IsEnhancedDelegationEnabled && isRHSNode && (opDelStrategy as BinaryOpDelegationStrategy)?.Op == BinaryOp.In && !binding.IsAsync(node) && binding.GetType(node).IsTable&& binding.GetType(node).IsColumn&& metadata.IsDelegationSupportedByTable(DelegationCapability.CdsIn)) || IsValidNode(node, binding)) { return(true); } } switch (node.Kind) { case NodeKind.DottedName: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var dottedNodeValStrategy = _function.GetDottedNameNodeDelegationStrategy(); return(dottedNodeValStrategy.IsValidDottedNameNode(node.AsDottedName(), binding, metadata, opDelStrategy)); } case NodeKind.Call: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var cNodeValStrategy = _function.GetCallNodeDelegationStrategy(); return(cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata)); } case NodeKind.FirstName: { var firstNameNodeValStrategy = _function.GetFirstNameNodeDelegationStrategy(); return(firstNameNodeValStrategy.IsValidFirstNameNode(node.AsFirstName(), binding, opDelStrategy)); } case NodeKind.UnaryOp: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var unaryopNode = node.AsUnaryOpLit(); IOpDelegationStrategy unaryOpNodeDelegationStrategy = _function.GetOpDelegationStrategy(unaryopNode.Op); return(unaryOpNodeDelegationStrategy.IsSupportedOpNode(unaryopNode, metadata, binding)); } case NodeKind.BinaryOp: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var binaryOpNode = node.AsBinaryOp().VerifyValue(); opDelStrategy = _function.GetOpDelegationStrategy(binaryOpNode.Op, binaryOpNode); var binaryOpDelStrategy = (opDelStrategy as BinaryOpDelegationStrategy).VerifyValue(); Contracts.Assert(binaryOpNode.Op == binaryOpDelStrategy.Op); if (!opDelStrategy.IsSupportedOpNode(node, metadata, binding)) { SuggestDelegationHint(binaryOpNode, binding); return(false); } break; } default: { var kind = node.Kind; if (kind != NodeKind.BoolLit && kind != NodeKind.StrLit && kind != NodeKind.NumLit) { var telemetryMessage = string.Format("NodeKind {0} unsupported.", kind); SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage); return(false); } break; } } return(true); }
private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); Contracts.AssertValue(binding); Contracts.AssertValue(opDelStrategy); if (!binding.IsRowScope(node)) { return(true); } switch (node.Kind) { case NodeKind.DottedName: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var dottedNodeValStrategy = _function.GetDottedNameNodeDelegationStrategy(); return(dottedNodeValStrategy.IsValidDottedNameNode(node.AsDottedName(), binding, metadata, opDelStrategy)); } case NodeKind.Call: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var cNodeValStrategy = _function.GetCallNodeDelegationStrategy(); return(cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata)); } case NodeKind.FirstName: { var firstNameNodeValStrategy = _function.GetFirstNameNodeDelegationStrategy(); return(firstNameNodeValStrategy.IsValidFirstNameNode(node.AsFirstName(), binding, opDelStrategy)); } case NodeKind.UnaryOp: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } UnaryOpNode unaryOpNode = node.AsUnaryOpLit().VerifyValue(); opDelStrategy = _function.GetOpDelegationStrategy(unaryOpNode.Op).VerifyValue(); var unaryOpDelStrategy = (opDelStrategy as UnaryOpDelegationStrategy).VerifyValue(); Contracts.Assert(unaryOpDelStrategy.Op == unaryOpNode.Op); if (!opDelStrategy.IsSupportedOpNode(node, metadata, binding)) { SuggestDelegationHint(node, binding); return(false); } return(true); } case NodeKind.BinaryOp: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var binaryOpNode = node.AsBinaryOp().VerifyValue(); IOpDelegationStrategy binaryOpNodeDelValidationStrategy = _function.GetOpDelegationStrategy(binaryOpNode.Op, binaryOpNode); return(binaryOpNodeDelValidationStrategy.IsSupportedOpNode(node.AsBinaryOp(), metadata, binding)); } } SuggestDelegationHint(node, binding); return(false); }
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 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); }