Exemple #1
0
            protected DelegationCapability ParseColumnCapability(JsonElement columnCapabilityJsonObj, string capabilityKey)
            {
                Contracts.AssertNonEmpty(capabilityKey);

                // Retrieve the entry for the column using column name as key.
                if (!columnCapabilityJsonObj.TryGetProperty(capabilityKey, out var functionsJsonArray))
                {
                    return(DelegationCapability.None);
                }

                DelegationCapability columnCapability = DelegationCapability.None;

                foreach (var op in functionsJsonArray.EnumerateArray())
                {
                    var operatorStr = op.GetString();
                    Contracts.AssertNonEmpty(operatorStr);

                    // If we don't support the operator then don't look at this capability.
                    if (!DelegationCapability.OperatorToDelegationCapabilityMap.ContainsKey(operatorStr))
                    {
                        continue;
                    }

                    columnCapability |= DelegationCapability.OperatorToDelegationCapabilityMap[operatorStr];
                }

                return(columnCapability);
            }
 public override bool IsDelegationSupportedByTable(DelegationCapability delegationCapability)
 {
     if (_filterFunctionsSupportedByTable.HasValue)
     {
         return(_filterFunctionsSupportedByTable.Value.HasCapability(delegationCapability.Capabilities));
     }
     else
     {
         return(base.IsDelegationSupportedByTable(delegationCapability)); /* This is needed for compatibility with older metadata */
     }
 }
Exemple #3
0
            public override OperationCapabilityMetadata Parse(JsonElement dataServiceCapabilitiesJsonObject, DType schema)
            {
                Contracts.AssertValid(schema);

                Dictionary <DPath, DelegationCapability> columnRestrictions = new Dictionary <DPath, DelegationCapability>();

                if (!dataServiceCapabilitiesJsonObject.TryGetProperty(CapabilitiesConstants.Sort_Restriction, out var sortRestrictionJsonObject))
                {
                    return(null);
                }

                if (sortRestrictionJsonObject.TryGetProperty(CapabilitiesConstants.Sort_UnsortableProperties, out var unSortablePropertiesJsonArray))
                {
                    foreach (var prop in unSortablePropertiesJsonArray.EnumerateArray())
                    {
                        var columnName = new DName(prop.GetString());
                        var columnPath = DPath.Root.Append(columnName);

                        if (!columnRestrictions.ContainsKey(columnPath))
                        {
                            columnRestrictions.Add(columnPath, new DelegationCapability(DelegationCapability.Sort));
                        }
                    }
                }

                if (sortRestrictionJsonObject.TryGetProperty(CapabilitiesConstants.Sort_AscendingOnlyProperties, out var acendingOnlyPropertiesJsonArray))
                {
                    foreach (var prop in acendingOnlyPropertiesJsonArray.EnumerateArray())
                    {
                        var columnName = new DName(prop.GetString());
                        var columnPath = DPath.Root.Append(columnName);

                        if (!columnRestrictions.ContainsKey(columnPath))
                        {
                            columnRestrictions.Add(columnPath, new DelegationCapability(DelegationCapability.SortAscendingOnly));
                            continue;
                        }

                        var existingRestrictions = columnRestrictions[columnPath].Capabilities;
                        columnRestrictions[columnPath] = new DelegationCapability(existingRestrictions | DelegationCapability.SortAscendingOnly);
                    }
                }

                return(new SortOpMetadata(schema, columnRestrictions));
            }
        public FilterOpMetadata(DType tableSchema, Dictionary <DPath, DelegationCapability> columnRestrictions,
                                Dictionary <DPath, DelegationCapability> columnCapabilities,
                                DelegationCapability filterFunctionsSupportedByAllColumns,
                                DelegationCapability?filterFunctionsSupportedByTable)
            : base(tableSchema)
        {
            Contracts.AssertValue(columnRestrictions);
            Contracts.AssertValue(columnCapabilities);

            _columnCapabilities = columnCapabilities;
            _columnRestrictions = columnRestrictions;
            _filterFunctionsSupportedByTable = filterFunctionsSupportedByTable;
            _defaultCapabilities             = filterFunctionsSupportedByAllColumns;
            if (_filterFunctionsSupportedByTable != null)
            {
                _defaultCapabilities = filterFunctionsSupportedByAllColumns | DelegationCapability.Filter;
            }
        }
        public override bool TryGetColumnCapabilities(DPath columnPath, out DelegationCapability capabilities)
        {
            Contracts.AssertValid(columnPath);

            // See if there is a specific capability defined for column.
            // If not then just return default one.
            if (!_columnCapabilities.TryGetValue(columnPath, out capabilities))
            {
                return(base.TryGetColumnCapabilities(columnPath, out capabilities));
            }

            // If metadata specified any restrictions for this column then apply those
            // before returning capabilities.
            DelegationCapability restrictions;

            if (TryGetColumnRestrictions(columnPath, out restrictions))
            {
                capabilities &= ~restrictions;
            }

            return(true);
        }
Exemple #6
0
        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();
            Contracts.Assert(args.Length >= MinArity);

            DelegationCapability funcDelegationCapability = FunctionDelegationCapability | (_isAnd ? DelegationCapability.And : DelegationCapability.Or);

            if (!metadata.IsDelegationSupportedByTable(funcDelegationCapability))
            {
                return(false);
            }

            foreach (var arg in args)
            {
                NodeKind argKind = arg.VerifyValue().Kind;
                switch (argKind)
                {
                case NodeKind.FirstName:
                {
                    var firstNameStrategy = GetFirstNameNodeDelegationStrategy();
                    if (!firstNameStrategy.IsValidFirstNameNode(arg.AsFirstName(), binding, null))
                    {
                        return(false);
                    }

                    break;
                }

                case NodeKind.Call:
                {
                    var cNodeStrategy = GetCallNodeDelegationStrategy();
                    if (!cNodeStrategy.IsValidCallNode(arg.AsCall(), binding, metadata))
                    {
                        SuggestDelegationHint(arg, binding);
                        return(false);
                    }

                    break;
                }

                case NodeKind.DottedName:
                {
                    var dottedStrategy = GetDottedNameNodeDelegationStrategy();
                    if (!dottedStrategy.IsValidDottedNameNode(arg.AsDottedName(), binding, metadata, null))
                    {
                        SuggestDelegationHint(arg, binding);
                        return(false);
                    }

                    break;
                }

                case NodeKind.BinaryOp:
                {
                    BinaryOpNode opNode = arg.AsBinaryOp();
                    var          binaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op, opNode);
                    if (!binaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding))
                    {
                        SuggestDelegationHint(arg, binding);
                        return(false);
                    }

                    break;
                }

                case NodeKind.UnaryOp:
                {
                    UnaryOpNode opNode = arg.AsUnaryOpLit();
                    var         unaryOpNodeValidationStrategy = GetOpDelegationStrategy(opNode.Op);
                    if (!unaryOpNodeValidationStrategy.IsSupportedOpNode(opNode, metadata, binding))
                    {
                        SuggestDelegationHint(arg, binding);
                        return(false);
                    }

                    break;
                }

                case NodeKind.BoolLit:
                    break;

                default:
                    return(false);
                }
            }

            return(true);
        }
Exemple #7
0
        // See if CountDistinct delegation is available. If true, we can make use of it on primary key as a workaround for CountRows delegation
        internal bool TryGetValidDataSourceForDelegation(CallNode callNode, TexlBinding binding, out IExternalDataSource dataSource, out DelegationCapability preferredFunctionDelegationCapability)
        {
            Contracts.AssertValue(callNode);
            Contracts.AssertValue(binding);

            preferredFunctionDelegationCapability = FunctionDelegationCapability;
            // We ensure Document is available because some tests run with a null Document.
            if ((binding.Document != null &&
                 binding.Document.Properties.EnabledFeatures.IsEnhancedDelegationEnabled) &&
                TryGetValidDataSourceForDelegation(callNode, binding, FunctionDelegationCapability, out dataSource) &&
                !ExpressionContainsView(callNode, binding))
            {
                // Check that target table is not an expanded entity (1-N/N-N relationships)
                // TASK 9966488: Enable CountRows/CountIf delegation for table relationships
                TexlNode[] args = callNode.Args.Children.VerifyValue();
                if (args.Length > 0)
                {
                    if (binding.GetType(args[0]).HasExpandInfo)
                    {
                        SuggestDelegationHint(callNode, binding);
                        return(false);
                    }
                    else
                    {
                        return(true);
                    }
                }
            }

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

            return(false);
        }
Exemple #8
0
 public MinMaxTableFunction(bool isMin)
     : base(isMin ? "Min" : "Max", isMin ? TexlStrings.AboutMinT : TexlStrings.AboutMaxT, FunctionCategories.Table)
 {
     _delegationCapability = isMin ? DelegationCapability.Min : DelegationCapability.Max;
 }
Exemple #9
0
            public override OperationCapabilityMetadata Parse(JsonElement dataServiceCapabilitiesJsonObject, DType schema)
            {
                Contracts.AssertValid(schema);

                // Check if any filter metadata is specified or not.
                var filterRestrictionExists     = dataServiceCapabilitiesJsonObject.TryGetProperty(CapabilitiesConstants.Filter_Restriction, out var filterRestrictionJsonObject);
                var globalFilterFunctionsExists = dataServiceCapabilitiesJsonObject.TryGetProperty(CapabilitiesConstants.Filter_Functions, out var globalFilterFunctionsJsonArray);
                var globalFilterSupportsExists  = dataServiceCapabilitiesJsonObject.TryGetProperty(CapabilitiesConstants.Filter_SupportedFunctions, out var globalFilterSupportedFunctionsJsonArray);
                var columnCapabilitiesExists    = dataServiceCapabilitiesJsonObject.TryGetProperty(CapabilitiesConstants.ColumnsCapabilities, out var columnCapabilitiesJsonObj);

                if (!filterRestrictionExists && !globalFilterFunctionsExists && !globalFilterSupportsExists && !columnCapabilitiesExists)
                {
                    return(null);
                }

                // Go through all filter restrictions if defined.
                var columnRestrictions = new Dictionary <DPath, DelegationCapability>();

                // If any nonFilterablepropertis exist then mark each column as such.
                if (filterRestrictionExists && filterRestrictionJsonObject.TryGetProperty(CapabilitiesConstants.Filter_NonFilterableProperties, out var nonFilterablePropertiesJsonArray))
                {
                    foreach (var prop in nonFilterablePropertiesJsonArray.EnumerateArray())
                    {
                        var columnName = DPath.Root.Append(new DName(prop.GetString()));
                        if (!columnRestrictions.ContainsKey(columnName))
                        {
                            columnRestrictions.Add(columnName, new DelegationCapability(DelegationCapability.Filter));
                        }
                    }
                }

                // Check for any FilterFunctions defined at table level.
                DelegationCapability filterFunctionsSupportedByAllColumns = DelegationCapability.None;

                if (globalFilterFunctionsExists)
                {
                    foreach (var op in globalFilterFunctionsJsonArray.EnumerateArray())
                    {
                        var operatorStr = op.GetString();
                        Contracts.AssertNonEmpty(operatorStr);

                        // If we don't support the operator then don't look at this capability.
                        if (!DelegationCapability.OperatorToDelegationCapabilityMap.ContainsKey(operatorStr))
                        {
                            continue;
                        }

                        // If filter functions are specified at table level then that means filter operation is supported.
                        filterFunctionsSupportedByAllColumns |= DelegationCapability.OperatorToDelegationCapabilityMap[operatorStr] | DelegationCapability.Filter;
                    }
                }

                // Check for any FilterSupportedFunctions defined at table level.
                DelegationCapability?filterFunctionsSupportedByTable = null;

                if (globalFilterSupportsExists)
                {
                    filterFunctionsSupportedByTable = DelegationCapability.None;
                    foreach (var op in globalFilterSupportedFunctionsJsonArray.EnumerateArray())
                    {
                        var operatorStr = op.GetString();
                        Contracts.AssertNonEmpty(operatorStr);

                        // If we don't support the operator then don't look at this capability.
                        if (!DelegationCapability.OperatorToDelegationCapabilityMap.ContainsKey(operatorStr))
                        {
                            continue;
                        }

                        // If filter functions are specified at table level then that means filter operation is supported.
                        filterFunctionsSupportedByTable |= DelegationCapability.OperatorToDelegationCapabilityMap[operatorStr] | DelegationCapability.Filter;
                    }
                }

                Dictionary <DPath, DelegationCapability> columnCapabilities = new Dictionary <DPath, DelegationCapability>();

                if (!columnCapabilitiesExists)
                {
                    return(new FilterOpMetadata(schema, columnRestrictions, columnCapabilities, filterFunctionsSupportedByAllColumns, filterFunctionsSupportedByTable));
                }

                // Sweep through all column filter capabilities.
                foreach (var column in columnCapabilitiesJsonObj.EnumerateObject())
                {
                    var columnPath = DPath.Root.Append(new DName(column.Name));

                    // Internal columns don't appear in schema and we don't gather any information about it as they don't appear in expressions.
                    // Task 790576: Runtime should provide visibility information along with delegation metadata information per column
                    if (!schema.Contains(columnPath))
                    {
                        continue;
                    }

                    // Get capabilities object for column
                    var capabilitiesDefinedByColumn = column.Value;

                    // Get properties object for the column
                    if (capabilitiesDefinedByColumn.TryGetProperty(CapabilitiesConstants.Properties, out var propertyCapabilities))
                    {
                        foreach (var property in propertyCapabilities.EnumerateObject())
                        {
                            var propertyPath = columnPath.Append(new DName(property.Name));
                            var capabilitiesDefinedByColumnProperty = property.Value;

                            if (!capabilitiesDefinedByColumnProperty.TryGetProperty(CapabilitiesConstants.Capabilities, out var propertyCapabilityJsonObject))
                            {
                                continue;
                            }

                            var propertyCapability = ParseColumnCapability(propertyCapabilityJsonObject, capabilityKey: CapabilitiesConstants.Filter_Functions);
                            if (propertyCapability.Capabilities != DelegationCapability.None)
                            {
                                Contracts.Assert(schema.Contains(propertyPath));

                                // If column is specified as non-filterable then this metadata shouldn't be present.
                                // But if it is present then we should ignore it.
                                if (!columnRestrictions.ContainsKey(propertyPath))
                                {
                                    columnCapabilities.Add(propertyPath, propertyCapability | DelegationCapability.Filter);
                                }
                            }
                        }
                    }

                    // Get capability object defined for column.
                    // This is optional as for columns with complex types (nested table or record), it will have "properties" key instead.
                    // We are not supporting that case for now. So we ignore it currently.
                    if (!capabilitiesDefinedByColumn.TryGetProperty(CapabilitiesConstants.Capabilities, out var capabilityJsonObject))
                    {
                        continue;
                    }

                    var isChoice = capabilityJsonObject.TryGetProperty(CapabilitiesConstants.PropertyIsChoice, out var isChoiceElement) && isChoiceElement.GetBoolean();

                    var capability = ParseColumnCapability(capabilityJsonObject, capabilityKey: CapabilitiesConstants.Filter_Functions);
                    if (capability.Capabilities != DelegationCapability.None)
                    {
                        Contracts.Assert(schema.Contains(columnPath));

                        // If column is specified as non-filterable then this metadata shouldn't be present.
                        // But if it is present then we should ignore it.
                        if (!columnRestrictions.ContainsKey(columnPath))
                        {
                            columnCapabilities.Add(columnPath, capability | DelegationCapability.Filter);
                        }

                        if (isChoice == true)
                        {
                            var choicePropertyPath = columnPath.Append(new DName(ODataMetaParser.ValueProperty));
                            if (!columnRestrictions.ContainsKey(choicePropertyPath))
                            {
                                columnCapabilities.Add(choicePropertyPath, capability | DelegationCapability.Filter);
                            }
                        }
                    }
                }

                return(new FilterOpMetadata(schema, columnRestrictions, columnCapabilities, filterFunctionsSupportedByAllColumns, filterFunctionsSupportedByTable));
            }
Exemple #10
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);
        }