1 #region MIT license
2 //
3 // MIT license
4 //
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 // THE SOFTWARE.
24 //
25 #endregion
26 using System;
27 using System.Data.Common;
28 using System.Linq;
29 using System.Collections.Generic;
30 using System.Text;
31 using System.Data.Linq.Mapping;
32 using System.Reflection;
33 using System.Data;
34 
35 #if MONO_STRICT
36 using System.Data.Linq;
37 #else
38 using DbLinq.Data.Linq;
39 #endif
40 
41 using DbLinq.Data.Linq.SqlClient;
42 using DbLinq.Util;
43 using DbLinq.Vendor;
44 
45 namespace DbLinq.Firebird
46 {
47     [Vendor(typeof(FirebirdProvider))]
48 #if !MONO_STRICT
49     public
50 #endif
51     class FirebirdVendor : Vendor.Implementation.Vendor
52     {
53         public override string VendorName { get { return "FirebirdSql"; } }
54 
55         protected readonly FirebirdSqlProvider sqlProvider = new FirebirdSqlProvider();
56         public override ISqlProvider SqlProvider { get { return sqlProvider; } }
57 
58         /// <summary>
59         /// call mysql stored proc or stored function,
60         /// optionally return DataSet, and collect return params.
61         /// </summary>
ExecuteMethodCall(DataContext context, MethodInfo method , params object[] inputValues)62         public override System.Data.Linq.IExecuteResult ExecuteMethodCall(DataContext context, MethodInfo method
63                                                                  , params object[] inputValues)
64         {
65             if (method == null)
66                 throw new ArgumentNullException("L56 Null 'method' parameter");
67 
68             //check to make sure there is exactly one [FunctionEx]? that's below.
69             //FunctionAttribute functionAttrib = GetFunctionAttribute(method);
70             var functionAttrib = context.Mapping.GetFunction(method);
71 
72             ParameterInfo[] paramInfos = method.GetParameters();
73             //int numRequiredParams = paramInfos.Count(p => p.IsIn || p.IsRetval);
74             //if (numRequiredParams != inputValues.Length)
75             //    throw new ArgumentException("L161 Argument count mismatch");
76 
77             string sp_name = functionAttrib.MappedName;
78 
79             // picrap: is there any way to abstract some part of this?
80             using (IDbCommand command = context.Connection.CreateCommand())
81             {
82                 command.CommandText = sp_name;
83                 //FbSqlCommand command = new FbSqlCommand("select * from hello0()");
84                 int currInputIndex = 0;
85 
86                 List<string> paramNames = new List<string>();
87                 for (int i = 0; i < paramInfos.Length; i++)
88                 {
89                     ParameterInfo paramInfo = paramInfos[i];
90 
91                     //TODO: check to make sure there is exactly one [Parameter]?
92                     ParameterAttribute paramAttrib = paramInfo.GetCustomAttributes(false).OfType<ParameterAttribute>().Single();
93 
94                     string paramName = "@" + paramAttrib.Name; //eg. '@param1'
95                     paramNames.Add(paramName);
96 
97                     System.Data.ParameterDirection direction = GetDirection(paramInfo, paramAttrib);
98                     //FbDbType dbType = FbSqlTypeConversions.ParseType(paramAttrib.DbType);
99                     IDbDataParameter cmdParam = command.CreateParameter();
100                     cmdParam.ParameterName = paramName;
101                     //cmdParam.Direction = System.Data.ParameterDirection.Input;
102                     if (direction == System.Data.ParameterDirection.Input || direction == System.Data.ParameterDirection.InputOutput)
103                     {
104                         object inputValue = inputValues[currInputIndex++];
105                         cmdParam.Value = inputValue;
106                     }
107                     else
108                     {
109                         cmdParam.Value = null;
110                     }
111                     cmdParam.Direction = direction;
112                     command.Parameters.Add(cmdParam);
113                 }
114 
115                 if (!functionAttrib.IsComposable) // IsCompsable is false when we have a procedure
116                 {
117                     //procedures: under the hood, this seems to prepend 'CALL '
118                     command.CommandType = System.Data.CommandType.StoredProcedure;
119                 }
120                 else
121                 {
122                     //functions: 'SELECT * FROM myFunction()' or 'SELECT * FROM hello(?s)'
123                     command.CommandText = "SELECT * FROM " + command.CommandText + "("
124                         + string.Join(",", paramNames.ToArray()) + ")";
125                 }
126 
127                 if (method.ReturnType == typeof(DataSet))
128                 {
129                     //unknown shape of resultset:
130                     System.Data.DataSet dataSet = new DataSet();
131                     //IDataAdapter adapter = new FbDataAdapter((FbCommand)command);
132                     IDbDataAdapter adapter = CreateDataAdapter(context);
133                     adapter.SelectCommand = command;
134                     adapter.Fill(dataSet);
135                     List<object> outParamValues = CopyOutParams(paramInfos, command.Parameters);
136                     return new ProcedureResult(dataSet, outParamValues.ToArray());
137                 }
138                 else
139                 {
140                     object obj = command.ExecuteScalar();
141                     List<object> outParamValues = CopyOutParams(paramInfos, command.Parameters);
142                     return new ProcedureResult(obj, outParamValues.ToArray());
143                 }
144             }
145         }
146 
GetDirection(ParameterInfo paramInfo, ParameterAttribute paramAttrib)147         static System.Data.ParameterDirection GetDirection(ParameterInfo paramInfo, ParameterAttribute paramAttrib)
148         {
149             //strange hack to determine what's a ref, out parameter:
150             //http://lists.ximian.com/pipermain/mono-list/2003-March/012751.html
151             bool hasAmpersand = paramInfo.ParameterType.FullName.Contains('&');
152             if (paramInfo.IsOut)
153                 return System.Data.ParameterDirection.Output;
154             if (hasAmpersand)
155                 return System.Data.ParameterDirection.InputOutput;
156             return System.Data.ParameterDirection.Input;
157         }
158 
159         /// <summary>
160         /// Collect all Out or InOut param values, casting them to the correct .net type.
161         /// </summary>
CopyOutParams(ParameterInfo[] paramInfos, IDataParameterCollection paramSet)162         private List<object> CopyOutParams(ParameterInfo[] paramInfos, IDataParameterCollection paramSet)
163         {
164             List<object> outParamValues = new List<object>();
165             //Type type_t = typeof(T);
166             int i = -1;
167             foreach (IDbDataParameter param in paramSet)
168             {
169                 i++;
170                 if (param.Direction == System.Data.ParameterDirection.Input)
171                 {
172                     outParamValues.Add("unused");
173                     continue;
174                 }
175 
176                 object val = param.Value;
177                 Type desired_type = paramInfos[i].ParameterType;
178 
179                 if (desired_type.Name.EndsWith("&"))
180                 {
181                     //for ref and out parameters, we need to tweak ref types, e.g.
182                     // "System.Int32&, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
183                     string fullName1 = desired_type.AssemblyQualifiedName;
184                     string fullName2 = fullName1.Replace("&", "");
185                     desired_type = Type.GetType(fullName2);
186                 }
187                 try
188                 {
189                     //fi.SetValue(t, val); //fails with 'System.Decimal cannot be converted to Int32'
190                     //DbLinq.util.FieldUtils.SetObjectIdField(t, fi, val);
191                     //object val2 = FieldUtils.CastValue(val, desired_type);
192                     object val2 = TypeConvert.To(val, desired_type);
193                     outParamValues.Add(val2);
194                 }
195                 catch (Exception)
196                 {
197                     //fails with 'System.Decimal cannot be converted to Int32'
198                     //Logger.Write(Level.Error, "CopyOutParams ERROR L245: failed on CastValue(): " + ex.Message);
199                 }
200             }
201             return outParamValues;
202         }
203     }
204 }
205