DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
FunctionProvider.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 using System.Linq.Expressions;
21 
22 using Deveel.Data.Sql;
23 using Deveel.Data.Sql.Fluid;
24 using Deveel.Data.Types;
25 
26 using DryIoc;
27 
28 namespace Deveel.Data.Routines {
29  public abstract class FunctionProvider : IRoutineResolver, IConfigurationContext, IDisposable {
30  private Container container;
31 
32  protected FunctionProvider() {
33  container = new Container(Rules.Default.WithResolveIEnumerableAsLazyEnumerable());
34  CallInit();
35  }
36 
38  Dispose(false);
39  }
40 
41  public abstract string SchemaName { get; }
42 
43  private void CallInit() {
44  OnInit();
45  }
46 
47  protected abstract void OnInit();
48 
49  protected virtual ObjectName NormalizeName(ObjectName name) {
50  var parentName = name.Parent;
51  if (parentName == null)
52  return null;
53 
54  return name;
55  }
56 
57  protected void Register(FunctionInfo functionInfo, Func<InvokeContext, InvokeResult> body, Func<InvokeContext, SqlType> returnType) {
58  Register(new DelegateFunction(functionInfo, body, returnType));
59  }
60 
61  protected void Register(Func<IFunctionConfiguration, IFunctionConfiguration> config) {
62  if (config == null)
63  return;
64 
65  var configuration = new FunctionConfiguration(this);
66  config(configuration);
67 
68  var functionInfos = configuration.FunctionInfo;
69  foreach (var functionInfo in functionInfos) {
70  Register(functionInfo, configuration.ExecuteFunc, configuration.ReturnTypeFunc);
71  }
72  }
73 
74  protected void Register(IFunction function) {
75  if (function == null)
76  throw new ArgumentNullException("function");
77 
78  var functionName = function.FullName.FullName.ToUpperInvariant();
79  container.RegisterInstance(function, serviceKey: functionName,reuse:Reuse.Singleton);
80  }
81 
83  return ResolveFunction(request, query);
84  }
85 
86  public IFunction ResolveFunction(Invoke invoke, IQuery query) {
87  var name = NormalizeName(invoke.RoutineName);
88 
89  if (name == null ||
90  !name.ParentName.Equals(SchemaName))
91  return null;
92 
93  var functionName = name.FullName.ToUpperInvariant();
94  var functions = container.ResolveMany<IFunction>(serviceKey:functionName).ToArrayOrSelf();
95  if (functions.Length == 0)
96  return null;
97  if (functions.Length == 1)
98  return functions[0];
99 
100  return functions.FirstOrDefault(x => x.RoutineInfo.MatchesInvoke(invoke, query));
101  }
102 
103  #region DelegateFunction
104 
106  private readonly Func<InvokeContext, InvokeResult> functionBody;
107  private readonly Func<InvokeContext, SqlType> returnType;
108 
109  public DelegateFunction(FunctionInfo functionInfo, Func<InvokeContext, InvokeResult> functionBody, Func<InvokeContext, SqlType> returnType)
110  : base(functionInfo) {
111  this.functionBody = functionBody;
112  this.returnType = returnType;
113  }
114 
115  public override InvokeResult Execute(InvokeContext context) {
116  return functionBody(context);
117  }
118 
119  public override SqlType ReturnType(InvokeContext context) {
120  if (returnType == null)
121  return FunctionInfo.ReturnType;
122 
123  return returnType(context);
124  }
125  }
126 
127  #endregion
128 
129  #region FunctionConfiguration
130 
132  private readonly FunctionProvider provider;
133  private readonly Dictionary<string, RoutineParameter> parameters;
134  private List<ObjectName> aliases;
135 
137  this.provider = provider;
138  parameters = new Dictionary<string, RoutineParameter>();
139  FunctionType = FunctionType.Static;
140  }
141 
142  public FunctionType FunctionType { get; private set; }
143 
144  public Func<InvokeContext, SqlType> ReturnTypeFunc { get; private set; }
145 
146  public Func<InvokeContext, InvokeResult> ExecuteFunc { get; private set; }
147 
149  get {
150  var result = new List<FunctionInfo> { new FunctionInfo(FunctionName, parameters.Values.ToArray()) };
151  if (aliases != null && aliases.Count > 0)
152  result.AddRange(aliases.Select(name => new FunctionInfo(name, parameters.Values.ToArray())));
153 
154  return result.ToArray();
155  }
156  }
157 
158  public ObjectName FunctionName { get; private set; }
159 
160  public RoutineParameter[] Parameters {
161  get { return parameters.Values.ToArray(); }
162  }
163 
164  public bool HasParameter(string name) {
165  return parameters.ContainsKey(name);
166  }
167 
168  public bool HasUnboundedParameter() {
169  return parameters.Values.Any(x => x.IsUnbounded);
170  }
171 
173  if (name == null)
174  throw new ArgumentNullException("name");
175 
176  var parent = name.ParentName;
177 
178  if (!provider.SchemaName.Equals(parent))
179  throw new ArgumentException(String.Format(
180  "The parent name ({0}) is not valid in this provider schema context ({1})", parent, provider.SchemaName));
181 
182  FunctionName = name;
183  return this;
184  }
185 
187  FunctionType = functionType;
188  return this;
189  }
190 
192  if (alias == null)
193  throw new ArgumentNullException("alias");
194 
195  if (FunctionName == null)
196  throw new ArgumentException("The function has no name configured and cannot be aliased.");
197 
198  var parent = alias.ParentName;
199 
200  if (!provider.SchemaName.Equals(parent))
201  throw new ArgumentException();
202 
203  if (aliases == null)
204  aliases = new List<ObjectName>();
205 
206  aliases.Add(alias);
207 
208  return this;
209  }
210 
211  public IFunctionConfiguration WithParameter(Action<IFunctionParameterConfiguration> config) {
212  var paramConfig = new FunctionParameterConfiguration(this);
213  if (config != null) {
214  config(paramConfig);
215 
216  var param = paramConfig.AsParameter();
217 
218  if (String.IsNullOrEmpty(param.Name))
219  throw new InvalidOperationException("A parameter must define a name.");
220 
221  parameters.Add(param.Name, param);
222  }
223 
224  return this;
225  }
226 
227  public IFunctionConfiguration ReturnsType(Func<InvokeContext, SqlType> returns) {
228  ReturnTypeFunc = returns;
229  return this;
230  }
231 
232  public IFunctionConfiguration WhenExecute(Func<InvokeContext, InvokeResult> execute) {
233  ExecuteFunc = execute;
234  return this;
235  }
236 
238  get { return provider; }
239  }
240  }
241 
242  #endregion
243 
244  #region FunctionParemeterConfiguration
245 
248 
249  private string parameterName;
250  private SqlType sqlType;
252 
254  this.configuration = configuration;
255 
256  attributes = new ParameterAttributes();
257  sqlType = PrimitiveTypes.Numeric();
258  }
259 
261  if (String.IsNullOrEmpty(name))
262  throw new ArgumentNullException("name");
263 
264  if (configuration.HasParameter(name))
265  throw new ArgumentException(String.Format("A parameter with name '{0}' was already configured for the function", name), "name");
266 
267  parameterName = name;
268 
269  return this;
270  }
271 
273  if (type == null)
274  throw new ArgumentNullException("type");
275 
276  sqlType = type;
277 
278  return this;
279  }
280 
282  if (configuration.HasUnboundedParameter())
283  throw new ArgumentException("An unbounded parameter is already configured");
284 
285  if (flag)
286  attributes |= ParameterAttributes.Unbounded;
287 
288  return this;
289  }
290 
292  return new RoutineParameter(parameterName, sqlType, attributes);
293  }
294  }
295 
296  #endregion
297 
298  public void Dispose() {
299  Dispose(true);
300  GC.SuppressFinalize(this);
301  }
302 
303  protected virtual void Dispose(bool disposing) {
304  if (disposing) {
305  if (container != null)
306  container.Dispose();
307  }
308 
309  container = null;
310  }
311  }
312 }
Provides some helper functions for resolving and creating SqlType instances that are primitive to the...
DelegateFunction(FunctionInfo functionInfo, Func< InvokeContext, InvokeResult > functionBody, Func< InvokeContext, SqlType > returnType)
The system uses instances of this interface to resolve routines given a user invocation.
IFunctionConfiguration WithParameter(Action< IFunctionParameterConfiguration > config)
Defines a routine that is a function, that means it returns a value after its execution.
Definition: IFunction.cs:26
A long string in the system.
A system routine that returns a value at the end of its execution.
Definition: Function.cs:31
void Register(Func< IFunctionConfiguration, IFunctionConfiguration > config)
override InvokeResult Execute(InvokeContext context)
Executes the function and provides a result.
IFunctionConfiguration OfType(FunctionType functionType)
Describes the name of an object within a database.
Definition: ObjectName.cs:44
The contract to define a program routine that can interact with database objects. ...
Definition: IRoutine.cs:26
readonly Dictionary< string, RoutineParameter > parameters
readonly Func< InvokeContext, InvokeResult > functionBody
virtual void Dispose(bool disposing)
virtual ObjectName NormalizeName(ObjectName name)
IFunctionConfiguration ReturnsType(Func< InvokeContext, SqlType > returns)
readonly Func< InvokeContext, SqlType > returnType
IFunctionParameterConfiguration Unbounded(bool flag)
static NumericType Numeric()
Represents the result of the execution of a routine.
Definition: InvokeResult.cs:25
The information about the invocation of a routine, including the full name and arguments (as SqlExpre...
Definition: Invoke.cs:30
FunctionType
The different type of a function.
Definition: FunctionType.cs:25
Defines the properties of a specific SQL Type and handles the values compatible.
Definition: SqlType.cs:33
void Register(FunctionInfo functionInfo, Func< InvokeContext, InvokeResult > body, Func< InvokeContext, SqlType > returnType)
The function signature information that are used to resolve a function within a context.
Definition: FunctionInfo.cs:30
IFunctionConfiguration WithAlias(ObjectName alias)
ObjectName Parent
Gets the parent reference of the current one, if any or null if none.
Definition: ObjectName.cs:99
IFunction ResolveFunction(Invoke invoke, IQuery query)
ObjectName RoutineName
Gets the fully qualified name of the routine to invoke.
Definition: Invoke.cs:58
IRoutine ResolveRoutine(Invoke request, IQuery query)
Resolves a routine that matches the given invocation within the context provided. ...
override SqlType ReturnType(InvokeContext context)
Resolves the function return type against the given context.
IFunctionConfiguration WhenExecute(Func< InvokeContext, InvokeResult > execute)