//
// Author:
// Tim Coleman (tim@timcoleman.com)
+// Veerapuram Varadhan (vvaradhan@novell.com)
//
// Copyright (C) Tim Coleman, 2002
//
//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004, 2009 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
using System.ComponentModel;
using System.Data;
using System.Data.Common;
+using System.Data.SqlTypes;
using System.Text;
-namespace System.Data.SqlClient {
-#if NET_2_0
+namespace System.Data.SqlClient
+{
public sealed class SqlCommandBuilder : DbCommandBuilder
-#else
- public sealed class SqlCommandBuilder : Component
-#endif // NET_2_0
{
#region Fields
- bool disposed = false;
-
- DataTable dbSchemaTable;
- SqlDataAdapter adapter;
- string quotePrefix;
- string quoteSuffix;
- string[] columnNames;
- string tableName;
+ readonly string _catalogSeparator = ".";
+ readonly string _schemaSeparator = ".";
+ readonly CatalogLocation _catalogLocation = CatalogLocation.Start;
- SqlCommand deleteCommand;
- SqlCommand insertCommand;
- SqlCommand updateCommand;
-
- // Used to construct WHERE clauses
- static readonly string clause1 = "({0} = 1 AND {1} IS NULL)";
- static readonly string clause2 = "({0} = {1})";
-
#endregion // Fields
#region Constructors
- public SqlCommandBuilder ()
+ public SqlCommandBuilder ()
{
- dbSchemaTable = null;
- adapter = null;
-#if NET_2_0
- quoteSuffix = "]";
- quotePrefix = "[";
-#else
- quoteSuffix = String.Empty;
- quotePrefix = String.Empty;
-#endif
+ QuoteSuffix = "]";
+ QuotePrefix = "[";
}
public SqlCommandBuilder (SqlDataAdapter adapter)
#region Properties
-#if !NET_2_0
- [DataSysDescription ("The DataAdapter for which to automatically generate SqlCommands")]
-#endif
[DefaultValue (null)]
public new SqlDataAdapter DataAdapter {
- get { return adapter; }
- set {
- if (adapter != null)
- adapter.RowUpdating -= new SqlRowUpdatingEventHandler (RowUpdatingHandler);
-
- adapter = value;
-
- if (adapter != null)
- adapter.RowUpdating += new SqlRowUpdatingEventHandler (RowUpdatingHandler);
+ get {
+ return (SqlDataAdapter)base.DataAdapter;
+ } set {
+ base.DataAdapter = value;
}
}
- private string QuotedTableName {
- get { return GetQuotedString (tableName); }
- }
-
[Browsable (false)]
-#if !NET_2_0
- [DataSysDescription ("The character used in a text command as the opening quote for quoting identifiers that contain special characters.")]
-#endif
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
- public
-#if NET_2_0
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public
override
-#endif // NET_2_0
- string QuotePrefix {
- get { return quotePrefix; }
- set {
- if (dbSchemaTable != null)
- throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
- quotePrefix = value;
+ string QuotePrefix {
+ get {
+ return base.QuotePrefix;
+ }
+ set {
+ if (value != "[" && value != "\"")
+ throw new ArgumentException ("Only '[' " +
+ "and '\"' are allowed as value " +
+ "for the 'QuoteSuffix' property.");
+ base.QuotePrefix = value;
}
}
[Browsable (false)]
-#if !NET_2_0
- [DataSysDescription ("The character used in a text command as the closing quote for quoting identifiers that contain special characters. ")]
-#endif
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
- public
-#if NET_2_0
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public
override
-#endif // NET_2_0
- string QuoteSuffix {
- get { return quoteSuffix; }
+ string QuoteSuffix {
+ get {
+ return base.QuoteSuffix;
+ }
set {
- if (dbSchemaTable != null)
- throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
- quoteSuffix = value;
+ if (value != "]" && value != "\"")
+ throw new ArgumentException ("Only ']' " +
+ "and '\"' are allowed as value " +
+ "for the 'QuoteSuffix' property.");
+ base.QuoteSuffix = value;
}
}
- private SqlCommand SourceCommand {
- get {
- if (adapter != null)
- return adapter.SelectCommand;
- return null;
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override string CatalogSeparator {
+ get { return _catalogSeparator; }
+ set {
+ if (value != _catalogSeparator)
+ throw new ArgumentException ("Only " +
+ "'.' is allowed as value " +
+ "for the 'CatalogSeparator' " +
+ "property.");
}
}
- #endregion // Properties
-
- #region Methods
-
- private void BuildCache (bool closeConnection)
- {
- SqlCommand sourceCommand = SourceCommand;
- if (sourceCommand == null)
- throw new InvalidOperationException ("The DataAdapter.SelectCommand property needs to be initialized.");
- SqlConnection connection = sourceCommand.Connection;
- if (connection == null)
- throw new InvalidOperationException ("The DataAdapter.SelectCommand.Connection property needs to be initialized.");
-
- if (dbSchemaTable == null) {
- if (connection.State == ConnectionState.Open)
- closeConnection = false;
- else
- connection.Open ();
-
- SqlDataReader reader = sourceCommand.ExecuteReader (CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
- dbSchemaTable = reader.GetSchemaTable ();
- reader.Close ();
- if (closeConnection)
- connection.Close ();
- BuildInformation (dbSchemaTable);
- }
- }
-
- private void BuildInformation (DataTable schemaTable)
- {
- tableName = String.Empty;
- foreach (DataRow schemaRow in schemaTable.Rows) {
- if (schemaRow.IsNull ("BaseTableName") ||
- schemaRow ["BaseTableName"] == String.Empty)
- continue;
-
- if (tableName == String.Empty)
- tableName = (string) schemaRow ["BaseTableName"];
- else if (tableName != (string) schemaRow["BaseTableName"])
- throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override string SchemaSeparator {
+ get { return _schemaSeparator; }
+ set {
+ if (value != _schemaSeparator)
+ throw new ArgumentException ("Only " +
+ "'.' is allowed as value " +
+ "for the 'SchemaSeparator' " +
+ "property.");
}
- if (tableName == String.Empty)
- throw new InvalidOperationException ("Dynamic SQL generation is not supported with no base table.");
- dbSchemaTable = schemaTable;
}
- private SqlCommand CreateDeleteCommand ()
- {
- // If no table was found, then we can't do an delete
- if (QuotedTableName == String.Empty)
- return null;
-
- CreateNewCommand (ref deleteCommand);
-
- string command = String.Format ("DELETE FROM {0}", QuotedTableName);
- StringBuilder columns = new StringBuilder ();
- StringBuilder whereClause = new StringBuilder ();
- string dsColumnName = String.Empty;
- bool keyFound = false;
- int parmIndex = 1;
-
- foreach (DataRow schemaRow in dbSchemaTable.Rows) {
- if ((bool)schemaRow["IsExpression"] == true)
- continue;
- if (!IncludedInWhereClause (schemaRow))
- continue;
-
- if (whereClause.Length > 0)
- whereClause.Append (" AND ");
-
- bool isKey = (bool) schemaRow ["IsKey"];
- SqlParameter parameter = null;
-
- if (isKey)
- keyFound = true;
-
- //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
- //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
- //following the 2.0 approach
- bool allowNull = (bool) schemaRow ["AllowDBNull"];
- if (!isKey && allowNull) {
- parameter = deleteCommand.Parameters.Add (String.Format ("@p{0}", parmIndex++),
- SqlDbType.Int);
- String sourceColumnName = (string) schemaRow ["BaseColumnName"];
- parameter.Value = 1;
-
- whereClause.Append ("(");
- whereClause.Append (String.Format (clause1, parameter.ParameterName,
- GetQuotedString (sourceColumnName)));
- whereClause.Append (" OR ");
- }
-
- parameter = deleteCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
- parameter.SourceVersion = DataRowVersion.Original;
-
- whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
-
- if (!isKey && allowNull)
- whereClause.Append (")");
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override CatalogLocation CatalogLocation {
+ get { return _catalogLocation; }
+ set {
+ if (value != CatalogLocation.Start)
+ throw new ArgumentException ("Only " +
+ "'Start' is allowed as value " +
+ "for the 'CatalogLocation' " +
+ "property.");
}
- if (!keyFound)
- throw new InvalidOperationException ("Dynamic SQL generation for the DeleteCommand is not supported against a SelectCommand that does not return any key column information.");
-
- // We're all done, so bring it on home
- string sql = String.Format ("{0} WHERE ({1})", command, whereClause.ToString ());
- deleteCommand.CommandText = sql;
- return deleteCommand;
}
- private SqlCommand CreateInsertCommand ()
- {
- if (QuotedTableName == String.Empty)
- return null;
-
- CreateNewCommand (ref insertCommand);
- string command = String.Format ("INSERT INTO {0}", QuotedTableName);
- string sql;
- StringBuilder columns = new StringBuilder ();
- StringBuilder values = new StringBuilder ();
- string dsColumnName = String.Empty;
- int parmIndex = 1;
- foreach (DataRow schemaRow in dbSchemaTable.Rows) {
- if (!IncludedInInsert (schemaRow))
- continue;
-
- if (parmIndex > 1) {
- columns.Append (", ");
- values.Append (", ");
- }
-
- SqlParameter parameter = insertCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
- parameter.SourceVersion = DataRowVersion.Current;
-
- columns.Append (GetQuotedString (parameter.SourceColumn));
- values.Append (parameter.ParameterName);
- }
+ #endregion // Properties
- sql = String.Format ("{0} ({1}) VALUES ({2})", command, columns.ToString (), values.ToString ());
- insertCommand.CommandText = sql;
- return insertCommand;
- }
+ #region Methods
- private void CreateNewCommand (ref SqlCommand command)
+
+ public static void DeriveParameters (SqlCommand command)
{
- SqlCommand sourceCommand = SourceCommand;
- if (command == null) {
- command = sourceCommand.Connection.CreateCommand ();
- command.CommandTimeout = sourceCommand.CommandTimeout;
- command.Transaction = sourceCommand.Transaction;
- }
- command.CommandType = CommandType.Text;
- command.UpdatedRowSource = UpdateRowSource.None;
- command.Parameters.Clear ();
+ command.DeriveParameters ();
}
- private SqlCommand CreateUpdateCommand ()
- {
- // If no table was found, then we can't do an update
- if (QuotedTableName == String.Empty)
- return null;
-
- CreateNewCommand (ref updateCommand);
-
- string command = String.Format ("UPDATE {0} SET ", QuotedTableName);
- StringBuilder columns = new StringBuilder ();
- StringBuilder whereClause = new StringBuilder ();
- int parmIndex = 1;
- string dsColumnName = String.Empty;
- bool keyFound = false;
-
- // First, create the X=Y list for UPDATE
- foreach (DataRow schemaRow in dbSchemaTable.Rows) {
- if (!IncludedInUpdate (schemaRow))
- continue;
- if (columns.Length > 0)
- columns.Append (", ");
-
- SqlParameter parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
- parameter.SourceVersion = DataRowVersion.Current;
-
- columns.Append (String.Format ("{0} = {1}", GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
- }
-
- // Now, create the WHERE clause. This may be optimizable, but it would be ugly to incorporate
- // into the loop above. "Premature optimization is the root of all evil." -- Knuth
- foreach (DataRow schemaRow in dbSchemaTable.Rows) {
- if ((bool)schemaRow["IsExpression"] == true)
- continue;
-
- if (!IncludedInWhereClause (schemaRow))
- continue;
-
- if (whereClause.Length > 0)
- whereClause.Append (" AND ");
-
- bool isKey = (bool) schemaRow ["IsKey"];
- SqlParameter parameter = null;
-
-
- if (isKey)
- keyFound = true;
-
- //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
- //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
- //following the 2.0 approach
- bool allowNull = (bool) schemaRow ["AllowDBNull"];
- if (!isKey && allowNull) {
- parameter = updateCommand.Parameters.Add (String.Format ("@p{0}", parmIndex++),
- SqlDbType.Int);
- parameter.Value = 1;
- whereClause.Append ("(");
- whereClause.Append (String.Format (clause1, parameter.ParameterName,
- GetQuotedString ((string) schemaRow ["BaseColumnName"])));
- whereClause.Append (" OR ");
- }
-
- parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
- parameter.SourceVersion = DataRowVersion.Original;
-
- whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
-
- if (!isKey && allowNull)
- whereClause.Append (")");
- }
- if (!keyFound)
- throw new InvalidOperationException ("Dynamic SQL generation for the UpdateCommand is not supported against a SelectCommand that does not return any key column information.");
-
- // We're all done, so bring it on home
- string sql = String.Format ("{0}{1} WHERE ({2})", command, columns.ToString (), whereClause.ToString ());
- updateCommand.CommandText = sql;
- return updateCommand;
- }
- private SqlParameter CreateParameter (int parmIndex, DataRow schemaRow)
+ public
+ new
+ SqlCommand GetDeleteCommand ()
{
- string name = String.Format ("@p{0}", parmIndex);
- string sourceColumn = (string) schemaRow ["BaseColumnName"];
- SqlDbType sqlDbType = (SqlDbType) schemaRow ["ProviderType"];
- int size = (int) schemaRow ["ColumnSize"];
-
- return new SqlParameter (name, sqlDbType, size, sourceColumn);
+ return (SqlCommand) base.GetDeleteCommand (false);
}
- public static void DeriveParameters (SqlCommand command)
+ public
+ new
+ SqlCommand GetInsertCommand ()
{
- command.DeriveParameters ();
+ return (SqlCommand) base.GetInsertCommand (false);
}
- protected override void Dispose (bool disposing)
+ public
+ new
+ SqlCommand GetUpdateCommand ()
{
- if (!disposed) {
- if (disposing) {
- if (insertCommand != null)
- insertCommand.Dispose ();
- if (deleteCommand != null)
- deleteCommand.Dispose ();
- if (updateCommand != null)
- updateCommand.Dispose ();
- if (dbSchemaTable != null)
- dbSchemaTable.Dispose ();
- }
- disposed = true;
- }
+ return (SqlCommand) base.GetUpdateCommand (false);
}
- public
-#if NET_2_0
- new
-#endif // NET_2_0
- SqlCommand GetDeleteCommand ()
+ public new SqlCommand GetUpdateCommand (bool useColumnsForParameterNames)
{
- BuildCache (true);
- if (deleteCommand == null)
- return CreateDeleteCommand ();
- return deleteCommand;
+ return (SqlCommand) base.GetUpdateCommand (useColumnsForParameterNames);
}
- public
-#if NET_2_0
- new
-#endif // NET_2_0
- SqlCommand GetInsertCommand ()
+ public new SqlCommand GetDeleteCommand (bool useColumnsForParameterNames)
{
- BuildCache (true);
- if (insertCommand == null)
- return CreateInsertCommand ();
- return insertCommand;
+ return (SqlCommand) base.GetDeleteCommand (useColumnsForParameterNames);
}
- private string GetQuotedString (string value)
+ public new SqlCommand GetInsertCommand (bool useColumnsForParameterNames)
{
- if (value == String.Empty || value == null)
- return value;
- if (quotePrefix == String.Empty && quoteSuffix == String.Empty)
- return value;
- return String.Format ("{0}{1}{2}", quotePrefix, value, quoteSuffix);
+ return (SqlCommand) base.GetInsertCommand (useColumnsForParameterNames);
}
+
+ public override string QuoteIdentifier (string unquotedIdentifier)
+ {
+ if (unquotedIdentifier == null)
+ throw new ArgumentNullException ("unquotedIdentifier");
- public
-#if NET_2_0
- new
-#endif // NET_2_0
- SqlCommand GetUpdateCommand ()
+ string prefix = QuotePrefix;
+ string suffix = QuoteSuffix;
+
+ if ((prefix == "[" && suffix != "]") || (prefix == "\"" && suffix != "\""))
+ throw new ArgumentException ("The QuotePrefix " +
+ "and QuoteSuffix properties do not match.");
+
+ string escaped = unquotedIdentifier.Replace (suffix,
+ suffix + suffix);
+ return string.Concat (prefix, escaped, suffix);
+ }
+
+ public override string UnquoteIdentifier (string quotedIdentifier)
{
- BuildCache (true);
- if (updateCommand == null)
- return CreateUpdateCommand ();
- return updateCommand;
+ return base.UnquoteIdentifier (quotedIdentifier);
}
private bool IncludedInInsert (DataRow schemaRow)
return true;
}
- [MonoTODO ("Figure out what else needs to be cleaned up when we refresh.")]
- public
-#if NET_2_0
- override
-#endif // NET_2_0
- void RefreshSchema ()
+
+ protected override void ApplyParameterInfo (DbParameter parameter,
+ DataRow datarow,
+ StatementType statementType,
+ bool whereClause)
+ {
+ SqlParameter sqlParam = (SqlParameter) parameter;
+ sqlParam.SqlDbType = (SqlDbType) datarow ["ProviderType"];
+
+ object precision = datarow ["NumericPrecision"];
+ if (precision != DBNull.Value) {
+ short val = (short) precision;
+ if (val < byte.MaxValue && val >= byte.MinValue)
+ sqlParam.Precision = (byte) val;
+ }
+
+ object scale = datarow ["NumericScale"];
+ if (scale != DBNull.Value) {
+ short val = ((short) scale);
+ if (val < byte.MaxValue && val >= byte.MinValue)
+ sqlParam.Scale = (byte) val;
+ }
+ }
+
+ protected override
+ string GetParameterName (int parameterOrdinal)
+ {
+ return String.Format ("@p{0}", parameterOrdinal);
+ }
+
+ protected override
+ string GetParameterName (string parameterName)
+ {
+ return String.Format ("@{0}", parameterName);
+ }
+
+ protected override string GetParameterPlaceholder (int parameterOrdinal)
{
- tableName = String.Empty;
- dbSchemaTable = null;
- CreateNewCommand (ref deleteCommand);
- CreateNewCommand (ref updateCommand);
- CreateNewCommand (ref insertCommand);
+ return GetParameterName (parameterOrdinal);
}
-#if NET_2_0
- [MonoTODO]
- protected override void ApplyParameterInfo (DbParameter dbParameter,
- DataRow row,
- StatementType statementType,
- bool whereClause)
- {
- throw new NotImplementedException ();
- }
-
- [MonoTODO]
- protected override string GetParameterName (int position)
- {
- throw new NotImplementedException ();
- }
-
- [MonoTODO]
- protected override string GetParameterName (string parameterName)
- {
- throw new NotImplementedException ();
- }
-
-
- [MonoTODO]
- protected override string GetParameterPlaceholder (int position)
- {
- throw new NotImplementedException ();
- }
-
-#endif // NET_2_0
#endregion // Methods
#region Event Handlers
- private void RowUpdatingHandler (object sender, SqlRowUpdatingEventArgs args)
+ void RowUpdatingHandler (object sender, SqlRowUpdatingEventArgs args)
+ {
+ base.RowUpdatingHandler (args);
+ }
+
+ protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
{
- if (args.Command != null)
- return;
- try {
- switch (args.StatementType) {
- case StatementType.Insert:
- args.Command = GetInsertCommand ();
- break;
- case StatementType.Update:
- args.Command = GetUpdateCommand ();
- break;
- case StatementType.Delete:
- args.Command = GetDeleteCommand ();
- break;
+ SqlDataAdapter sda = adapter as SqlDataAdapter;
+ if (sda == null) {
+ throw new InvalidOperationException ("Adapter needs to be a SqlDataAdapter");
}
- } catch (Exception e) {
- args.Errors = e;
- args.Status = UpdateStatus.ErrorsOccurred;
- } \r
+
+ if (sda != base.DataAdapter)
+ sda.RowUpdating += new SqlRowUpdatingEventHandler (RowUpdatingHandler);
+ else
+ sda.RowUpdating -= new SqlRowUpdatingEventHandler (RowUpdatingHandler);;
+ }
+
+ protected override DataTable GetSchemaTable (DbCommand srcCommand)
+ {
+ using (SqlDataReader rdr = (SqlDataReader) srcCommand.ExecuteReader (CommandBehavior.KeyInfo | CommandBehavior.SchemaOnly))
+ return rdr.GetSchemaTable ();
}
-#if NET_2_0
- [MonoTODO]
- protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
- {
- throw new NotImplementedException ();
- }
-#endif // NET_2_0
+ protected override DbCommand InitializeCommand (DbCommand command)
+ {
+ if (command == null) {
+ command = new SqlCommand ();
+ } else {
+ command.CommandTimeout = 30;
+ command.Transaction = null;
+ command.CommandType = CommandType.Text;
+ command.UpdatedRowSource = UpdateRowSource.None;
+ }
+ return command;
+ }
#endregion // Event Handlers
}
}
-