2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq.PostgreSql / PgsqlSqlProvider.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.Collections.Generic;\r
29 using System.Linq;\r
30 using DbLinq.Data.Linq.Sql;\r
31 using DbLinq.Vendor.Implementation;\r
32 \r
33 namespace DbLinq.PostgreSql\r
34 {\r
35 #if !MONO_STRICT\r
36     public\r
37 #endif\r
38     class PgsqlSqlProvider : SqlProvider\r
39     {\r
40         public override SqlStatement GetInsertIds(SqlStatement table, IList<SqlStatement> autoPKColumn, IList<SqlStatement> inputPKColumns, IList<SqlStatement> inputPKValues, IList<SqlStatement> outputColumns, IList<SqlStatement> outputParameters, IList<SqlStatement> outputExpressions)\r
41         {\r
42             // No parameters? no need to get them back.\r
43             \r
44             if (outputParameters.Count == 0)\r
45                 return SqlStatement.Empty;\r
46             \r
47             // Otherwise we keep track of the new values. Note that we avoid null expressions\r
48             // that can be present in the passed list (is this a bug above us?)\r
49             \r
50             IList<SqlStatement> ids = new List<SqlStatement>();           \r
51             foreach (SqlStatement outputExpression in outputExpressions) {\r
52                 if (outputExpression != null)\r
53                     ids.Add(outputExpression.Replace("nextval(", "currval(", true));\r
54             }\r
55             return SqlStatement.Format("SELECT {0}", SqlStatement.Join(", ", ids.ToArray()));\r
56         }\r
57 \r
58         public override SqlStatement GetLiteral(DateTime literal)\r
59         {\r
60             return "'" + literal.ToString("o") + "'::timestamp";\r
61         }        \r
62         \r
63         protected override SqlStatement GetLiteralStringToUpper(SqlStatement a)\r
64         {\r
65             return string.Format("UPPER({0})", a);\r
66         }\r
67 \r
68         protected override SqlStatement GetLiteralStringToLower(SqlStatement a)\r
69         {\r
70             return string.Format("LOWER({0})", a);\r
71         }\r
72         \r
73         protected override SqlStatement GetLiteralDateDiff(SqlStatement dateA, SqlStatement dateB)\r
74         {\r
75             return string.Format("(EXTRACT(EPOCH FROM ({0})::timestamp)-EXTRACT(EPOCH FROM ({1})::timestamp))*1000", dateA, dateB);\r
76         }\r
77                  \r
78         protected override SqlStatement GetLiteralEqual(SqlStatement a, SqlStatement b)\r
79         {\r
80             // PostgreSQL return NULL (and not a boolean) for every comparaison involving\r
81             // a NULL value, unless the operator used is "IS" (or "IS NOT"). Also,\r
82             // using those two operators when the right-hand value is not a literal\r
83             // NULL is an error. The only possibility is to explicitly check for NULL\r
84             // literals and even swap the operands to make sure NULL gets to the\r
85             // right place.\r
86             \r
87             if (b.Count == 1 && b[0].Sql == "NULL")\r
88                 return SqlStatement.Format("{0} IS {1}", a, b);\r
89             else if (a.Count == 1 && a[0].Sql == "NULL")\r
90                 return SqlStatement.Format("{0} IS {1}", b, a);\r
91             else\r
92                 return SqlStatement.Format("{0} = {1}", a, b);\r
93         }\r
94         \r
95         protected override SqlStatement GetLiteralNotEqual(SqlStatement a, SqlStatement b)\r
96         {\r
97             // See comment above, in GetLiteralEqual().\r
98             \r
99             if (b.Count == 1 && b[0].Sql == "NULL")\r
100                 return SqlStatement.Format("{0} IS NOT {1}", a, b);\r
101             else if (a.Count == 1 && a[0].Sql == "NULL")\r
102                 return SqlStatement.Format("{0} IS NOT {1}", b, a);\r
103             else\r
104                 return SqlStatement.Format("{0} <> {1}", a, b);\r
105         }        \r
106 \r
107         public static readonly Dictionary<Type, string> typeMapping = new Dictionary<Type, string>\r
108                                                                           {\r
109             {typeof(int),"integer"},\r
110             {typeof(uint),"integer"},\r
111 \r
112             {typeof(long),"bigint"},\r
113             {typeof(ulong),"bigint"},\r
114 \r
115             {typeof(float),"real"}, //TODO: could be float or real. check ranges.\r
116             {typeof(double),"double precision"}, //TODO: could be float or real. check ranges.\r
117             \r
118             {typeof(decimal),"decimal"},\r
119 \r
120             {typeof(short),"smallint"},\r
121             {typeof(ushort),"smallint"},\r
122 \r
123             {typeof(bool),"boolean"},\r
124 \r
125             {typeof(string),"text"}, \r
126             {typeof(char[]),"text"},\r
127 \r
128             {typeof(char),"char"},\r
129 \r
130             {typeof(DateTime),"timestamp"},\r
131             //{typeof(Guid),"uniqueidentifier"}\r
132             {typeof(byte[]),"bytea"},\r
133         };\r
134 \r
135         public override SqlStatement GetLiteralConvert(SqlStatement a, Type type)\r
136         {\r
137             if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))\r
138                 type = type.GetGenericArguments().First();\r
139 \r
140             string sqlTypeName;\r
141             if (typeMapping.ContainsKey(type))\r
142                 sqlTypeName = typeMapping[type];\r
143             else\r
144                 sqlTypeName = type.Name;\r
145 \r
146             return string.Format("({0})::{1}", a, sqlTypeName);\r
147         }\r
148 \r
149         /// <summary>\r
150         /// In PostgreSQL an insensitive name is lowercase\r
151         /// </summary>\r
152         /// <param name="dbName"></param>\r
153         /// <returns></returns>\r
154         protected override bool IsNameCaseSafe(string dbName)\r
155         {\r
156             return dbName == dbName.ToLower();\r
157         }\r
158     }\r
159 }\r