Merge branch 'master' into config-checks-ipv6
[mono.git] / mcs / class / Mono.Data.Sqlite / Mono.Data.Sqlite_2.0 / SQLiteCommandBuilder.cs
1 /********************************************************\r
2  * ADO.NET 2.0 Data Provider for SQLite Version 3.X\r
3  * Written by Robert Simpson (robert@blackcastlesoft.com)\r
4  * \r
5  * Released to the public domain, use at your own risk!\r
6  ********************************************************/\r
7 \r
8 namespace Mono.Data.Sqlite\r
9 {\r
10   using System;\r
11   using System.Data;\r
12   using System.Data.Common;\r
13   using System.Globalization;\r
14   using System.ComponentModel;\r
15 \r
16   /// <summary>\r
17   /// SQLite implementation of DbCommandBuilder.\r
18   /// </summary>\r
19   public sealed class SqliteCommandBuilder : DbCommandBuilder\r
20   {\r
21     /// <summary>\r
22     /// Default constructor\r
23     /// </summary>\r
24     public SqliteCommandBuilder() : this(null)\r
25     {\r
26     }\r
27 \r
28     /// <summary>\r
29     /// Initializes the command builder and associates it with the specified data adapter.\r
30     /// </summary>\r
31     /// <param name="adp"></param>\r
32     public SqliteCommandBuilder(SqliteDataAdapter adp)\r
33     {\r
34       QuotePrefix = "[";\r
35       QuoteSuffix = "]";\r
36       DataAdapter = adp;\r
37     }\r
38 \r
39     /// <summary>\r
40     /// Minimal amount of parameter processing.  Primarily sets the DbType for the parameter equal to the provider type in the schema\r
41     /// </summary>\r
42     /// <param name="parameter">The parameter to use in applying custom behaviors to a row</param>\r
43     /// <param name="row">The row to apply the parameter to</param>\r
44     /// <param name="statementType">The type of statement</param>\r
45     /// <param name="whereClause">Whether the application of the parameter is part of a WHERE clause</param>\r
46     protected override void ApplyParameterInfo(DbParameter parameter, DataRow row, StatementType statementType, bool whereClause)\r
47     {\r
48       SqliteParameter param = (SqliteParameter)parameter;\r
49       param.DbType = (DbType)row[SchemaTableColumn.ProviderType];\r
50     }\r
51 \r
52     /// <summary>\r
53     /// Returns a valid named parameter\r
54     /// </summary>\r
55     /// <param name="parameterName">The name of the parameter</param>\r
56     /// <returns>Error</returns>\r
57     protected override string GetParameterName(string parameterName)\r
58     {\r
59       return String.Format(CultureInfo.InvariantCulture, "@{0}", parameterName);\r
60     }\r
61 \r
62     /// <summary>\r
63     /// Returns a named parameter for the given ordinal\r
64     /// </summary>\r
65     /// <param name="parameterOrdinal">The i of the parameter</param>\r
66     /// <returns>Error</returns>\r
67     protected override string GetParameterName(int parameterOrdinal)\r
68     {\r
69       return String.Format(CultureInfo.InvariantCulture, "@param{0}", parameterOrdinal);\r
70     }\r
71 \r
72     /// <summary>\r
73     /// Returns a placeholder character for the specified parameter i.\r
74     /// </summary>\r
75     /// <param name="parameterOrdinal">The index of the parameter to provide a placeholder for</param>\r
76     /// <returns>Returns a named parameter</returns>\r
77     protected override string GetParameterPlaceholder(int parameterOrdinal)\r
78     {\r
79       return GetParameterName(parameterOrdinal);\r
80     }\r
81 \r
82     /// <summary>\r
83     /// Sets the handler for receiving row updating events.  Used by the DbCommandBuilder to autogenerate SQL\r
84     /// statements that may not have previously been generated.\r
85     /// </summary>\r
86     /// <param name="adapter">A data adapter to receive events on.</param>\r
87     protected override void SetRowUpdatingHandler(DbDataAdapter adapter)\r
88     {\r
89       if (adapter == base.DataAdapter)\r
90       {\r
91         ((SqliteDataAdapter)adapter).RowUpdating -= new EventHandler<RowUpdatingEventArgs>(RowUpdatingEventHandler);\r
92       }\r
93       else\r
94       {\r
95         ((SqliteDataAdapter)adapter).RowUpdating += new EventHandler<RowUpdatingEventArgs>(RowUpdatingEventHandler);\r
96       }\r
97     }\r
98 \r
99     private void RowUpdatingEventHandler(object sender, RowUpdatingEventArgs e)\r
100     {\r
101       base.RowUpdatingHandler(e);\r
102     }\r
103 \r
104     /// <summary>\r
105     /// Gets/sets the DataAdapter for this CommandBuilder\r
106     /// </summary>\r
107     public new SqliteDataAdapter DataAdapter\r
108     {\r
109       get { return (SqliteDataAdapter)base.DataAdapter; }\r
110       set { base.DataAdapter = value; }\r
111     }\r
112 \r
113     /// <summary>\r
114     /// Returns the automatically-generated SQLite command to delete rows from the database\r
115     /// </summary>\r
116     /// <returns></returns>\r
117     public new SqliteCommand GetDeleteCommand()\r
118     {\r
119       return (SqliteCommand)base.GetDeleteCommand();\r
120     }\r
121 \r
122     /// <summary>\r
123     /// Returns the automatically-generated SQLite command to delete rows from the database\r
124     /// </summary>\r
125     /// <param name="useColumnsForParameterNames"></param>\r
126     /// <returns></returns>\r
127     public new SqliteCommand GetDeleteCommand(bool useColumnsForParameterNames)\r
128     {\r
129       return (SqliteCommand)base.GetDeleteCommand(useColumnsForParameterNames);\r
130     }\r
131 \r
132     /// <summary>\r
133     /// Returns the automatically-generated SQLite command to update rows in the database\r
134     /// </summary>\r
135     /// <returns></returns>\r
136     public new SqliteCommand GetUpdateCommand()\r
137     {\r
138       return (SqliteCommand)base.GetUpdateCommand();\r
139     }\r
140 \r
141     /// <summary>\r
142     /// Returns the automatically-generated SQLite command to update rows in the database\r
143     /// </summary>\r
144     /// <param name="useColumnsForParameterNames"></param>\r
145     /// <returns></returns>\r
146     public new SqliteCommand GetUpdateCommand(bool useColumnsForParameterNames)\r
147     {\r
148       return (SqliteCommand)base.GetUpdateCommand(useColumnsForParameterNames);\r
149     }\r
150 \r
151     /// <summary>\r
152     /// Returns the automatically-generated SQLite command to insert rows into the database\r
153     /// </summary>\r
154     /// <returns></returns>\r
155     public new SqliteCommand GetInsertCommand()\r
156     {\r
157       return (SqliteCommand)base.GetInsertCommand();\r
158     }\r
159 \r
160     /// <summary>\r
161     /// Returns the automatically-generated SQLite command to insert rows into the database\r
162     /// </summary>\r
163     /// <param name="useColumnsForParameterNames"></param>\r
164     /// <returns></returns>\r
165     public new SqliteCommand GetInsertCommand(bool useColumnsForParameterNames)\r
166     {\r
167       return (SqliteCommand)base.GetInsertCommand(useColumnsForParameterNames);\r
168     }\r
169 \r
170     /// <summary>\r
171     /// Overridden to hide its property from the designer\r
172     /// </summary>\r
173 #if !PLATFORM_COMPACTFRAMEWORK\r
174     [Browsable(false)]\r
175 #endif\r
176     public override CatalogLocation CatalogLocation\r
177     {\r
178       get\r
179       {\r
180         return base.CatalogLocation;\r
181       }\r
182       set\r
183       {\r
184         base.CatalogLocation = value;\r
185       }\r
186     }\r
187 \r
188     /// <summary>\r
189     /// Overridden to hide its property from the designer\r
190     /// </summary>\r
191 #if !PLATFORM_COMPACTFRAMEWORK\r
192     [Browsable(false)]\r
193 #endif\r
194     public override string CatalogSeparator\r
195     {\r
196       get\r
197       {\r
198         return base.CatalogSeparator;\r
199       }\r
200       set\r
201       {\r
202         base.CatalogSeparator = value;\r
203       }\r
204     }\r
205 \r
206     /// <summary>\r
207     /// Overridden to hide its property from the designer\r
208     /// </summary>\r
209 #if !PLATFORM_COMPACTFRAMEWORK\r
210     [Browsable(false)]\r
211 #endif\r
212     [DefaultValue("[")]\r
213     public override string QuotePrefix\r
214     {\r
215       get\r
216       {\r
217         return base.QuotePrefix;\r
218       }\r
219       set\r
220       {\r
221         base.QuotePrefix = value;\r
222       }\r
223     }\r
224 \r
225     /// <summary>\r
226     /// Overridden to hide its property from the designer\r
227     /// </summary>\r
228 #if !PLATFORM_COMPACTFRAMEWORK\r
229     [Browsable(false)]\r
230 #endif\r
231     public override string QuoteSuffix\r
232     {\r
233       get\r
234       {\r
235         return base.QuoteSuffix;\r
236       }\r
237       set\r
238       {\r
239         base.QuoteSuffix = value;\r
240       }\r
241     }\r
242 \r
243     /// <summary>\r
244     /// Places brackets around an identifier\r
245     /// </summary>\r
246     /// <param name="unquotedIdentifier">The identifier to quote</param>\r
247     /// <returns>The bracketed identifier</returns>\r
248     public override string QuoteIdentifier(string unquotedIdentifier)\r
249     {\r
250       if (String.IsNullOrEmpty(QuotePrefix)\r
251         || String.IsNullOrEmpty(QuoteSuffix)\r
252         || String.IsNullOrEmpty(unquotedIdentifier))\r
253         return unquotedIdentifier;\r
254 \r
255       return QuotePrefix + unquotedIdentifier.Replace(QuoteSuffix, QuoteSuffix + QuoteSuffix) + QuoteSuffix;\r
256     }\r
257 \r
258     /// <summary>\r
259     /// Removes brackets around an identifier\r
260     /// </summary>\r
261     /// <param name="quotedIdentifier">The quoted (bracketed) identifier</param>\r
262     /// <returns>The undecorated identifier</returns>\r
263     public override string UnquoteIdentifier(string quotedIdentifier)\r
264     {\r
265       if (String.IsNullOrEmpty(QuotePrefix)\r
266         || String.IsNullOrEmpty(QuoteSuffix)\r
267         || String.IsNullOrEmpty(quotedIdentifier))\r
268         return quotedIdentifier;\r
269 \r
270       if (quotedIdentifier.StartsWith(QuotePrefix, StringComparison.InvariantCultureIgnoreCase) == false\r
271         || quotedIdentifier.EndsWith(QuoteSuffix, StringComparison.InvariantCultureIgnoreCase) == false)\r
272         return quotedIdentifier;\r
273 \r
274       return quotedIdentifier.Substring(QuotePrefix.Length, quotedIdentifier.Length - (QuotePrefix.Length + QuoteSuffix.Length)).Replace(QuoteSuffix + QuoteSuffix, QuoteSuffix);\r
275     }\r
276 \r
277     /// <summary>\r
278     /// Overridden to hide its property from the designer\r
279     /// </summary>\r
280 #if !PLATFORM_COMPACTFRAMEWORK\r
281     [Browsable(false)]\r
282 #endif\r
283     public override string SchemaSeparator\r
284     {\r
285       get\r
286       {\r
287         return base.SchemaSeparator;\r
288       }\r
289       set\r
290       {\r
291         base.SchemaSeparator = value;\r
292       }\r
293     }\r
294 \r
295     /// <summary>\r
296     /// Override helper, which can help the base command builder choose the right keys for the given query\r
297     /// </summary>\r
298     /// <param name="sourceCommand"></param>\r
299     /// <returns></returns>\r
300     protected override DataTable GetSchemaTable(DbCommand sourceCommand)\r
301     {\r
302       using (IDataReader reader = sourceCommand.ExecuteReader(CommandBehavior.KeyInfo | CommandBehavior.SchemaOnly))\r
303       {\r
304         DataTable schema = reader.GetSchemaTable();\r
305 \r
306         // If the query contains a primary key, turn off the IsUnique property\r
307         // for all the non-key columns\r
308         if (HasSchemaPrimaryKey(schema))\r
309           ResetIsUniqueSchemaColumn(schema);\r
310 \r
311         // if table has no primary key we use unique columns as a fall back\r
312         return schema;\r
313       }\r
314     }\r
315 \r
316     private bool HasSchemaPrimaryKey(DataTable schema)\r
317     {\r
318       DataColumn IsKeyColumn = schema.Columns[SchemaTableColumn.IsKey];\r
319       \r
320       foreach (DataRow schemaRow in schema.Rows)\r
321       {\r
322         if ((bool)schemaRow[IsKeyColumn] == true)\r
323           return true;\r
324       }\r
325 \r
326       return false;\r
327     }\r
328 \r
329     private void ResetIsUniqueSchemaColumn(DataTable schema)\r
330     {\r
331       DataColumn IsUniqueColumn = schema.Columns[SchemaTableColumn.IsUnique];\r
332       DataColumn IsKeyColumn = schema.Columns[SchemaTableColumn.IsKey];\r
333 \r
334       foreach (DataRow schemaRow in schema.Rows)\r
335       {\r
336         if ((bool)schemaRow[IsKeyColumn] == false)\r
337           schemaRow[IsUniqueColumn] = false;\r
338       }\r
339 \r
340       schema.AcceptChanges();\r
341     }\r
342   }\r
343 }\r