Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntityModel / SchemaObjectModel / Function.cs
1 //---------------------------------------------------------------------
2 // <copyright file="Function.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.EntityModel.SchemaObjectModel
11 {
12     using System;
13     using System.Collections.Generic;
14     using System.Collections.ObjectModel;
15     using System.Data.Entity;
16     using System.Data.Metadata.Edm;
17     using System.Diagnostics;
18     using System.Xml;
19
20     /// <summary>
21     /// class representing the Schema element in the schema
22     /// </summary>
23     internal class Function : SchemaType
24     {
25         #region Instance Fields
26         // if adding properties also add to InitializeObject()!
27         private bool _isAggregate = false;
28         private bool _isBuiltIn = false;
29         private bool _isNiladicFunction = false;
30         protected bool _isComposable = true;
31         protected FunctionCommandText _commandText = null;
32         private string _storeFunctionName = null;
33         protected SchemaType _type = null;
34         private string _unresolvedType = null;
35         protected bool _isRefType = false;
36         // both are not specified
37         protected SchemaElementLookUpTable<Parameter> _parameters = null;
38         protected List<ReturnType> _returnTypeList = null;
39         private CollectionKind _returnTypeCollectionKind = CollectionKind.None;
40         private ParameterTypeSemantics _parameterTypeSemantics;
41         private string _schema;
42
43         private string _functionStrongName;
44         #endregion
45
46         #region Static Fields
47
48         private static System.Text.RegularExpressions.Regex s_typeParser = new System.Text.RegularExpressions.Regex(@"^(?<modifier>((Collection)|(Ref)))\s*\(\s*(?<typeName>\S*)\s*\)$", System.Text.RegularExpressions.RegexOptions.Compiled);
49
50         /// <summary>
51         /// 
52         /// </summary>
53         /// <param name="type"></param>
54         /// <returns></returns>
55         internal static void RemoveTypeModifier(ref string type, out TypeModifier typeModifier, out bool isRefType)
56         {
57             isRefType = false;
58             typeModifier = TypeModifier.None;
59
60             System.Text.RegularExpressions.Match match = s_typeParser.Match(type);
61             if (match.Success)
62             {
63                 type = match.Groups["typeName"].Value;
64                 switch (match.Groups["modifier"].Value)
65                 {
66                     case "Collection":
67                         typeModifier = TypeModifier.Array;
68                         return;
69                     case "Ref":
70                         isRefType = true;
71                         return;
72                     default:
73                         Debug.Assert(false, "Unexpected modifier: " + match.Groups["modifier"].Value);
74                         break;
75                 }
76             }
77             
78         }
79
80         internal static string GetTypeNameForErrorMessage(SchemaType type, CollectionKind colKind, bool isRef)
81         {
82             string typeName = type.FQName;
83             if (isRef)
84             {
85                 typeName = "Ref(" + typeName + ")";
86             }
87             switch (colKind)
88             {
89                 case CollectionKind.Bag:
90                     typeName = "Collection(" + typeName + ")";
91                     break;
92                 default:
93                     Debug.Assert(colKind == CollectionKind.None, "Unexpected CollectionKind");
94                     break;
95             }
96             return typeName;
97         }
98         #endregion
99
100         #region Public Methods
101         /// <summary>
102         /// ctor for a schema function
103         /// </summary>
104         public Function(Schema parentElement)
105             : base(parentElement)
106         {
107         }
108         #endregion
109
110         #region Public Properties
111
112         public bool IsAggregate
113         {
114             get
115             {
116                 return _isAggregate;
117             }
118             internal set
119             {
120                 _isAggregate = value;
121             }
122         }
123
124         public bool IsBuiltIn
125         {
126             get
127             {
128                 return _isBuiltIn;
129             }
130             internal set
131             {
132                 _isBuiltIn = value;
133             }
134         }
135
136         public bool IsNiladicFunction
137         {
138             get
139             {
140                 return _isNiladicFunction;
141             }
142             internal set
143             {
144                 _isNiladicFunction = value;
145             }
146         }
147
148         public bool IsComposable
149         {
150             get
151             {
152                 return _isComposable;
153             }
154             internal set
155             {
156                 _isComposable = value;
157             }
158         }
159
160         public string CommandText
161         {
162             get
163             {
164                 if (_commandText != null)
165                 {
166                     return _commandText.CommandText;
167                 }
168                 return null;
169             }
170         }
171
172         public ParameterTypeSemantics ParameterTypeSemantics
173         {
174             get
175             {
176                 return _parameterTypeSemantics;
177             }
178             internal set
179             {
180                 _parameterTypeSemantics = value;
181             }
182         }
183
184         public string StoreFunctionName
185         {
186             get
187             {
188                 return _storeFunctionName;
189             }
190             internal set
191             {
192                 Debug.Assert(value != null, "StoreFunctionName should never be set null value");
193                 _storeFunctionName = value;
194             }
195         }
196
197         public virtual SchemaType Type
198         {
199             get
200             {
201                 if (null != _returnTypeList)
202                 {
203                     Debug.Assert(_returnTypeList.Count == 1, "Shouldn't use Type if there could be multiple return types");
204                     return this._returnTypeList[0].Type;
205                 }
206                 else
207                 {
208                     return this._type;
209                 }
210             }
211         }
212
213         public IList<ReturnType> ReturnTypeList
214         {
215             get
216             {
217                 return null != _returnTypeList ? new ReadOnlyCollection<ReturnType>(_returnTypeList) : null;
218             }
219         }
220
221         public SchemaElementLookUpTable<Parameter> Parameters
222         {
223             get
224             {
225                 if (_parameters == null)
226                 {
227                     _parameters = new SchemaElementLookUpTable<Parameter>();
228                 }
229                 return _parameters;
230             }
231         }
232
233         public CollectionKind CollectionKind
234         {
235             get
236             {
237                 return _returnTypeCollectionKind;
238             }
239             internal set
240             {
241                 _returnTypeCollectionKind = value;
242             }
243         }
244
245         public override string Identity
246         {
247             get
248             {
249                 if (String.IsNullOrEmpty(_functionStrongName))
250                 {
251                     string name = this.FQName;
252                     System.Text.StringBuilder stringBuilder = new Text.StringBuilder(name);
253                     bool first = true;
254                     stringBuilder.Append('(');
255                     foreach (Parameter parameter in this.Parameters)
256                     {
257                         if (!first)
258                         {
259                             stringBuilder.Append(',');
260                         }
261                         else
262                         {
263                             first = false;
264                         }
265                         stringBuilder.Append(Helper.ToString(parameter.ParameterDirection));
266                         stringBuilder.Append(' ');
267                         // we don't include the facets in the identity, since we are *not*
268                         // taking them into consideration inside the 
269                         // RankFunctionParameters method of TypeResolver.cs
270
271                         parameter.WriteIdentity(stringBuilder);
272                     }
273                     stringBuilder.Append(')');
274                     _functionStrongName = stringBuilder.ToString();
275                 }
276                 return _functionStrongName;
277             }
278         }
279
280         public bool IsReturnAttributeReftype
281         {
282             get
283             {
284                 return _isRefType;
285             }
286         }
287
288         public virtual bool IsFunctionImport { get { return false; } }
289
290         public string DbSchema
291         {
292             get
293             {
294                 return _schema;
295             }
296         }
297
298         #endregion
299
300         #region Protected Properties
301         protected override bool HandleElement(XmlReader reader)
302         {
303             if (base.HandleElement(reader))
304             {
305                 return true;
306             }
307             else if (CanHandleElement(reader, XmlConstants.CommandText))
308             {
309                 HandleCommandTextFunctionElment(reader);
310                 return true;
311             }
312             else if (CanHandleElement(reader, XmlConstants.Parameter))
313             {
314                 HandleParameterElement(reader);
315                 return true;
316             }
317             else if (CanHandleElement(reader, XmlConstants.ReturnTypeElement))
318             {
319                 HandleReturnTypeElement(reader);
320                 return true;
321             }
322             else if (Schema.DataModel == SchemaDataModelOption.EntityDataModel)
323             {
324                 if (CanHandleElement(reader, XmlConstants.ValueAnnotation))
325                 {
326                     // EF does not support this EDM 3.0 element, so ignore it.
327                     SkipElement(reader);
328                     return true;
329                 }
330                 else if (CanHandleElement(reader, XmlConstants.TypeAnnotation))
331                 {
332                     // EF does not support this EDM 3.0 element, so ignore it.
333                     SkipElement(reader);
334                     return true;
335                 }
336             }
337             return false;
338         }
339
340         protected override bool HandleAttribute(XmlReader reader)
341         {
342             if (base.HandleAttribute(reader))
343             {
344                 return true;
345             }
346             else if (CanHandleAttribute(reader, XmlConstants.ReturnType))
347             {
348                 HandleReturnTypeAttribute(reader);
349                 return true;
350             }
351             else if (CanHandleAttribute(reader, XmlConstants.AggregateAttribute))
352             {
353                 HandleAggregateAttribute(reader);
354                 return true;
355             }
356             else if (CanHandleAttribute(reader, XmlConstants.BuiltInAttribute))
357             {
358                 HandleBuiltInAttribute(reader);
359                 return true;
360             }
361             else if (CanHandleAttribute(reader, XmlConstants.StoreFunctionName))
362             {
363                 HandleStoreFunctionNameAttribute(reader);
364                 return true;
365             }
366             else if (CanHandleAttribute(reader, XmlConstants.NiladicFunction))
367             {
368                 HandleNiladicFunctionAttribute(reader);
369                 return true;
370             }
371             else if (CanHandleAttribute(reader, XmlConstants.IsComposable))
372             {
373                 HandleIsComposableAttribute(reader);
374                 return true;
375             }
376             else if (CanHandleAttribute(reader, XmlConstants.ParameterTypeSemantics))
377             {
378                 HandleParameterTypeSemanticsAttribute(reader);
379                 return true;
380             }
381             else if (CanHandleAttribute(reader, XmlConstants.Schema))
382             {
383                 HandleDbSchemaAttribute(reader);
384                 return true;
385             }
386
387             return false;
388         }
389         #endregion
390
391         #region Internal Methods
392
393         internal override void ResolveTopLevelNames()
394         {
395             base.ResolveTopLevelNames();
396
397             if (_unresolvedType != null)
398             {
399                 Debug.Assert(Schema.DataModel != SchemaDataModelOption.ProviderManifestModel, "ProviderManifest cannot have ReturnType as an attribute");
400                 Schema.ResolveTypeName(this, UnresolvedReturnType, out _type);
401             }
402
403             if (null != _returnTypeList)
404             {
405                 foreach (ReturnType returnType in _returnTypeList)
406                 {
407                     returnType.ResolveTopLevelNames();
408                 }
409             }
410
411             foreach (Parameter parameter in this.Parameters)
412             {
413                 parameter.ResolveTopLevelNames();
414             }
415         }
416
417         /// <summary>
418         /// Perform local validation on function definition.
419         /// </summary>
420         internal override void Validate()
421         {
422             base.Validate();
423
424             if (_type != null && _returnTypeList != null)
425             {
426                 AddError(ErrorCode.ReturnTypeDeclaredAsAttributeAndElement, EdmSchemaErrorSeverity.Error, Strings.TypeDeclaredAsAttributeAndElement);
427             }
428
429             // only call Type if _returnTypeList is empty, to ensure that we don't it when 
430             // _returnTypeList has more than one element.
431             if (this._returnTypeList == null && this.Type == null)
432             {
433                 // Composable functions and function imports must declare return type.
434                 if (this.IsComposable)
435                 {
436                     AddError(ErrorCode.ComposableFunctionOrFunctionImportWithoutReturnType, EdmSchemaErrorSeverity.Error,
437                         Strings.ComposableFunctionOrFunctionImportMustDeclareReturnType);
438                 }
439             }
440             else
441             {
442                 // Non-composable functions (except function imports) must not declare a return type.
443                 if (!this.IsComposable && !this.IsFunctionImport)
444                 {
445                     AddError(ErrorCode.NonComposableFunctionWithReturnType, EdmSchemaErrorSeverity.Error,
446                         Strings.NonComposableFunctionMustNotDeclareReturnType);
447                 }
448             }
449
450             if (Schema.DataModel != SchemaDataModelOption.EntityDataModel)
451             {
452                 if (IsAggregate)
453                 {
454
455                     // Make sure that the function has exactly one parameter and that takes
456                     // a collection type
457                     if (Parameters.Count != 1)
458                     {
459                         AddError(ErrorCode.InvalidNumberOfParametersForAggregateFunction,
460                                  EdmSchemaErrorSeverity.Error,
461                                  this,
462                                  System.Data.Entity.Strings.InvalidNumberOfParametersForAggregateFunction(FQName));
463                     }
464                     else if (Parameters.GetElementAt(0).CollectionKind == CollectionKind.None)
465                     {
466                         // Since we have already checked that there should be exactly one parameter, it should be safe to get the
467                         // first parameter for the function
468                         Parameter param = Parameters.GetElementAt(0);
469
470                         AddError(ErrorCode.InvalidParameterTypeForAggregateFunction,
471                                  EdmSchemaErrorSeverity.Error,
472                                  this,
473                                  System.Data.Entity.Strings.InvalidParameterTypeForAggregateFunction(param.Name, FQName));
474                     }
475
476                 }
477
478                 if (!this.IsComposable)
479                 {
480                     // All aggregates, built-in and niladic functions must be composable, so throw error here.
481                     if (this.IsAggregate ||
482                         this.IsNiladicFunction ||
483                         this.IsBuiltIn)
484                     {
485                         AddError(ErrorCode.NonComposableFunctionAttributesNotValid, EdmSchemaErrorSeverity.Error,
486                             Strings.NonComposableFunctionHasDisallowedAttribute);
487                     }
488                 }
489
490                 if (null != this.CommandText)
491                 {
492                     // Functions with command text are not composable.
493                     if (this.IsComposable)
494                     {
495                         AddError(ErrorCode.ComposableFunctionWithCommandText, EdmSchemaErrorSeverity.Error,
496                             Strings.CommandTextFunctionsNotComposable);
497                     }
498
499                     // Functions with command text cannot declare store function name.
500                     if (null != this.StoreFunctionName)
501                     {
502                         AddError(ErrorCode.FunctionDeclaresCommandTextAndStoreFunctionName, EdmSchemaErrorSeverity.Error,
503                             Strings.CommandTextFunctionsCannotDeclareStoreFunctionName);
504                     }
505                 }
506             }
507
508             if (Schema.DataModel == SchemaDataModelOption.ProviderDataModel)
509             {
510                 // In SSDL function may return a primitive value or a collection of rows with scalar props.
511                 // It is not possible to encode "collection of rows" in the ReturnType attribute, so the only check needed here is to make sure that the type is scalar and not a collection.
512                 if (_type != null && (_type is ScalarType == false || _returnTypeCollectionKind != Metadata.Edm.CollectionKind.None))
513                 {
514                     AddError(ErrorCode.FunctionWithNonPrimitiveTypeNotSupported,
515                              EdmSchemaErrorSeverity.Error,
516                              this,
517                              System.Data.Entity.Strings.FunctionWithNonPrimitiveTypeNotSupported(GetTypeNameForErrorMessage(_type, _returnTypeCollectionKind, _isRefType), this.FQName));
518                 }
519             }
520
521             if (_returnTypeList != null)
522             {
523                 foreach (ReturnType returnType in _returnTypeList)
524                 {
525                     // FunctiomImportElement has additional validation for return types.
526                     returnType.Validate();
527                 }
528             }
529
530             if (_parameters != null)
531             {
532                 foreach (var parameter in _parameters)
533                 {
534                     parameter.Validate();
535                 }
536             }
537
538             if (_commandText != null)
539             {
540                 _commandText.Validate();
541             }
542         }
543
544         internal override void ResolveSecondLevelNames()
545         {
546             foreach (var parameter in _parameters)
547             {
548                 parameter.ResolveSecondLevelNames();
549             }
550         }
551
552         internal override SchemaElement Clone(SchemaElement parentElement)
553         {
554             // We only support clone for FunctionImports.
555             throw Error.NotImplemented();
556         }
557         protected void CloneSetFunctionFields(Function clone)
558         {
559             clone._isAggregate = _isAggregate;
560             clone._isBuiltIn = _isBuiltIn;
561             clone._isNiladicFunction = _isNiladicFunction;
562             clone._isComposable = _isComposable;
563             clone._commandText = _commandText;
564             clone._storeFunctionName = _storeFunctionName;
565             clone._type = _type;
566             clone._returnTypeList = _returnTypeList;
567             clone._returnTypeCollectionKind = _returnTypeCollectionKind;
568             clone._parameterTypeSemantics = _parameterTypeSemantics;
569             clone._schema = _schema;
570             clone.Name = this.Name;
571
572             // Clone all the parameters
573             foreach (Parameter parameter in this.Parameters)
574             {
575                 AddErrorKind error = clone.Parameters.TryAdd((Parameter)parameter.Clone(clone));
576                 Debug.Assert(error == AddErrorKind.Succeeded, "Since we are cloning a validated function, this should never fail.");
577             }
578         }
579         #endregion
580
581         #region Internal Properties
582         /// <summary>
583         /// 
584         /// </summary>
585         /// <value></value>
586         internal string UnresolvedReturnType
587         {
588             get
589             {
590                 return _unresolvedType;
591             }
592             set
593             {
594                 _unresolvedType = value;
595             }
596         }
597         #endregion //Internal Properties
598
599         #region Private Methods
600
601         /// <summary>
602         /// The method that is called when a DbSchema attribute is encountered.
603         /// </summary>
604         /// <param name="reader">An XmlReader positioned at the Type attribute.</param>
605         private void HandleDbSchemaAttribute(XmlReader reader)
606         {
607             Debug.Assert(Schema.DataModel == SchemaDataModelOption.ProviderDataModel, "We shouldn't see this attribute unless we are parsing ssdl");
608             Debug.Assert(reader != null);
609
610             _schema = reader.Value;
611         }
612
613         /// <summary>
614         /// Handler for the Version attribute
615         /// </summary>
616         /// <param name="reader">xml reader currently positioned at Version attribute</param>
617         private void HandleAggregateAttribute(XmlReader reader)
618         {
619             Debug.Assert(reader != null);
620             bool isAggregate = false;
621             HandleBoolAttribute(reader, ref isAggregate);
622             IsAggregate = isAggregate;
623         }
624
625         /// <summary>
626         /// Handler for the Namespace attribute
627         /// </summary>
628         /// <param name="reader">xml reader currently positioned at Namespace attribute</param>
629         private void HandleBuiltInAttribute(XmlReader reader)
630         {
631             Debug.Assert(reader != null);
632             bool isBuiltIn = false;
633             HandleBoolAttribute(reader, ref isBuiltIn);
634             IsBuiltIn = isBuiltIn;
635         }
636
637         /// <summary>
638         /// Handler for the Alias attribute
639         /// </summary>
640         /// <param name="reader">xml reader currently positioned at Alias attribute</param>
641         private void HandleStoreFunctionNameAttribute(XmlReader reader)
642         {
643             Debug.Assert(reader != null);
644             string value = reader.Value.ToString();
645             if (!String.IsNullOrEmpty(value))
646             {
647                 value = value.Trim();
648                 StoreFunctionName = value;
649             }
650         }
651
652         /// <summary>
653         /// Handler for the NiladicFunctionAttribute attribute
654         /// </summary>
655         /// <param name="reader">xml reader currently positioned at Namespace attribute</param>
656         private void HandleNiladicFunctionAttribute(XmlReader reader)
657         {
658             Debug.Assert(reader != null);
659             bool isNiladicFunction = false;
660             HandleBoolAttribute(reader, ref isNiladicFunction);
661             IsNiladicFunction = isNiladicFunction;
662         }
663
664         /// <summary>
665         /// Handler for the IsComposableAttribute attribute
666         /// </summary>
667         /// <param name="reader">xml reader currently positioned at Namespace attribute</param>
668         private void HandleIsComposableAttribute(XmlReader reader)
669         {
670             Debug.Assert(reader != null);
671             bool isComposable = true;
672             HandleBoolAttribute(reader, ref isComposable);
673             IsComposable = isComposable;
674         }
675
676         private void HandleCommandTextFunctionElment(XmlReader reader)
677         {
678             Debug.Assert(reader != null);
679
680             FunctionCommandText commandText = new FunctionCommandText(this);
681             commandText.Parse(reader);
682             _commandText = commandText;
683         }
684
685         protected virtual void HandleReturnTypeAttribute(XmlReader reader)
686         {
687             Debug.Assert(reader != null);
688             Debug.Assert(UnresolvedReturnType == null);
689
690             string type;
691             if (!Utils.GetString(Schema, reader, out type))
692                 return;
693
694             TypeModifier typeModifier;
695
696             RemoveTypeModifier(ref type, out typeModifier, out _isRefType);
697
698             switch (typeModifier)
699             {
700                 case TypeModifier.Array:
701                     CollectionKind = CollectionKind.Bag;
702                     break;
703                 case TypeModifier.None:
704                     break;
705                 default:
706                     Debug.Assert(false, "RemoveTypeModifier already checks for this");
707                     break;
708             }
709
710             if (!Utils.ValidateDottedName(Schema, reader, type))
711                 return;
712
713             UnresolvedReturnType = type;
714         }
715
716         /// <summary>
717         /// Handler for the Parameter Element
718         /// </summary>
719         /// <param name="reader">xml reader currently positioned at Parameter Element</param>
720         protected void HandleParameterElement(XmlReader reader)
721         {
722             Debug.Assert(reader != null);
723
724             Parameter parameter = new Parameter(this);
725
726             parameter.Parse(reader);
727
728             Parameters.Add(parameter, true, Strings.ParameterNameAlreadyDefinedDuplicate);
729         }
730
731         /// <summary>
732         /// Handler for the ReturnType element
733         /// </summary>
734         /// <param name="reader">xml reader currently positioned at ReturnType element</param>
735         protected void HandleReturnTypeElement(XmlReader reader)
736         {
737             Debug.Assert(reader != null);
738
739             ReturnType returnType = new ReturnType(this);
740
741             returnType.Parse(reader);
742
743             if (this._returnTypeList == null)
744             {
745                 this._returnTypeList = new List<ReturnType>();
746             }
747             this._returnTypeList.Add(returnType);
748         }
749
750         /// <summary>
751         /// Handles ParameterTypeSemantics attribute
752         /// </summary>
753         /// <param name="reader"></param>
754         private void HandleParameterTypeSemanticsAttribute(XmlReader reader)
755         {
756             Debug.Assert(reader != null);
757
758             string value = reader.Value;
759
760             if (String.IsNullOrEmpty(value))
761             {
762                 return;
763             }
764
765             value = value.Trim();
766
767             if (!String.IsNullOrEmpty(value))
768             {
769                 switch (value)
770                 {
771                     case "ExactMatchOnly":
772                         ParameterTypeSemantics = ParameterTypeSemantics.ExactMatchOnly;
773                         break;
774                     case "AllowImplicitPromotion":
775                         ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitPromotion;
776                         break;
777                     case "AllowImplicitConversion":
778                         ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion;
779                         break;
780                     default:
781                         // don't try to use the name of the function, because we are still parsing the 
782                         // attributes, and we may not be to the name attribute yet.
783                         AddError(ErrorCode.InvalidValueForParameterTypeSemantics, EdmSchemaErrorSeverity.Error, reader,
784                             System.Data.Entity.Strings.InvalidValueForParameterTypeSemanticsAttribute(
785                                           value));
786
787                         break;
788                 }
789             }
790         }
791
792         #endregion
793     }
794 }