Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Metadata / Edm / EdmFunction.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EdmFunction.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System.Collections.Generic;
11 using System.Diagnostics;
12 using System.Linq;
13 using System.Text;
14
15 namespace System.Data.Metadata.Edm
16 {
17     /// <summary>
18     /// Class for representing a function
19     /// </summary>
20     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
21     public sealed class EdmFunction : EdmType
22     {
23         #region Constructors
24         internal EdmFunction(string name, string namespaceName, DataSpace dataSpace, EdmFunctionPayload payload)
25             : base(name, namespaceName, dataSpace)
26         {
27             //---- name of the 'schema'
28             //---- this is used by the SQL Gen utility and update pipeline to support generation of the correct function name in the store
29             _schemaName = payload.Schema;
30             _fullName = this.NamespaceName + "." + this.Name;
31
32             FunctionParameter[] returnParameters = payload.ReturnParameters;
33
34            Debug.Assert(returnParameters.All((returnParameter) => returnParameter != null), "All return parameters must be non-null");
35            Debug.Assert(returnParameters.All((returnParameter) => returnParameter.Mode == ParameterMode.ReturnValue), "Return parameter in a function must have the ParameterMode equal to ReturnValue.");
36             
37             _returnParameters = new ReadOnlyMetadataCollection<FunctionParameter>(
38                 returnParameters
39                     .Select((returnParameter) => SafeLink<EdmFunction>.BindChild<FunctionParameter>(this, FunctionParameter.DeclaringFunctionLinker, returnParameter))
40                     .ToList());
41             
42             if (payload.IsAggregate.HasValue) SetFunctionAttribute(ref _functionAttributes, FunctionAttributes.Aggregate, payload.IsAggregate.Value);
43             if (payload.IsBuiltIn.HasValue) SetFunctionAttribute(ref _functionAttributes, FunctionAttributes.BuiltIn, payload.IsBuiltIn.Value);
44             if (payload.IsNiladic.HasValue) SetFunctionAttribute(ref _functionAttributes, FunctionAttributes.NiladicFunction, payload.IsNiladic.Value);
45             if (payload.IsComposable.HasValue) SetFunctionAttribute(ref _functionAttributes, FunctionAttributes.IsComposable, payload.IsComposable.Value);
46             if (payload.IsFromProviderManifest.HasValue) SetFunctionAttribute(ref _functionAttributes, FunctionAttributes.IsFromProviderManifest, payload.IsFromProviderManifest.Value);
47             if (payload.IsCachedStoreFunction.HasValue) SetFunctionAttribute(ref _functionAttributes, FunctionAttributes.IsCachedStoreFunction, payload.IsCachedStoreFunction.Value);
48             if (payload.IsFunctionImport.HasValue) SetFunctionAttribute(ref _functionAttributes, FunctionAttributes.IsFunctionImport, payload.IsFunctionImport.Value);
49
50             if (payload.ParameterTypeSemantics.HasValue)
51             {
52                 _parameterTypeSemantics = payload.ParameterTypeSemantics.Value;
53             }
54
55             if (payload.StoreFunctionName != null)
56             {
57                 _storeFunctionNameAttribute = payload.StoreFunctionName;
58             }
59
60             if (payload.EntitySets != null)
61             {
62                 Debug.Assert(_returnParameters.Count == payload.EntitySets.Length, "The number of entity sets should match the number of return parameters");
63                 _entitySets = new ReadOnlyMetadataCollection<EntitySet>(payload.EntitySets);
64             }
65             else
66             {
67                 var list = new List<EntitySet>();
68                 if (_returnParameters.Count != 0)
69                 {
70                     Debug.Assert(_returnParameters.Count == 1, "If there was more than one result set payload.EntitySets should not have been null");
71                     list.Add(null);
72                 }
73                 _entitySets = new ReadOnlyMetadataCollection<EntitySet>(list);
74             }
75
76             if (payload.CommandText != null)
77             {
78                 _commandTextAttribute = payload.CommandText;
79             }
80
81             if (payload.Parameters != null)
82             {
83                 // validate the parameters
84                 foreach (FunctionParameter parameter in payload.Parameters)
85                 {
86                     if (parameter == null)
87                     {
88                         throw EntityUtil.CollectionParameterElementIsNull("parameters");
89                     }
90                     Debug.Assert(parameter.Mode != ParameterMode.ReturnValue, "No function parameter can have ParameterMode equal to ReturnValue.");
91                 }
92
93                 // Populate the parameters
94                 _parameters = new SafeLinkCollection<EdmFunction, FunctionParameter>(this, FunctionParameter.DeclaringFunctionLinker, new MetadataCollection<FunctionParameter>(payload.Parameters));
95             }
96             else
97             {
98                 _parameters = new ReadOnlyMetadataCollection<FunctionParameter>(new MetadataCollection<FunctionParameter>());
99             }
100         }
101
102         #endregion
103
104         #region Fields
105         private readonly ReadOnlyMetadataCollection<FunctionParameter> _returnParameters;
106         private readonly ReadOnlyMetadataCollection<FunctionParameter> _parameters;
107         private readonly FunctionAttributes _functionAttributes = FunctionAttributes.Default;
108         private readonly string _storeFunctionNameAttribute;
109         private readonly ParameterTypeSemantics _parameterTypeSemantics;
110         private readonly string _commandTextAttribute;
111         private readonly string _schemaName;
112         private readonly ReadOnlyMetadataCollection<EntitySet> _entitySets;
113         private readonly string _fullName;
114         #endregion
115
116         #region Properties
117         /// <summary>
118         /// Returns the kind of the type
119         /// </summary>
120         public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.EdmFunction; } }
121
122         /// <summary>
123         /// Returns the full name of this type, which is namespace + "." + name. 
124         /// </summary>
125         public override string FullName
126         {
127             get
128             {
129                 return _fullName;
130             }
131         }
132
133         /// <summary>
134         /// Gets the collection of parameters
135         /// </summary>
136         public ReadOnlyMetadataCollection<FunctionParameter> Parameters
137         {
138             get
139             {
140                 return _parameters;
141             }
142         }
143
144         /// <summary>
145         /// Returns true if this is a C-space function and it has an eSQL body defined as DefiningExpression.
146         /// </summary>
147         internal bool HasUserDefinedBody
148         {
149             get
150             {
151                 return this.IsModelDefinedFunction && !String.IsNullOrEmpty(this.CommandTextAttribute);
152             }
153         }
154
155         /// <summary>
156         /// For function imports, optionally indicates the entity set to which the result is bound.
157         /// If the function import has multiple result sets, returns the entity set to which the first result is bound
158         /// </summary>
159         [MetadataProperty(BuiltInTypeKind.EntitySet, false)]
160         internal EntitySet EntitySet
161         {
162             get
163             {
164                 return _entitySets.Count != 0 ? _entitySets[0] : null;
165             }
166         }
167
168         /// <summary>
169         /// For function imports, indicates the entity sets to which the return parameters are bound.
170         /// The number of elements in the collection matches the number of return parameters. 
171         /// A null element in the collection indicates that the corresponding are not bound to an entity set.
172         /// </summary>
173         [MetadataProperty(BuiltInTypeKind.EntitySet, true)]
174         internal ReadOnlyMetadataCollection<EntitySet> EntitySets
175         {
176             get
177             {
178                 return _entitySets;
179             }
180         }
181
182         /// <summary>
183         /// Gets the return parameter of this function
184         /// </summary>
185         [MetadataProperty(BuiltInTypeKind.FunctionParameter, false)]
186         public FunctionParameter ReturnParameter
187         {
188             get
189             {
190                 return _returnParameters.FirstOrDefault();
191             }
192         }
193
194         /// <summary>
195         /// Gets the return parameters of this function
196         /// </summary>
197         [MetadataProperty(BuiltInTypeKind.FunctionParameter, true)]
198         public ReadOnlyMetadataCollection<FunctionParameter> ReturnParameters
199         {
200             get
201             {
202                 return _returnParameters;
203             }
204         }
205         
206         [MetadataProperty(PrimitiveTypeKind.String, false)]
207         internal string StoreFunctionNameAttribute
208         {
209             get { return _storeFunctionNameAttribute; }
210         }
211
212         [MetadataProperty(typeof(ParameterTypeSemantics), false)]
213         internal ParameterTypeSemantics ParameterTypeSemanticsAttribute
214         {
215             get { return _parameterTypeSemantics; }
216         }
217
218         // Function attribute parameters
219         [MetadataProperty(PrimitiveTypeKind.Boolean, false)]
220         internal bool AggregateAttribute
221         {
222             get
223             {
224                 return GetFunctionAttribute(FunctionAttributes.Aggregate);
225             }
226         }
227
228         [MetadataProperty(PrimitiveTypeKind.Boolean, false)]
229         internal bool BuiltInAttribute
230         {
231             get
232             {
233                 return GetFunctionAttribute(FunctionAttributes.BuiltIn);
234             }
235         }
236
237         [MetadataProperty(PrimitiveTypeKind.Boolean, false)]
238         internal bool IsFromProviderManifest
239         {
240             get
241             {
242                 return GetFunctionAttribute(FunctionAttributes.IsFromProviderManifest);
243             }
244         }
245
246         [MetadataProperty(PrimitiveTypeKind.Boolean, false)]
247         internal bool NiladicFunctionAttribute
248         {
249             get
250             {
251                 return GetFunctionAttribute(FunctionAttributes.NiladicFunction);
252             }
253         }
254
255         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Composable")]
256         [MetadataProperty(PrimitiveTypeKind.Boolean, false)]
257         public bool IsComposableAttribute
258         {
259             get
260             {
261                 return GetFunctionAttribute(FunctionAttributes.IsComposable);
262             }
263         }
264
265         [MetadataProperty(PrimitiveTypeKind.String, false)]
266         public string CommandTextAttribute
267         {
268             get
269             {
270                 return _commandTextAttribute;
271             }
272         }
273
274         internal bool IsCachedStoreFunction
275         {
276             get
277             {
278                 return GetFunctionAttribute(FunctionAttributes.IsCachedStoreFunction);
279             }
280         }
281
282         internal bool IsModelDefinedFunction
283         {
284             get
285             {
286                 return this.DataSpace == DataSpace.CSpace && !IsCachedStoreFunction && !IsFromProviderManifest && !IsFunctionImport;
287             }
288         }
289
290         internal bool IsFunctionImport
291         {
292             get
293             {
294                 return GetFunctionAttribute(FunctionAttributes.IsFunctionImport);
295             }
296         }
297
298         [MetadataProperty(PrimitiveTypeKind.String, false)]
299         internal string Schema
300         {
301             get
302             {
303                 return _schemaName;
304             }
305         }
306         #endregion
307
308         #region Methods
309         /// <summary>
310         /// Sets this item to be readonly, once this is set, the item will never be writable again.
311         /// </summary>
312         internal override void SetReadOnly()
313         {
314             if (!IsReadOnly)
315             {
316                 base.SetReadOnly();
317                 this.Parameters.Source.SetReadOnly();
318                 foreach (FunctionParameter returnParameter in ReturnParameters)
319                 {
320                     returnParameter.SetReadOnly();
321                 }
322             }
323         }
324
325         /// <summary>
326         /// Builds function identity string in the form of "functionName (param1, param2, ... paramN)".
327         /// </summary>
328         internal override void BuildIdentity(StringBuilder builder)
329         {
330             // If we've already cached the identity, simply append it
331             if (null != CacheIdentity)
332             {
333                 builder.Append(CacheIdentity);
334                 return;
335             }
336
337             EdmFunction.BuildIdentity(
338                 builder, 
339                 FullName, 
340                 Parameters,
341                 (param) => param.TypeUsage,
342                 (param) => param.Mode);
343         }
344
345         /// <summary>
346         /// Builds identity based on the functionName and parameter types. All parameters are assumed to be <see cref="ParameterMode.In"/>.
347         /// Returns string in the form of "functionName (param1, param2, ... paramN)".
348         /// </summary>
349         internal static string BuildIdentity(string functionName, IEnumerable<TypeUsage> functionParameters)
350         {
351             StringBuilder identity = new StringBuilder();
352             
353             BuildIdentity(
354                 identity, 
355                 functionName, 
356                 functionParameters,
357                 (param) => param,
358                 (param) => ParameterMode.In);
359             
360             return identity.ToString();
361         }
362
363         /// <summary>
364         /// Builds identity based on the functionName and parameters metadata.
365         /// Returns string in the form of "functionName (param1, param2, ... paramN)".
366         /// </summary>
367         internal static void BuildIdentity<TParameterMetadata>(StringBuilder builder, 
368                                                                string functionName, 
369                                                                IEnumerable<TParameterMetadata> functionParameters,
370                                                                Func<TParameterMetadata, TypeUsage> getParameterTypeUsage,
371                                                                Func<TParameterMetadata, ParameterMode> getParameterMode)
372         {
373             //
374             // Note: some callers depend on the format of the returned identity string.
375             //
376
377             // Start with the function name
378             builder.Append(functionName);
379             
380             // Then add the string representing the list of parameters
381             builder.Append('(');
382             bool first = true;
383             foreach (TParameterMetadata parameter in functionParameters)
384             {
385                 if (first) { first = false; }
386                 else { builder.Append(","); }
387                 builder.Append(Helper.ToString(getParameterMode(parameter)));
388                 builder.Append(' ');
389                 getParameterTypeUsage(parameter).BuildIdentity(builder);
390             }
391             builder.Append(')');
392         }
393
394         private bool GetFunctionAttribute(FunctionAttributes attribute)
395         {
396             return attribute == (attribute & _functionAttributes);
397         }
398
399         private static void SetFunctionAttribute(ref FunctionAttributes field, FunctionAttributes attribute, bool isSet)
400         {
401             if (isSet)
402             {
403                 // make sure that attribute bits are set to 1
404                 field |= attribute;
405             }
406             else
407             {
408                 // make sure that attribute bits are set to 0
409                 field ^= field & attribute;
410             }
411         }
412
413         #endregion
414
415         #region Nested types
416         [Flags]
417         private enum FunctionAttributes : byte
418         {
419             None = 0,
420             Aggregate = 1,
421             BuiltIn = 2,
422             NiladicFunction = 4,
423             IsComposable = 8,
424             IsFromProviderManifest = 16,
425             IsCachedStoreFunction = 32,
426             IsFunctionImport = 64,
427             Default = IsComposable,
428         }
429         #endregion
430     }
431
432     internal struct EdmFunctionPayload
433     {
434         public string Name;
435         public string NamespaceName;
436         public string Schema;
437         public string StoreFunctionName;
438         public string CommandText;
439         public EntitySet[] EntitySets;
440         public bool? IsAggregate;
441         public bool? IsBuiltIn;
442         public bool? IsNiladic;
443         public bool? IsComposable;
444         public bool? IsFromProviderManifest;
445         public bool? IsCachedStoreFunction;
446         public bool? IsFunctionImport;
447         public FunctionParameter[] ReturnParameters;
448         public ParameterTypeSemantics? ParameterTypeSemantics;
449         public FunctionParameter[] Parameters;
450         public DataSpace DataSpace;
451     }
452 }