wasCSharpSQLite – Blame information for rev

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 //
2 // Community.CsharpSqlite.SQLiteClient.SqliteCommandBuilder.cs
3 //
4 // Author(s): Tim Coleman (tim@timcoleman.com)
5 // Marek Habersack (grendello@gmail.com)
6 //
7 // Copyright (C) Tim Coleman, 2002
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 // Copyright (C) 2007 Marek Habersack
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30  
31 using System;
32 using System.Data;
33 using System.Data.Common;
34 using System.Text;
35  
36 namespace Community.CsharpSqlite.SQLiteClient
37 {
38 public sealed class SqliteCommandBuilder : DbCommandBuilder
39 {
40 static readonly string clause1 = "({0} = 1 AND {1} IS NULL)";
41 static readonly string clause2 = "({0} = {1})";
42  
43 DataTable _schemaTable;
44 SqliteDataAdapter _dataAdapter;
45 SqliteCommand _insertCommand;
46 SqliteCommand _updateCommand;
47 SqliteCommand _deleteCommand;
48 bool _disposed;
49 string _quotePrefix = "'";
50 string _quoteSuffix = "'";
51 string _tableName;
52 SqliteRowUpdatingEventHandler rowUpdatingHandler;
53  
54 public new DbDataAdapter DataAdapter {
55 get { return _dataAdapter; }
56 set {
57 if (_dataAdapter != null)
58 _dataAdapter.RowUpdating -= new SqliteRowUpdatingEventHandler (RowUpdatingHandler);
59 _dataAdapter = value as SqliteDataAdapter;
60 if (_dataAdapter != null)
61 _dataAdapter.RowUpdating += new SqliteRowUpdatingEventHandler (RowUpdatingHandler);
62 }
63 }
64  
65 public override string QuotePrefix {
66 get { return _quotePrefix; }
67  
68 set {
69 if (_schemaTable != null)
70 throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update or Delete commands have been generated.");
71 _quotePrefix = value;
72 }
73 }
74  
75 public override string QuoteSuffix {
76 get { return _quoteSuffix; }
77  
78 set {
79 if (_schemaTable != null)
80 throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update or Delete commands have been generated.");
81 _quoteSuffix = value;
82 }
83 }
84  
85 private SqliteCommand SourceCommand {
86 get {
87 if (_dataAdapter != null)
88 return _dataAdapter.SelectCommand as SqliteCommand;
89 return null;
90 }
91 }
92  
93 private string QuotedTableName {
94 get { return GetQuotedString (_tableName); }
95 }
96  
97 public new SqliteCommand GetDeleteCommand ()
98 {
99 BuildCache (true);
100 if (_deleteCommand == null)
101 return CreateDeleteCommand (false);
102 return _deleteCommand;
103 }
104  
105 public new SqliteCommand GetInsertCommand ()
106 {
107 BuildCache (true);
108 if (_insertCommand == null)
109 return CreateInsertCommand (false);
110 return _insertCommand;
111 }
112  
113 public new SqliteCommand GetUpdateCommand ()
114 {
115 BuildCache (true);
116 if (_updateCommand == null)
117 return CreateUpdateCommand (false);
118 return _updateCommand;
119 }
120  
121 public override void RefreshSchema ()
122 {
123 // FIXME: "Figure out what else needs to be cleaned up when we refresh."
124 _tableName = String.Empty;
125 _schemaTable = null;
126 CreateNewCommand (ref _deleteCommand);
127 CreateNewCommand (ref _updateCommand);
128 CreateNewCommand (ref _insertCommand);
129 }
130  
131 protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
132 {
133 if (!(adapter is SqliteDataAdapter)) {
134 throw new InvalidOperationException ("Adapter needs to be a SqliteDataAdapter");
135 }
136 rowUpdatingHandler = new SqliteRowUpdatingEventHandler (RowUpdatingHandler);
137 ((SqliteDataAdapter) adapter).RowUpdating += rowUpdatingHandler;
138 }
139  
140 protected override void ApplyParameterInfo (DbParameter dbParameter,
141 DataRow row,
142 StatementType statementType,
143 bool whereClause)
144 {
145 // Nothing to do here
146 }
147  
148 protected override string GetParameterName (int position)
149 {
150 return String.Format ("?p{0}", position);
151 }
152  
153 protected override string GetParameterName (string parameterName)
154 {
155 if (string.IsNullOrEmpty(parameterName))
156 throw new ArgumentException ("parameterName cannot be null or empty");
157 if (parameterName [0] == '?')
158 return parameterName;
159 return String.Format ("?{0}", parameterName);
160 }
161  
162  
163 protected override string GetParameterPlaceholder (int position)
164 {
165 return String.Format ("?p{0}", position);
166 }
167  
168 protected override void Dispose (bool disposing)
169 {
170 if (!_disposed) {
171 if (disposing) {
172 if (_insertCommand != null)
173 _insertCommand.Dispose ();
174 if (_deleteCommand != null)
175 _deleteCommand.Dispose ();
176 if (_updateCommand != null)
177 _updateCommand.Dispose ();
178 if (_schemaTable != null)
179 _schemaTable.Dispose ();
180 }
181 _disposed = true;
182 }
183 }
184  
185 private void BuildCache (bool closeConnection)
186 {
187 SqliteCommand sourceCommand = SourceCommand;
188 if (sourceCommand == null)
189 throw new InvalidOperationException ("The DataAdapter.SelectCommand property needs to be initialized.");
190 SqliteConnection connection = sourceCommand.Connection as SqliteConnection;
191 if (connection == null)
192 throw new InvalidOperationException ("The DataAdapter.SelectCommand.Connection property needs to be initialized.");
193  
194 if (_schemaTable == null) {
195 if (connection.State == ConnectionState.Open)
196 closeConnection = false;
197 else
198 connection.Open ();
199  
200 SqliteDataReader reader = sourceCommand.ExecuteReader (CommandBehavior.SchemaOnly |
201 CommandBehavior.KeyInfo);
202 _schemaTable = reader.GetSchemaTable ();
203 reader.Close ();
204 if (closeConnection)
205 connection.Close ();
206 BuildInformation (_schemaTable);
207 }
208 }
209  
210 private void BuildInformation (DataTable schemaTable)
211 {
212 _tableName = String.Empty;
213 foreach (DataRow schemaRow in schemaTable.Rows) {
214 if (schemaRow.IsNull ("BaseTableName") ||
215 (string) schemaRow ["BaseTableName"] == String.Empty)
216 continue;
217  
218 if (_tableName == String.Empty)
219 _tableName = (string) schemaRow ["BaseTableName"];
220 else if (_tableName != (string) schemaRow["BaseTableName"])
221 throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
222 }
223 if (_tableName == String.Empty)
224 throw new InvalidOperationException ("Dynamic SQL generation is not supported with no base table.");
225 _schemaTable = schemaTable;
226 }
227  
228 private SqliteCommand CreateInsertCommand (bool option)
229 {
230 if (QuotedTableName == String.Empty)
231 return null;
232  
233 CreateNewCommand (ref _insertCommand);
234  
235 string command = String.Format ("INSERT INTO {0}", QuotedTableName);
236 string sql;
237 StringBuilder columns = new StringBuilder ();
238 StringBuilder values = new StringBuilder ();
239  
240 int parmIndex = 1;
241 foreach (DataRow schemaRow in _schemaTable.Rows) {
242 if (!IncludedInInsert (schemaRow))
243 continue;
244  
245 if (parmIndex > 1) {
246 columns.Append (", ");
247 values.Append (", ");
248 }
249  
250 SqliteParameter parameter = null;
251 if (option) {
252 parameter = _insertCommand.Parameters.Add (CreateParameter (schemaRow));
253 } else {
254 parameter = _insertCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
255 }
256 parameter.SourceVersion = DataRowVersion.Current;
257 columns.Append (GetQuotedString (parameter.SourceColumn));
258 values.Append (parameter.ParameterName);
259 }
260  
261 sql = String.Format ("{0} ({1}) VALUES ({2})", command, columns.ToString (), values.ToString ());
262 _insertCommand.CommandText = sql;
263 return _insertCommand;
264 }
265  
266 private SqliteCommand CreateDeleteCommand (bool option)
267 {
268 // If no table was found, then we can't do an delete
269 if (QuotedTableName == String.Empty)
270 return null;
271  
272 CreateNewCommand (ref _deleteCommand);
273  
274 string command = String.Format ("DELETE FROM {0}", QuotedTableName);
275 StringBuilder whereClause = new StringBuilder ();
276 bool keyFound = false;
277 int parmIndex = 1;
278  
279 foreach (DataRow schemaRow in _schemaTable.Rows) {
280 if ((bool)schemaRow["IsExpression"] == true)
281 continue;
282 if (!IncludedInWhereClause (schemaRow))
283 continue;
284  
285 if (whereClause.Length > 0)
286 whereClause.Append (" AND ");
287  
288 bool isKey = (bool) schemaRow ["IsKey"];
289 SqliteParameter parameter = null;
290  
291 if (isKey)
292 keyFound = true;
293  
294 bool allowNull = (bool) schemaRow ["AllowDBNull"];
295 if (!isKey && allowNull) {
296 if (option) {
297 parameter = _deleteCommand.Parameters.Add (
298 String.Format ("@{0}", schemaRow ["BaseColumnName"]), DbType.Int32);
299 } else {
300 parameter = _deleteCommand.Parameters.Add (
301 String.Format ("@p{0}", parmIndex++), DbType.Int32);
302 }
303 String sourceColumnName = (string) schemaRow ["BaseColumnName"];
304 parameter.Value = 1;
305  
306 whereClause.Append ("(");
307 whereClause.Append (String.Format (clause1, parameter.ParameterName,
308 GetQuotedString (sourceColumnName)));
309 whereClause.Append (" OR ");
310 }
311  
312 if (option) {
313 parameter = _deleteCommand.Parameters.Add (CreateParameter (schemaRow));
314 } else {
315 parameter = _deleteCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
316 }
317 parameter.SourceVersion = DataRowVersion.Original;
318  
319 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn),
320 parameter.ParameterName));
321  
322 if (!isKey && allowNull)
323 whereClause.Append (")");
324 }
325 if (!keyFound)
326 throw new InvalidOperationException ("Dynamic SQL generation for the DeleteCommand is not supported against a SelectCommand that does not return any key column information.");
327  
328 string sql = String.Format ("{0} WHERE ({1})", command, whereClause.ToString ());
329 _deleteCommand.CommandText = sql;
330 return _deleteCommand;
331 }
332  
333 private SqliteCommand CreateUpdateCommand (bool option)
334 {
335 if (QuotedTableName == String.Empty)
336 return null;
337  
338 CreateNewCommand (ref _updateCommand);
339  
340 string command = String.Format ("UPDATE {0} SET ", QuotedTableName);
341 StringBuilder columns = new StringBuilder ();
342 StringBuilder whereClause = new StringBuilder ();
343 int parmIndex = 1;
344 bool keyFound = false;
345  
346 foreach (DataRow schemaRow in _schemaTable.Rows) {
347 if (!IncludedInUpdate (schemaRow))
348 continue;
349 if (columns.Length > 0)
350 columns.Append (", ");
351  
352 SqliteParameter parameter = null;
353 if (option) {
354 parameter = _updateCommand.Parameters.Add (CreateParameter (schemaRow));
355 } else {
356 parameter = _updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
357 }
358 parameter.SourceVersion = DataRowVersion.Current;
359  
360 columns.Append (String.Format ("{0} = {1}", GetQuotedString (parameter.SourceColumn),
361 parameter.ParameterName));
362 }
363  
364 foreach (DataRow schemaRow in _schemaTable.Rows) {
365 if ((bool)schemaRow["IsExpression"] == true)
366 continue;
367  
368 if (!IncludedInWhereClause (schemaRow))
369 continue;
370  
371 if (whereClause.Length > 0)
372 whereClause.Append (" AND ");
373  
374 bool isKey = (bool) schemaRow ["IsKey"];
375 SqliteParameter parameter = null;
376  
377 if (isKey)
378 keyFound = true;
379  
380 bool allowNull = (bool) schemaRow ["AllowDBNull"];
381 if (!isKey && allowNull) {
382 if (option) {
383 parameter = _updateCommand.Parameters.Add (
384 String.Format ("@{0}", schemaRow ["BaseColumnName"]), SqlDbType.Int);
385 } else {
386 parameter = _updateCommand.Parameters.Add (
387 String.Format ("@p{0}", parmIndex++), SqlDbType.Int);
388 }
389 parameter.Value = 1;
390 whereClause.Append ("(");
391 whereClause.Append (String.Format (clause1, parameter.ParameterName,
392 GetQuotedString ((string) schemaRow ["BaseColumnName"])));
393 whereClause.Append (" OR ");
394 }
395  
396 if (option) {
397 parameter = _updateCommand.Parameters.Add (CreateParameter (schemaRow));
398 } else {
399 parameter = _updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
400 }
401 parameter.SourceVersion = DataRowVersion.Original;
402 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn),
403 parameter.ParameterName));
404  
405 if (!isKey && allowNull)
406 whereClause.Append (")");
407 }
408 if (!keyFound)
409 throw new InvalidOperationException ("Dynamic SQL generation for the UpdateCommand is not supported against a SelectCommand that does not return any key column information.");
410  
411 string sql = String.Format ("{0}{1} WHERE ({2})", command, columns.ToString (), whereClause.ToString ());
412 _updateCommand.CommandText = sql;
413 return _updateCommand;
414 }
415  
416 private void CreateNewCommand (ref SqliteCommand command)
417 {
418 SqliteCommand sourceCommand = SourceCommand;
419 if (command == null) {
420 command = sourceCommand.Connection.CreateCommand () as SqliteCommand;
421 command.CommandTimeout = sourceCommand.CommandTimeout;
422 command.Transaction = sourceCommand.Transaction;
423 }
424 command.CommandType = CommandType.Text;
425 command.UpdatedRowSource = UpdateRowSource.None;
426 command.Parameters.Clear ();
427 }
428  
429 private bool IncludedInWhereClause (DataRow schemaRow)
430 {
431 if ((bool) schemaRow ["IsLong"])
432 return false;
433 return true;
434 }
435  
436 private bool IncludedInInsert (DataRow schemaRow)
437 {
438 // not all of the below are supported by Sqlite, but we leave them here anyway, since some day Sqlite may
439 // support some of them.
440 if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
441 return false;
442 if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
443 return false;
444 if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
445 return false;
446 if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
447 return false;
448 if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
449 return false;
450 return true;
451 }
452  
453 private bool IncludedInUpdate (DataRow schemaRow)
454 {
455 // not all of the below are supported by Sqlite, but we leave them here anyway, since some day Sqlite may
456 // support some of them.
457 if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
458 return false;
459 if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
460 return false;
461 if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
462 return false;
463 if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
464 return false;
465 if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
466 return false;
467  
468 return true;
469 }
470  
471 private SqliteParameter CreateParameter (DataRow schemaRow)
472 {
473 string sourceColumn = (string) schemaRow ["BaseColumnName"];
474 string name = String.Format ("@{0}", sourceColumn);
475 DbType dbType = (DbType) schemaRow ["ProviderType"];
476 int size = (int) schemaRow ["ColumnSize"];
477  
478 return new SqliteParameter (name, dbType, size, sourceColumn);
479 }
480  
481 private SqliteParameter CreateParameter (int parmIndex, DataRow schemaRow)
482 {
483 string name = String.Format ("@p{0}", parmIndex);
484 string sourceColumn = (string) schemaRow ["BaseColumnName"];
485 DbType dbType = (DbType) schemaRow ["ProviderType"];
486 int size = (int) schemaRow ["ColumnSize"];
487  
488 return new SqliteParameter (name, dbType, size, sourceColumn);
489 }
490  
491 private string GetQuotedString (string value)
492 {
493 if (value == String.Empty || value == null)
494 return value;
495 if (string.IsNullOrEmpty(_quotePrefix) && string.IsNullOrEmpty(_quoteSuffix))
496 return value;
497 return String.Format ("{0}{1}{2}", _quotePrefix, value, _quoteSuffix);
498 }
499  
500 private void RowUpdatingHandler (object sender, RowUpdatingEventArgs args)
501 {
502 if (args.Command != null)
503 return;
504 try {
505 switch (args.StatementType) {
506 case StatementType.Insert:
507 args.Command = GetInsertCommand ();
508 break;
509 case StatementType.Update:
510 args.Command = GetUpdateCommand ();
511 break;
512 case StatementType.Delete:
513 args.Command = GetDeleteCommand ();
514 break;
515 }
516 } catch (Exception e) {
517 args.Errors = e;
518 args.Status = UpdateStatus.ErrorsOccurred;
519 }
520 }
521 }
522 }