2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq.SqlServer / SqlServerSqlProvider.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System;\r
28 using System.Linq;\r
29 using System.Collections.Generic;\r
30 \r
31 using DbLinq.Vendor.Implementation;\r
32 \r
33 #if MONO_STRICT\r
34 using System.Data.Linq.Sql;\r
35 using System.Data.Linq.Sugar.Expressions;\r
36 #else\r
37 using DbLinq.Data.Linq.Sql;\r
38 using DbLinq.Data.Linq.Sugar.Expressions;\r
39 #endif\r
40 \r
41 namespace DbLinq.SqlServer\r
42 {\r
43 #if MONO_STRICT\r
44     internal\r
45 #else\r
46     public\r
47 #endif\r
48  class SqlServerSqlProvider : SqlProvider\r
49     {\r
50         public override ExpressionTranslator GetTranslator()\r
51         {\r
52             return new SqlServerExpressionTranslator();\r
53         }\r
54 \r
55         protected override char SafeNameStartQuote { get { return '['; } }\r
56         protected override char SafeNameEndQuote { get { return ']'; } }\r
57 \r
58         /// <summary>\r
59         /// Returns a table alias\r
60         /// Ensures about the right case\r
61         /// </summary>\r
62         /// <param name="table"></param>\r
63         /// <param name="alias"></param>\r
64         /// <returns></returns>\r
65         public override string GetTableAsAlias(string table, string alias)\r
66         {\r
67             return string.Format("{0} AS {1}", GetTable(table), GetTableAlias(alias));\r
68         }\r
69 \r
70         public override SqlStatement GetLiteral(bool literal)\r
71         {\r
72             if (literal)\r
73                 return "1";\r
74             return "0";\r
75         }\r
76 \r
77         public override string GetParameterName(string nameBase)\r
78         {\r
79             return string.Format("@{0}", nameBase);\r
80         }\r
81 \r
82         public override SqlStatement GetLiteralLimit(SqlStatement select, SqlStatement limit)\r
83         {\r
84             var trimSelect = "SELECT ";\r
85             if (select.Count > 0 && select[0].Sql.StartsWith(trimSelect))\r
86             {\r
87                 var selectBuilder = new SqlStatementBuilder(select);\r
88                 var remaining = select[0].Sql.Substring(trimSelect.Length);\r
89                 selectBuilder.Parts[0] = new SqlLiteralPart(remaining);\r
90                 return SqlStatement.Format("SELECT TOP ({0}) {1}", limit, selectBuilder.ToSqlStatement());\r
91             }\r
92             throw new ArgumentException("S0051: Unknown select format");\r
93         }\r
94 \r
95         public override SqlStatement GetLiteralLimit(SqlStatement select, SqlStatement limit, SqlStatement offset, SqlStatement offsetAndLimit)\r
96         {\r
97             var from    = "FROM ";\r
98             var orderBy = "ORDER BY ";\r
99             var selectK = "SELECT ";\r
100             int fromIdx     = select[0].Sql.IndexOf(from);\r
101             int orderByIdx  = select[0].Sql.IndexOf(orderBy);\r
102 \r
103             if (fromIdx < 0)\r
104                 throw new ArgumentException("S0051: Unknown select format: " + select[0].Sql);\r
105 \r
106             string orderByClause = null;\r
107             string sourceClause = null;\r
108             if (orderByIdx >= 0)\r
109             {\r
110                 orderByClause = select[0].Sql.Substring(orderByIdx);\r
111                 sourceClause = select[0].Sql.Substring(fromIdx, orderByIdx - fromIdx);\r
112             }\r
113             else\r
114             {\r
115                 orderByClause = "ORDER BY " + select[0].Sql.Substring(selectK.Length, fromIdx - selectK.Length);\r
116                 sourceClause = select[0].Sql.Substring(fromIdx);\r
117             }\r
118 \r
119             var selectFieldsClause = select[0].Sql.Substring(0, fromIdx);\r
120 \r
121             return SqlStatement.Format(\r
122                 "SELECT *{0}" +\r
123                 "FROM ({0}" +\r
124                 "    {1},{0}" +\r
125                 "    ROW_NUMBER() OVER({2}) AS [__ROW_NUMBER]{0}" +\r
126                 "    {3}" +\r
127                 "    ) AS [t0]{0}" +\r
128                 "WHERE [__ROW_NUMBER] BETWEEN {4}+1 AND {4}+{5}{0}" +\r
129                 "ORDER BY [__ROW_NUMBER]",\r
130                 NewLine, selectFieldsClause, orderByClause, sourceClause, offset, limit);\r
131         }\r
132 \r
133         protected override SqlStatement GetLiteralDateDiff(SqlStatement dateA, SqlStatement dateB)\r
134         {\r
135             return SqlStatement.Format("(CONVERT(BigInt,DATEDIFF(DAY, {0}, {1}))) * 86400000 +" //diffierence in milliseconds regards days\r
136                      + "DATEDIFF(MILLISECOND, "\r
137 \r
138                                 // (DateA-DateB) in days +DateB = difference in time\r
139                                 + @"DATEADD(DAY, \r
140                                       DATEDIFF(DAY, {0}, {1})\r
141                                       ,{0})"\r
142 \r
143                                 + ",{1})", dateB, dateA);\r
144 \r
145             //this trick is needed in sqlserver since DATEDIFF(MILLISECONDS,{0},{1}) usually crhases in the database engine due an overflow:\r
146             //System.Data.SqlClient.SqlException : Difference of two datetime columns caused overflow at runtime.\r
147         }\r
148 \r
149         protected override SqlStatement GetLiteralDateTimePart(SqlStatement dateExpression, SpecialExpressionType operationType)\r
150         {\r
151             return SqlStatement.Format("DATEPART({0},{1})", operationType.ToString().ToUpper(), dateExpression);\r
152         }\r
153 \r
154         protected override SqlStatement GetLiteralMathPow(SqlStatement p, SqlStatement p_2)\r
155         {\r
156             return SqlStatement.Format("POWER({0},{1})", p, p_2);\r
157         }\r
158 \r
159         protected override SqlStatement GetLiteralMathLog(SqlStatement p, SqlStatement p_2)\r
160         {\r
161             return SqlStatement.Format("(LOG({0})/LOG({1}))", p, p_2);\r
162         }\r
163 \r
164         protected override SqlStatement GetLiteralMathLn(SqlStatement p)\r
165         {\r
166             return GetLiteralMathLog(p, string.Format("{0}", Math.E));\r
167         }\r
168 \r
169         protected override SqlStatement GetLiteralStringLength(SqlStatement a)\r
170         {\r
171             return SqlStatement.Format("LEN({0})", a);\r
172         }\r
173 \r
174         protected override SqlStatement GetLiteralSubString(SqlStatement baseString, SqlStatement startIndex, SqlStatement count)\r
175         {\r
176             //in standard sql base string index is 1 instead 0\r
177             return SqlStatement.Format("SUBSTRING({0}, {1}, {2})", baseString, startIndex, count);\r
178         }\r
179 \r
180         protected override SqlStatement GetLiteralSubString(SqlStatement baseString, SqlStatement startIndex)\r
181         {\r
182             return GetLiteralSubString(baseString, startIndex, GetLiteralStringLength(baseString));\r
183         }\r
184 \r
185         protected override SqlStatement GetLiteralTrim(SqlStatement a)\r
186         {\r
187             return SqlStatement.Format("RTRIM(LTRIM({0}))", a);\r
188         }\r
189 \r
190         protected override SqlStatement GetLiteralStringConcat(SqlStatement a, SqlStatement b)\r
191         {\r
192             return SqlStatement.Format("{0} + {1}", a, b);\r
193         }\r
194 \r
195         protected override SqlStatement GetLiteralStringToLower(SqlStatement a)\r
196         {\r
197             return SqlStatement.Format("LOWER({0})", a);\r
198         }\r
199 \r
200         protected override SqlStatement GetLiteralStringToUpper(SqlStatement a)\r
201         {\r
202             return SqlStatement.Format("UPPER({0})", a);\r
203         }\r
204 \r
205         protected override SqlStatement GetLiteralStringIndexOf(SqlStatement baseString, SqlStatement searchString)\r
206         {\r
207             return GetLiteralSubtract(SqlStatement.Format("CHARINDEX({0},{1})", searchString, baseString), "1");\r
208         }\r
209 \r
210         protected override SqlStatement GetLiteralStringIndexOf(SqlStatement baseString, SqlStatement searchString, SqlStatement startIndex)\r
211         {\r
212             return GetLiteralSubtract(SqlStatement.Format("CHARINDEX({0},{1},{2})", searchString, baseString, startIndex), "1");\r
213         }\r
214 \r
215         protected override SqlStatement GetLiteralStringIndexOf(SqlStatement baseString, SqlStatement searchString, SqlStatement startIndex, SqlStatement count)\r
216         {\r
217             return GetLiteralSubtract(SqlStatement.Format("CHARINDEX({0},{1},{2})", searchString, GetLiteralSubString(baseString, "1", GetLiteralStringConcat(count, startIndex)), startIndex), "1");\r
218         }\r
219 \r
220         //http://msdn.microsoft.com/en-us/library/4e5xt97a(VS.71).aspx\r
221         public static readonly Dictionary<Type, string> typeMapping = new Dictionary<Type, string>\r
222         {\r
223             {typeof(int),"int"},\r
224             {typeof(uint),"int"},\r
225 \r
226             {typeof(long),"bigint"},\r
227             {typeof(ulong),"bigint"},\r
228 \r
229             {typeof(float),"float"}, //TODO: could be float or real. check ranges.\r
230             {typeof(double),"float"}, //TODO: could be float or real. check ranges.\r
231             \r
232             {typeof(decimal),"numeric"},\r
233 \r
234             {typeof(short),"tinyint"},\r
235             {typeof(ushort),"tinyint"},\r
236 \r
237             {typeof(bool),"bit"},\r
238 \r
239             // trunk? They could be: varchar, char,nchar, ntext,text... it should be the most flexible string type. TODO: check wich of them is better.\r
240             {typeof(string),"varchar"}, \r
241             {typeof(char[]),"varchar"},\r
242 \r
243             {typeof(char),"char"},\r
244 \r
245             {typeof(DateTime),"datetime"},\r
246             {typeof(Guid),"uniqueidentifier"}\r
247 \r
248             // there are more types: timestamps, images ... TODO: check what is the official behaviour\r
249         };\r
250 \r
251         public override SqlStatement GetLiteralConvert(SqlStatement a, Type type)\r
252         {\r
253             if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))\r
254                 type = type.GetGenericArguments().First();\r
255 \r
256             SqlStatement sqlTypeName;\r
257             if (typeMapping.ContainsKey(type))\r
258                 sqlTypeName = typeMapping[type];\r
259             else\r
260                 sqlTypeName = "variant";\r
261 \r
262             return SqlStatement.Format("CONVERT({0},{1})", sqlTypeName, a);\r
263         }\r
264 \r
265         public override string GetColumn(string table, string column)\r
266         {\r
267             if (column != "*")\r
268                 return base.GetColumn(table, column);\r
269             return "*";\r
270         }\r
271     }\r
272 }\r