2 // System.Data.OracleClient.OracleCommandBuilder.cs
4 // based on the SqlCommandBuilder in mcs/class/System.Data/System.Data.SqlClient
7 // Atsushi Enomoto <atsushi@ximian.com>
8 // Tim Coleman (tim@timcoleman.com)
9 // Daniel Morgan <danielmorgan@verizon.net>
11 // Copyright (C) Tim Coleman, 2002
12 // Copyright (C) Daniel Morgan, 2005
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.ComponentModel;
39 using System.Data.Common;
42 namespace System.Data.OracleClient {
44 public sealed class OracleCommandBuilder : DbCommandBuilder
46 public sealed class OracleCommandBuilder : Component
51 bool disposed = false;
53 DataTable dbSchemaTable;
54 OracleDataAdapter adapter;
60 OracleCommand deleteCommand;
61 OracleCommand insertCommand;
62 OracleCommand updateCommand;
64 // Used to construct WHERE clauses
65 static readonly string clause1 = "({0} IS NULL AND {1} IS NULL)";
66 static readonly string clause2 = "({0} = {1})";
72 public OracleCommandBuilder () {
75 quoteSuffix = String.Empty;
76 quotePrefix = String.Empty;
79 public OracleCommandBuilder (OracleDataAdapter adapter)
81 DataAdapter = adapter;
84 #endregion // Constructors
88 //[DataSysDescription ("The DataAdapter for which to automatically generate OracleCommands")]
90 public new OracleDataAdapter DataAdapter {
91 get { return adapter; }
94 adapter.RowUpdating -= new OracleRowUpdatingEventHandler (RowUpdatingHandler);
99 adapter.RowUpdating += new OracleRowUpdatingEventHandler (RowUpdatingHandler);
103 private string QuotedTableName {
104 get { return GetQuotedString (tableName); }
108 //[DataSysDescription ("The character used in a text command as the opening quote for quoting identifiers that contain special characters.")]
109 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
115 get { return quotePrefix; }
117 if (dbSchemaTable != null)
118 throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
124 //[DataSysDescription ("The character used in a text command as the closing quote for quoting identifiers that contain special characters.")]
125 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
131 get { return quoteSuffix; }
133 if (dbSchemaTable != null)
134 throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
139 private OracleCommand SourceCommand {
142 return adapter.SelectCommand;
147 #endregion // Properties
151 private void BuildCache (bool closeConnection)
153 OracleCommand sourceCommand = SourceCommand;
154 if (sourceCommand == null)
155 throw new InvalidOperationException ("The DataAdapter.SelectCommand property needs to be initialized.");
156 OracleConnection connection = sourceCommand.Connection;
157 if (connection == null)
158 throw new InvalidOperationException ("The DataAdapter.SelectCommand.Connection property needs to be initialized.");
160 if (dbSchemaTable == null) {
161 if (connection.State == ConnectionState.Open)
162 closeConnection = false;
166 OracleDataReader reader = sourceCommand.ExecuteReader (CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
167 dbSchemaTable = reader.GetSchemaTable ();
171 BuildInformation (dbSchemaTable);
175 private void BuildInformation (DataTable schemaTable)
177 tableName = String.Empty;
178 foreach (DataRow schemaRow in schemaTable.Rows) {
179 if (tableName == String.Empty)
180 tableName = schemaRow.IsNull ("BaseTableName") ? null : (string) schemaRow ["BaseTableName"];
181 else if (schemaRow.IsNull ("BaseTableName")) {
182 if (tableName != null)
183 throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
184 } else if (tableName != (string) schemaRow["BaseTableName"])
185 throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
187 dbSchemaTable = schemaTable;
190 private OracleCommand CreateDeleteCommand (DataRow row, DataTableMapping tableMapping)
192 // If no table was found, then we can't do an delete
193 if (QuotedTableName == String.Empty)
197 CreateNewCommand (ref deleteCommand);
199 string command = String.Format ("DELETE FROM {0} ", QuotedTableName);
200 StringBuilder whereClause = new StringBuilder ();
201 string dsColumnName = String.Empty;
202 bool keyFound = false;
205 foreach (DataRow schemaRow in dbSchemaTable.Rows) {
206 if (!IncludedInWhereClause (schemaRow))
209 if (whereClause.Length > 0)
210 whereClause.Append (" AND ");
212 bool isKey = (bool) schemaRow ["IsKey"];
213 OracleParameter parameter = null;
216 parameter = deleteCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
217 parameter.SourceVersion = DataRowVersion.Original;
219 dsColumnName = parameter.SourceColumn;
220 if (tableMapping != null
221 && tableMapping.ColumnMappings.Contains (parameter.SourceColumn))
222 dsColumnName = tableMapping.ColumnMappings [parameter.SourceColumn].DataSetColumn;
225 parameter.Value = row [dsColumnName, DataRowVersion.Original];
226 whereClause.Append ("(");
227 whereClause.Append (String.Format (clause1, GetQuotedString (parameter.SourceColumn), ":" + parameter.ParameterName));
228 whereClause.Append (" OR ");
233 parameter = deleteCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
234 parameter.SourceVersion = DataRowVersion.Original;
236 dsColumnName = parameter.SourceColumn;
237 if (tableMapping != null
238 && tableMapping.ColumnMappings.Contains (parameter.SourceColumn))
239 dsColumnName = tableMapping.ColumnMappings [parameter.SourceColumn].DataSetColumn;
242 parameter.Value = row [dsColumnName, DataRowVersion.Original];
244 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), ":" + parameter.ParameterName));
247 whereClause.Append (")");
250 throw new InvalidOperationException ("Dynamic SQL generation for the DeleteCommand is not supported against a SelectCommand that does not return any key column information.");
252 // We're all done, so bring it on home
253 string sql = String.Format ("{0} WHERE ( {1} )", command, whereClause.ToString ());
254 deleteCommand.CommandText = sql;
255 return deleteCommand;
258 private OracleCommand CreateInsertCommand (DataRow row, DataTableMapping tableMapping)
260 if (QuotedTableName == String.Empty)
263 CreateNewCommand (ref insertCommand);
265 string command = String.Format ("INSERT INTO {0}", QuotedTableName);
267 StringBuilder columns = new StringBuilder ();
268 StringBuilder values = new StringBuilder ();
269 string dsColumnName = String.Empty;
272 foreach (DataRow schemaRow in dbSchemaTable.Rows) {
273 if (!IncludedInInsert (schemaRow))
277 columns.Append (" , ");
278 values.Append (" , ");
281 OracleParameter parameter = insertCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
282 parameter.SourceVersion = DataRowVersion.Current;
284 dsColumnName = parameter.SourceColumn;
285 if (tableMapping != null
286 && tableMapping.ColumnMappings.Contains (parameter.SourceColumn))
287 dsColumnName = tableMapping.ColumnMappings [parameter.SourceColumn].DataSetColumn;
290 parameter.Value = row [dsColumnName, DataRowVersion.Current];
292 columns.Append (GetQuotedString (parameter.SourceColumn));
293 values.Append (":" + parameter.ParameterName);
296 sql = String.Format ("{0}( {1} ) VALUES ( {2} )", command, columns.ToString (), values.ToString ());
297 insertCommand.CommandText = sql;
298 return insertCommand;
301 private void CreateNewCommand (ref OracleCommand command) {
302 OracleCommand sourceCommand = SourceCommand;
303 if (command == null) {
304 command = sourceCommand.Connection.CreateCommand ();
305 command.Transaction = sourceCommand.Transaction;
307 command.CommandType = CommandType.Text;
308 command.UpdatedRowSource = UpdateRowSource.None;
311 private OracleCommand CreateUpdateCommand (DataRow row, DataTableMapping tableMapping)
313 // If no table was found, then we can't do an update
314 if (QuotedTableName == String.Empty)
317 CreateNewCommand (ref updateCommand);
319 string command = String.Format ("UPDATE {0} SET ", QuotedTableName);
320 StringBuilder columns = new StringBuilder ();
321 StringBuilder whereClause = new StringBuilder ();
323 string dsColumnName = String.Empty;
324 bool keyFound = false;
326 // First, create the X=Y list for UPDATE
327 foreach (DataRow schemaRow in dbSchemaTable.Rows) {
328 if (columns.Length > 0)
329 columns.Append (" , ");
331 OracleParameter parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
332 parameter.SourceVersion = DataRowVersion.Current;
334 dsColumnName = parameter.SourceColumn;
335 if (tableMapping != null
336 && tableMapping.ColumnMappings.Contains (parameter.SourceColumn))
337 dsColumnName = tableMapping.ColumnMappings [parameter.SourceColumn].DataSetColumn;
340 parameter.Value = row [dsColumnName, DataRowVersion.Current];
342 columns.Append (String.Format ("{0} = {1}", GetQuotedString (parameter.SourceColumn), ":" + parameter.ParameterName));
345 // Now, create the WHERE clause. This may be optimizable, but it would be ugly to incorporate
346 // into the loop above. "Premature optimization is the root of all evil." -- Knuth
347 foreach (DataRow schemaRow in dbSchemaTable.Rows) {
348 if (!IncludedInWhereClause (schemaRow))
351 if (whereClause.Length > 0)
352 whereClause.Append (" AND ");
354 bool isKey = (bool) schemaRow ["IsKey"];
355 OracleParameter parameter = null;
358 parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
359 parameter.SourceVersion = DataRowVersion.Original;
361 dsColumnName = parameter.SourceColumn;
362 if (tableMapping != null
363 && tableMapping.ColumnMappings.Contains (parameter.SourceColumn))
364 dsColumnName = tableMapping.ColumnMappings [parameter.SourceColumn].DataSetColumn;
367 parameter.Value = row [dsColumnName, DataRowVersion.Original];
369 whereClause.Append ("(");
370 whereClause.Append (String.Format (clause1, GetQuotedString (parameter.SourceColumn), ":" + parameter.ParameterName));
371 whereClause.Append (" OR ");
376 parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
377 parameter.SourceVersion = DataRowVersion.Original;
379 dsColumnName = parameter.SourceColumn;
380 if (tableMapping != null
381 && tableMapping.ColumnMappings.Contains (parameter.SourceColumn))
382 dsColumnName = tableMapping.ColumnMappings [parameter.SourceColumn].DataSetColumn;
385 parameter.Value = row [dsColumnName, DataRowVersion.Original];
387 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), ":" + parameter.ParameterName));
390 whereClause.Append (")");
393 throw new InvalidOperationException ("Dynamic SQL generation for the UpdateCommand is not supported against a SelectCommand that does not return any key column information.");
395 // We're all done, so bring it on home
396 string sql = String.Format ("{0}{1} WHERE ( {2} )", command, columns.ToString (), whereClause.ToString ());
397 updateCommand.CommandText = sql;
398 return updateCommand;
401 private OracleParameter CreateParameter (int parmIndex, DataRow schemaRow)
403 string name = String.Format ("p{0}", parmIndex);
405 string sourceColumn = (string) schemaRow ["BaseColumnName"];
406 int providerType = (int) schemaRow ["ProviderType"];
407 OracleType providerDbType = (OracleType) providerType;
408 int size = (int) schemaRow ["ColumnSize"];
410 return new OracleParameter (name, providerDbType, size, sourceColumn);
413 public static void DeriveParameters (OracleCommand command)
415 command.DeriveParameters ();
418 protected override void Dispose (bool disposing)
422 if (insertCommand != null)
423 insertCommand.Dispose ();
424 if (deleteCommand != null)
425 deleteCommand.Dispose ();
426 if (updateCommand != null)
427 updateCommand.Dispose ();
428 if (dbSchemaTable != null)
429 dbSchemaTable.Dispose ();
439 OracleCommand GetDeleteCommand ()
442 return CreateDeleteCommand (null, null);
449 OracleCommand GetInsertCommand ()
452 return CreateInsertCommand (null, null);
455 private string GetQuotedString (string value)
457 if (value == String.Empty || value == null)
459 if (quotePrefix == String.Empty && quoteSuffix == String.Empty)
461 return String.Format ("{0}{1}{2}", quotePrefix, value, quoteSuffix);
468 OracleCommand GetUpdateCommand ()
471 return CreateUpdateCommand (null, null);
474 private bool IncludedInInsert (DataRow schemaRow)
476 // If the parameter has one of these properties, then we don't include it in the insert:
477 if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
482 private bool IncludedInUpdate (DataRow schemaRow) {
483 // If the parameter has one of these properties, then we don't include it in the insert:
484 // AutoIncrement, Hidden, RowVersion
489 private bool IncludedInWhereClause (DataRow schemaRow) {
490 if ((bool) schemaRow ["IsLong"])
495 [MonoTODO ("Figure out what else needs to be cleaned up when we refresh.")]
500 void RefreshSchema ()
502 tableName = String.Empty;
503 dbSchemaTable = null;
508 protected override void ApplyParameterInfo (IDbDataParameter dbParameter, DataRow row)
510 throw new NotImplementedException ();
514 protected override string GetParameterName (int position)
516 throw new NotImplementedException ();
521 protected override string GetParameterPlaceholder (int position)
523 throw new NotImplementedException ();
527 protected override DbProviderFactory ProviderFactory
529 get {throw new NotImplementedException ();}
532 #endregion // Methods
534 #region Event Handlers
536 private void RowUpdatingHandler (object sender, OracleRowUpdatingEventArgs args)
538 if (args.Command != null)
541 switch (args.StatementType) {
542 case StatementType.Insert:
543 args.Command = GetInsertCommand ();
545 case StatementType.Update:
546 args.Command = GetUpdateCommand ();
548 case StatementType.Delete:
549 args.Command = GetDeleteCommand ();
552 } catch (Exception e) {
554 args.Status = UpdateStatus.ErrorsOccurred;
560 protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
562 throw new NotImplementedException ();
566 #endregion // Event Handlers