context: //
  // System.Data.SqlClient.SqlCommandBuilder.cs
  //
  // Author:
  //   Tim Coleman (tim@timcoleman.com)
  //   Veerapuram Varadhan (vvaradhan@novell.com)
  //
  // Copyright (C) Tim Coleman, 2002
  //
  
  //
  // 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
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System;
  using System.Collections;
  using System.ComponentModel;
  using System.Data;
  using System.Data.Common;
  #if NET_2_0
  using System.Data.SqlTypes;
  #endif
  using System.Text;
  
  namespace System.Data.SqlClient
  {
  #if NET_2_0
  	public sealed class SqlCommandBuilder : DbCommandBuilder
  #else
  	public sealed class SqlCommandBuilder : Component
  #endif // NET_2_0
  	{
  		#region Fields
  
  #if ONLY_1_1
  		bool disposed;
  
  		DataTable dbSchemaTable;
  		string quotePrefix;
  		string quoteSuffix;
  		string tableName;
  		SqlDataAdapter adapter;
  		SqlCommand insertCommand;
  		SqlCommand deleteCommand;
  		SqlCommand updateCommand;
  		// Used to construct WHERE clauses
  		static readonly string clause1 = "({0} = 1 AND {1} IS NULL)";
  		static readonly string clause2 = "({0} = {1})";
  
  #else
  		readonly string _catalogSeparator = ".";
  		readonly string _schemaSeparator = ".";
  		readonly CatalogLocation _catalogLocation = CatalogLocation.Start;
  #endif
  	
  		#endregion // Fields
  
  		#region Constructors
  
  		public SqlCommandBuilder ()
  		{
  #if NET_2_0
  			QuoteSuffix = "]";
  			QuotePrefix = "[";
  #endif
  		}
  
  		public SqlCommandBuilder (SqlDataAdapter adapter)
  			: this ()
  		{
  			DataAdapter = adapter;
  		}
  
  		#endregion // Constructors
  
  		#region Properties
  
  #if !NET_2_0
  		[DataSysDescription ("The DataAdapter for which to automatically generate SqlCommands")]
  #endif
  		[DefaultValue (null)]
  		public new SqlDataAdapter DataAdapter {
  			get { 
  #if ONLY_1_1
  				return adapter;
  #else
  				return (SqlDataAdapter)base.DataAdapter;
  #endif
  			} set {
  #if ONLY_1_1
                 if (adapter != null)
                         adapter.RowUpdating -= new SqlRowUpdatingEventHandler (RowUpdatingHandler);
  
                 adapter = value; 
                 if (adapter != null)
                         adapter.RowUpdating += new SqlRowUpdatingEventHandler (RowUpdatingHandler);
  #else
  				base.DataAdapter = value;
  #endif
  			}
  		}
  
  		[Browsable (false)]
  		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  #if !NET_2_0
  		[DataSysDescription ("The character used in a text command as the opening quote for quoting identifiers that contain special characters.")]
  #else
  		[EditorBrowsable (EditorBrowsableState.Never)]
  #endif // NET_2_0
  		public
  #if NET_2_0
  		override
  #endif // NET_2_0
  		string QuotePrefix {
  			get {
  #if ONLY_1_1
  				if (quotePrefix == null)
  					return string.Empty;
  				return quotePrefix;
  #else
  				return base.QuotePrefix;
  #endif
  			}
  			set {
  #if ONLY_1_1
  				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;
  #else
  				if (value != "[" && value != "\"")
  					throw new ArgumentException ("Only '[' " +
  						"and '\"' are allowed as value " +
  						"for the 'QuoteSuffix' property.");
  				base.QuotePrefix = value;
  #endif
  			}
  		}
  
  		[Browsable (false)]
  		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  #if !NET_2_0
  		[DataSysDescription ("The character used in a text command as the closing quote for quoting identifiers that contain special characters. ")]
  #else
  		[EditorBrowsable (EditorBrowsableState.Never)]
  #endif // NET_2_0
  		public
  #if NET_2_0
  		override
  #endif // NET_2_0
  		string QuoteSuffix {
  			get {
  #if ONLY_1_1
  				if (quoteSuffix == null)
  					return string.Empty;
  				return quoteSuffix;
  #else
  				return base.QuoteSuffix;
  #endif
  			}
  			set {
  #if ONLY_1_1
  				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;
  #else
  				if (value != "]" && value != "\"")
  					throw new ArgumentException ("Only ']' " +
  						"and '\"' are allowed as value " +
  						"for the 'QuoteSuffix' property.");
  				base.QuoteSuffix = value;
  #endif
  			}
  		}
  
  #if NET_2_0
  		[EditorBrowsable (EditorBrowsableState.Never)]
  		[Browsable (false)]
  		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  #if !NET_2_0
  		[DefaultValue (".")]
  #endif
  		public override string CatalogSeparator {
  			get { return _catalogSeparator; }
  			set {
  				if (value != _catalogSeparator)
  					throw new ArgumentException ("Only " +
  						"'.' is allowed as value " +
  						"for the 'CatalogSeparator' " +
  						"property.");
  			}
  		}
  
  		[EditorBrowsable (EditorBrowsableState.Never)]
  		[Browsable (false)]
  		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  #if !NET_2_0
  		[DefaultValue (".")]
  #endif
  		public override string SchemaSeparator {
  			get { return _schemaSeparator; }
  			set {
  				if (value != _schemaSeparator)
  					throw new ArgumentException ("Only " +
  						"'.' is allowed as value " +
  						"for the 'SchemaSeparator' " +
  						"property.");
  			}
  		}
  
  		[EditorBrowsable (EditorBrowsableState.Never)]
  		[Browsable (false)]
  		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  #if !NET_2_0
  		[DefaultValue (CatalogLocation.Start)]
  #endif
  		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.");
  			}
  		}
  
  #endif // NET_2_0
  
  #if ONLY_1_1
  		private SqlCommand SourceCommand {
  			get {
  				if (adapter != null)
  					return adapter.SelectCommand;
  				return null;
  			}
  		}
  #endif
  
  		#endregion // Properties
  
  		#region Methods
  
  #if ONLY_1_1
  		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") ||
  				    (string) 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.");
  			}
  			if (tableName == String.Empty)
  				throw new InvalidOperationException ("Dynamic SQL generation is not supported with no base table.");
  			dbSchemaTable = schemaTable;
  		}
  
  		private SqlCommand CreateDeleteCommand (bool useColumnsForParameterNames)
  		{
  			// 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 whereClause = new StringBuilder ();
  			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;
  
  				bool allowNull = (bool) schemaRow ["AllowDBNull"];
  				if (!isKey) {
  					string sourceColumnName = (string) schemaRow ["BaseColumnName"];
  					if (useColumnsForParameterNames) {
  						parameter = deleteCommand.Parameters.Add (
  							GetNullCheckParameterName (sourceColumnName),
  							SqlDbType.Int);
  					} else {
  						parameter = deleteCommand.Parameters.Add (
  							GetParameterName (parmIndex++),
  							SqlDbType.Int);
  					}
  					parameter.IsNullable = allowNull;
  					parameter.SourceVersion = DataRowVersion.Current;
  					parameter.Value = 1;
  
  					whereClause.Append ("(");
  					whereClause.Append (String.Format (clause1, parameter.ParameterName,
  									GetQuotedString (sourceColumnName)));
  					whereClause.Append (" OR ");
  				}
  
  				if (useColumnsForParameterNames)
  					parameter = CreateParameter (schemaRow, true);
  				else
  					parameter = CreateParameter (parmIndex++, schemaRow);
  				deleteCommand.Parameters.Add (parameter);
  				ApplyParameterInfo (parameter, schemaRow, StatementType.Delete, true);
  				parameter.IsNullable = allowNull;
  				parameter.SourceVersion = DataRowVersion.Original;
  
  				whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
  
  				if (!isKey)
  					whereClause.Append (")");
  			}
  			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 (bool useColumnsForParameterNames)
  		{
  			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 ();
  
  			int parmIndex = 1;
  			foreach (DataRow schemaRow in dbSchemaTable.Rows) {
  				if (!IncludedInInsert (schemaRow))
  					continue;
  
  				if (parmIndex > 1) {
  					columns.Append (" , ");
  					values.Append (" , ");
  				}
  
  				SqlParameter parameter = null;
  				if (useColumnsForParameterNames) {
  					parameter = CreateParameter (schemaRow, false);
  				} else {
  					parameter = CreateParameter (parmIndex, schemaRow);
  				}
  
  				insertCommand.Parameters.Add (parameter);
  				ApplyParameterInfo (parameter, schemaRow, StatementType.Insert, false);
  				parameter.SourceVersion = DataRowVersion.Current;
  				parameter.IsNullable = (bool) schemaRow ["AllowDBNull"];
  
  				columns.Append (GetQuotedString (parameter.SourceColumn));
  				values.Append (parameter.ParameterName);
  
  				parmIndex++;
  			}
  
  			sql = String.Format ("{0}( {1} ) VALUES ( {2} )", command, columns.ToString (), values.ToString ());
  			insertCommand.CommandText = sql;
  			return insertCommand;
  		}
  
  		private void CreateNewCommand (ref 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 ();
  		}
  
  		private SqlCommand CreateUpdateCommand (bool useColumnsForParameterNames)
  		{
  			// 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;
  			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 = null;
  				if (useColumnsForParameterNames) {
  					parameter = CreateParameter (schemaRow, false);
  				} else {
  					parameter = CreateParameter (parmIndex++, schemaRow);
  				}
  				updateCommand.Parameters.Add (parameter);
  				ApplyParameterInfo (parameter, schemaRow, StatementType.Update, false);
  				parameter.IsNullable = (bool) schemaRow ["AllowDBNull"];
  				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;
  
  				bool allowNull = (bool) schemaRow ["AllowDBNull"];
  				if (!isKey) {
  					string sourceColumnName = (string) schemaRow ["BaseColumnName"];
  					if (useColumnsForParameterNames) {
  						parameter = updateCommand.Parameters.Add (
  							GetNullCheckParameterName (sourceColumnName),
  							SqlDbType.Int);
  					} else {
  						parameter = updateCommand.Parameters.Add (
  							GetParameterName (parmIndex++),
  							SqlDbType.Int);
  					}
  					parameter.IsNullable = allowNull;
  					parameter.SourceVersion = DataRowVersion.Current;
  					parameter.Value = 1;
  					
  					whereClause.Append ("(");
  					whereClause.Append (String.Format (clause1, parameter.ParameterName,
  									GetQuotedString (sourceColumnName)));
  					whereClause.Append (" OR ");
  					
  				}
  
  				if (useColumnsForParameterNames) {
  					parameter = CreateParameter (schemaRow, true);
  				} else {
  					parameter = CreateParameter (parmIndex++, schemaRow);
  				}
  				updateCommand.Parameters.Add (parameter);
  				ApplyParameterInfo (parameter, schemaRow, StatementType.Update, true);
  				parameter.IsNullable = allowNull;
  				parameter.SourceVersion = DataRowVersion.Original;
  
  				whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
  
  				if (!isKey)
  					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 (DataRow schemaRow, bool whereClause)
  		{
  			string sourceColumn = (string) schemaRow ["BaseColumnName"];
  			string name;
  			if (whereClause)
  				name = GetParameterName ("Original_" + sourceColumn);
  			else
  				name = GetParameterName (sourceColumn);
  
  			SqlParameter param = new SqlParameter ();
  			param.ParameterName = name;
  			param.SourceColumn = sourceColumn;
  			return param;
  		}
  
  		private SqlParameter CreateParameter (int paramIndex, DataRow schemaRow)
  		{
  			string sourceColumn = (string) schemaRow ["BaseColumnName"];
  			string name = GetParameterName (paramIndex);
  
  			SqlParameter param = new SqlParameter ();
  			param.ParameterName = name;
  			param.SourceColumn = sourceColumn;
  			return param;
  		}
  #endif // ONLY_1_1
  				
  		public static void DeriveParameters (SqlCommand command)
  		{
  			command.DeriveParameters ();
  		}
  
  #if ONLY_1_1
  		protected override void Dispose (bool disposing)
  		{
  			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;
  			}
  		}
  #endif
  
  		public
  #if NET_2_0
  		new
  #endif // NET_2_0
  		SqlCommand GetDeleteCommand ()
  		{
  #if NET_2_0 
  			return (SqlCommand) base.GetDeleteCommand (false);
  #else
  			BuildCache (true);
  			if (deleteCommand == null)
  				return CreateDeleteCommand (false);
  			return deleteCommand;
  #endif
  		}
  
  		public
  #if NET_2_0
  		new
  #endif // NET_2_0
  		SqlCommand GetInsertCommand ()
  		{
  #if NET_2_0
  			return (SqlCommand) base.GetInsertCommand (false);
  #else
  			BuildCache (true);
  			if (insertCommand == null)
  				return CreateInsertCommand (false);
  			return insertCommand;
  #endif
  		}
  
  		public 
  #if NET_2_0
  		new
  #endif // NET_2_0
  		SqlCommand GetUpdateCommand ()
  		{
  #if NET_2_0
  			return (SqlCommand) base.GetUpdateCommand (false);
  #else
  			BuildCache (true);
  			if (updateCommand == null)
  				return CreateUpdateCommand (false);
  			return updateCommand;
  #endif
  		}
  
  #if NET_2_0
  		public new SqlCommand GetUpdateCommand (bool useColumnsForParameterNames)
  		{
  			return (SqlCommand) base.GetUpdateCommand (useColumnsForParameterNames);
  		}
  
  		public new SqlCommand GetDeleteCommand (bool useColumnsForParameterNames)
  		{
  			return (SqlCommand) base.GetDeleteCommand (useColumnsForParameterNames);
  		}
  
  		public new SqlCommand GetInsertCommand (bool useColumnsForParameterNames)
  		{
  			return (SqlCommand) base.GetInsertCommand (useColumnsForParameterNames);
  		}
  		
  		public override string QuoteIdentifier (string unquotedIdentifier)
  		{
  			if (unquotedIdentifier == null)
  				throw new ArgumentNullException ("unquotedIdentifier");
  
  			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)
  		{
  			return base.UnquoteIdentifier (quotedIdentifier);
  		}
  #endif // NET_2_0
  
  		private bool IncludedInInsert (DataRow schemaRow)
  		{
  			// If the parameter has one of these properties, then we don't include it in the insert:
  			// AutoIncrement, Hidden, Expression, RowVersion, ReadOnly
  
  			if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
  				return false;
  			if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
  				return false;
  			if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
  				return false;
  			if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
  				return false;
  			if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
  				return false;
  			return true;
  		}
  
  		private bool IncludedInUpdate (DataRow schemaRow)
  		{
  			// If the parameter has one of these properties, then we don't include it in the insert:
  			// AutoIncrement, Hidden, RowVersion
  
  			if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
  				return false;
  			if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
  				return false;
  			if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
  				return false;
  			if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
  				return false;
  			if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
  				return false;
  
  			return true;
  		}
  
  		private bool IncludedInWhereClause (DataRow schemaRow)
  		{
  			if ((bool) schemaRow ["IsLong"])
  				return false;
  			return true;
  		}
  
  #if ONLY_1_1				
  		private string GetQuotedString (string value)
  		{
  			if (value == null || value.Length == 0)
  				return value;
  
  			string prefix = QuotePrefix;
  			string suffix = QuoteSuffix;
  
  			if (prefix.Length == 0 && suffix.Length == 0)
  				return value;
  			return String.Format ("{0}{1}{2}", prefix, value, suffix);
  		}
  
  		string GetNullCheckParameterName (string parameterName)
  		{
  			return GetParameterName ("IsNull_" + parameterName);
  		}
  
  
  		private string QuotedTableName {
  			get { return GetQuotedString (tableName); }
  		}
  
  		public void RefreshSchema ()
  		{
  			// FIXME: "Figure out what else needs to be cleaned up when we refresh."
  			tableName = String.Empty;
  			dbSchemaTable = null;
  			deleteCommand = null;
  			insertCommand = null;
  			updateCommand = null;
  		}
  #endif
  
  #if NET_2_0
  		protected override void ApplyParameterInfo (DbParameter parameter,
  		                                            DataRow datarow,
  		                                            StatementType statementType,
  		                                            bool whereClause)
  		{
  			SqlParameter sqlParam = (SqlParameter) parameter;
  #else
  		void ApplyParameterInfo (SqlParameter sqlParam,
  		                         DataRow datarow,
  		                         StatementType statementType,
  		                         bool whereClause)
  		{
  #endif
  			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;
  			}
  		}
  
  #if NET_2_0
  		protected override
  #endif
  		string GetParameterName (int parameterOrdinal)
  		{
  			return String.Format ("@p{0}",  parameterOrdinal);
  		}
  
  #if NET_2_0
  		protected override
  #endif
  		string GetParameterName (string parameterName)
  		{
  			return String.Format ("@{0}", parameterName);
  		}
  
  #if NET_2_0
  		protected override string GetParameterPlaceholder (int parameterOrdinal)
  		{
  			return GetParameterName (parameterOrdinal);
  		}
  #endif
  
  		#endregion // Methods
  
  		#region Event Handlers
  
  		void RowUpdatingHandler (object sender, SqlRowUpdatingEventArgs args)
  		{
  #if NET_2_0
  			base.RowUpdatingHandler (args);
  #else
  			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;
  				}
  			} catch (Exception e) {
  				args.Errors = e;
  				args.Status = UpdateStatus.ErrorsOccurred;
  			}
  #endif
  		}
  
  #if NET_2_0
  		protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
  		{
  				SqlDataAdapter sda = adapter as SqlDataAdapter;
  
gt: 				if (sda == null) {