//
// Author:
// Umadevi S (sumadevi@novell.com)
+// Sureshkumar T (tsureshkumar@novell.com)
//
// Copyright (C) Novell Inc, 2004
//
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System.ComponentModel;
+using System.Text;
using System.Data;
using System.Data.Common;
+using System.ComponentModel;
namespace System.Data.Odbc
{
/// Provides a means of automatically generating single-table commands used to reconcile changes made to a DataSet with the associated database. This class cannot be inherited.
/// </summary>
-#if ONLY_1_1
- public sealed class OdbcCommandBuilder : Component
-#else // NET_2_0 and higher
+#if NET_2_0
public sealed class OdbcCommandBuilder : DbCommandBuilder
-#endif // ONLY_1_1
+#else // 1_1
+ public sealed class OdbcCommandBuilder : Component
+#endif // NET_2_0
{
#region Fields
- OdbcDataAdapter adapter;
- string quotePrefix;
- string quoteSuffix;
+ private OdbcDataAdapter _adapter;
+ private string _quotePrefix;
+ private string _quoteSuffix;
+
+ private DataTable _schema;
+ private string _tableName;
+ private OdbcCommand _insertCommand;
+ private OdbcCommand _updateCommand;
+ private OdbcCommand _deleteCommand;
+
+ bool _disposed;
#endregion // Fields
public OdbcCommandBuilder ()
{
- adapter = null;
- quotePrefix = String.Empty;
- quoteSuffix = String.Empty;
+ _adapter = null;
+ _quotePrefix = String.Empty;
+ _quoteSuffix = String.Empty;
}
public OdbcCommandBuilder (OdbcDataAdapter adapter)
: this ()
{
- this.adapter = adapter;
+ DataAdapter = adapter;
}
#endregion // Constructors
#endif // NET_2_0
OdbcDataAdapter DataAdapter {
get {
- return adapter;
+ return _adapter;
}
set {
- adapter = value;
+ if (_adapter == value)
+ return;
+
+ if (_adapter != null)
+ _adapter.RowUpdating -= new OdbcRowUpdatingEventHandler (OnRowUpdating);
+ _adapter = value;
+ if (_adapter != null)
+ _adapter.RowUpdating += new OdbcRowUpdatingEventHandler (OnRowUpdating);
+
+ }
+ }
+
+ private OdbcCommand SelectCommand
+ {
+ get {
+ if (DataAdapter == null)
+ return null;
+ return DataAdapter.SelectCommand;
+ }
+ }
+
+ private DataTable Schema
+ {
+ get {
+ if (_schema == null)
+ RefreshSchema ();
+ return _schema;
+ }
+ }
+
+ private string TableName
+ {
+ get {
+ if (_tableName != String.Empty)
+ return _tableName;
+
+ DataRow [] schemaRows = Schema.Select ("BaseTableName is not null and BaseTableName <> ''");
+ if (schemaRows.Length > 1) {
+ string tableName = (string) schemaRows [0] ["BaseTableName"];
+ foreach (DataRow schemaRow in schemaRows) {
+ if ( (string) schemaRow ["BaseTableName"] != tableName)
+ throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
+ }
+ }
+ if (schemaRows.Length == 0)
+ throw new InvalidOperationException ("Cannot determine the base table name. Cannot proceed");
+ _tableName = schemaRows [0] ["BaseTableName"].ToString ();
+ return _tableName;
}
}
#endif // NET_2_0
string QuotePrefix {
get {
- return quotePrefix;
+ return _quotePrefix;
}
set {
- quotePrefix = value;
+ _quotePrefix = value;
}
}
#endif // NET_2_0
string QuoteSuffix {
get {
- return quoteSuffix;
+ return _quoteSuffix;
}
set {
- quoteSuffix = value;
+ _quoteSuffix = value;
}
}
#region Methods
+ [MonoTODO]
public static void DeriveParameters (OdbcCommand command)
{
throw new NotImplementedException ();
}
- [MonoTODO]
protected override void Dispose (bool disposing)
{
- throw new NotImplementedException ();
+ if (_disposed)
+ return;
+
+ if (disposing) {
+ // dispose managed resource
+ if (_insertCommand != null) _insertCommand.Dispose ();
+ if (_updateCommand != null) _updateCommand.Dispose ();
+ if (_deleteCommand != null) _deleteCommand.Dispose ();
+ if (_schema != null) _insertCommand.Dispose ();
+
+ _insertCommand = null;
+ _updateCommand = null;
+ _deleteCommand = null;
+ _schema = null;
+ }
+ _disposed = true;
+ }
+
+ private bool IsUpdatable (DataRow schemaRow)
+ {
+ if ( (! schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
+ || (! schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
+ || (! schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
+ || (! schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
+ || (! schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
+ )
+ return false;
+ return true;
+ }
+
+ private string GetColumnName (DataRow schemaRow)
+ {
+ string columnName = schemaRow.IsNull ("BaseColumnName") ? String.Empty : (string) schemaRow ["BaseColumnName"];
+ if (columnName == String.Empty)
+ columnName = schemaRow.IsNull ("ColumnName") ? String.Empty : (string) schemaRow ["ColumnName"];
+ return columnName;
+ }
+
+ private OdbcParameter AddParameter (OdbcCommand cmd, string paramName, OdbcType odbcType,
+ int length, string sourceColumnName, DataRowVersion rowVersion)
+ {
+ OdbcParameter param;
+ if (length >= 0 && sourceColumnName != String.Empty)
+ param = cmd.Parameters.Add (paramName, odbcType, length, sourceColumnName);
+ else
+ param = cmd.Parameters.Add (paramName, odbcType);
+ param.SourceVersion = rowVersion;
+ return param;
+ }
+
+ /*
+ * creates where clause for optimistic concurrency
+ */
+ private string CreateOptWhereClause (OdbcCommand command)
+ {
+ string [] whereClause = new string [Schema.Rows.Count];
+
+ int count = 0;
+
+ foreach (DataRow schemaRow in Schema.Rows) {
+
+ // exclude non updatable columns
+ if (! IsUpdatable (schemaRow))
+ continue;
+
+ string columnName = GetColumnName (schemaRow);
+ if (columnName == String.Empty)
+ throw new InvalidOperationException ("Cannot form delete command. Column name is missing!");
+
+ bool allowNull = schemaRow.IsNull ("AllowDBNull") || (bool) schemaRow ["AllowDBNull"];
+ OdbcType sqlDbType = schemaRow.IsNull ("ProviderType") ? OdbcType.VarChar : (OdbcType) schemaRow ["ProviderType"];
+ int length = schemaRow.IsNull ("ColumnSize") ? -1 : (int) schemaRow ["ColumnSize"];
+
+ if (allowNull) {
+ whereClause [count] = String.Format ("((? = 1 AND {0} IS NULL) OR ({0} = ?))",
+ columnName);
+ AddParameter (command, columnName, sqlDbType, length, columnName, DataRowVersion.Original);
+ AddParameter (command, columnName, sqlDbType, length, columnName, DataRowVersion.Original);
+ } else {
+ whereClause [count] = String.Format ( "({0} = ?)", columnName);
+ AddParameter (command, columnName, sqlDbType, length, columnName, DataRowVersion.Original);
+ }
+
+ count++;
+ }
+
+ return String.Join (" AND ", whereClause, 0, count);
}
- [MonoTODO]
public
#if NET_2_0
new
#endif // NET_2_0
- OdbcCommand GetDeleteCommand ()
+ OdbcCommand GetInsertCommand ()
{
- throw new NotImplementedException ();
+ // FIXME: check validity of adapter
+ if (_insertCommand != null)
+ return _insertCommand;
+
+ if (_schema == null)
+ RefreshSchema ();
+
+ _insertCommand = new OdbcCommand ();
+ _insertCommand.Connection = DataAdapter.SelectCommand.Connection;
+ _insertCommand.Transaction = DataAdapter.SelectCommand.Transaction;
+ _insertCommand.CommandType = CommandType.Text;
+ _insertCommand.UpdatedRowSource = UpdateRowSource.None;
+
+ string query = String.Format ("INSERT INTO {0}", QuoteIdentifier (TableName));
+ string [] columns = new string [Schema.Rows.Count];
+ string [] values = new string [Schema.Rows.Count];
+
+ int count = 0;
+
+ foreach (DataRow schemaRow in Schema.Rows) {
+
+ // exclude non updatable columns
+ if (! IsUpdatable (schemaRow))
+ continue;
+
+ string columnName = GetColumnName (schemaRow);
+ if (columnName == String.Empty)
+ throw new InvalidOperationException ("Cannot form insert command. Column name is missing!");
+
+ // create column string & value string
+ columns [count] = QuoteIdentifier(columnName);
+ values [count++] = "?";
+
+ // create parameter and add
+ OdbcType sqlDbType = schemaRow.IsNull ("ProviderType") ? OdbcType.VarChar : (OdbcType) schemaRow ["ProviderType"];
+ int length = schemaRow.IsNull ("ColumnSize") ? -1 : (int) schemaRow ["ColumnSize"];
+
+ AddParameter (_insertCommand, columnName, sqlDbType, length, columnName, DataRowVersion.Current);
+ }
+
+ query = String.Format ("{0} ({1}) VALUES ({2})",
+ query,
+ String.Join (", ", columns, 0, count),
+ String.Join (", ", values, 0, count) );
+ _insertCommand.CommandText = query;
+ return _insertCommand;
}
+#if NET_2_0
[MonoTODO]
+ public new OdbcCommand GetInsertCommand (bool option)
+ {
+ // FIXME: check validity of adapter
+ if (_insertCommand != null)
+ return _insertCommand;
+
+ if (_schema == null)
+ RefreshSchema ();
+
+ if (option == false) {
+ return GetInsertCommand ();
+ } else {
+ throw new NotImplementedException ();
+ }
+ }
+#endif // NET_2_0
+
public
#if NET_2_0
new
#endif // NET_2_0
- OdbcCommand GetInsertCommand ()
+ OdbcCommand GetUpdateCommand ()
{
- throw new NotImplementedException ();
+ // FIXME: check validity of adapter
+ if (_updateCommand != null)
+ return _updateCommand;
+
+ if (_schema == null)
+ RefreshSchema ();
+
+ _updateCommand = new OdbcCommand ();
+ _updateCommand.Connection = DataAdapter.SelectCommand.Connection;
+ _updateCommand.Transaction = DataAdapter.SelectCommand.Transaction;
+ _updateCommand.CommandType = CommandType.Text;
+ _updateCommand.UpdatedRowSource = UpdateRowSource.None;
+
+ string query = String.Format ("UPDATE {0} SET", QuoteIdentifier (TableName));
+ string [] setClause = new string [Schema.Rows.Count];
+
+ int count = 0;
+
+ foreach (DataRow schemaRow in Schema.Rows) {
+
+ // exclude non updatable columns
+ if (! IsUpdatable (schemaRow))
+ continue;
+
+ string columnName = GetColumnName (schemaRow);
+ if (columnName == String.Empty)
+ throw new InvalidOperationException ("Cannot form update command. Column name is missing!");
+
+ OdbcType sqlDbType = schemaRow.IsNull ("ProviderType") ? OdbcType.VarChar : (OdbcType) schemaRow ["ProviderType"];
+ int length = schemaRow.IsNull ("ColumnSize") ? -1 : (int) schemaRow ["ColumnSize"];
+
+ // create column = value string
+ setClause [count] = String.Format ("{0} = ?", QuoteIdentifier(columnName));
+ AddParameter (_updateCommand, columnName, sqlDbType, length, columnName, DataRowVersion.Current);
+ count++;
+ }
+
+ // create where clause. odbc uses positional parameters. so where class
+ // is created seperate from the above loop.
+ string whereClause = CreateOptWhereClause (_updateCommand);
+
+ query = String.Format ("{0} {1} WHERE ({2})",
+ query,
+ String.Join (", ", setClause, 0, count),
+ whereClause);
+ _updateCommand.CommandText = query;
+ return _updateCommand;
}
+#if NET_2_0
[MonoTODO]
+ public new OdbcCommand GetUpdateCommand (bool option)
+ {
+ // FIXME: check validity of adapter
+ if (_updateCommand != null)
+ return _updateCommand;
+
+ if (_schema == null)
+ RefreshSchema ();
+
+ if (option == false) {
+ return GetUpdateCommand ();
+ } else {
+ throw new NotImplementedException ();
+ }
+ }
+#endif // NET_2_0
+
public
#if NET_2_0
new
#endif // NET_2_0
- OdbcCommand GetUpdateCommand ()
+ OdbcCommand GetDeleteCommand ()
{
- throw new NotImplementedException ();
+ // FIXME: check validity of adapter
+ if (_deleteCommand != null)
+ return _deleteCommand;
+
+ if (_schema == null)
+ RefreshSchema ();
+
+ _deleteCommand = new OdbcCommand ();
+ _deleteCommand.Connection = DataAdapter.SelectCommand.Connection;
+ _deleteCommand.Transaction = DataAdapter.SelectCommand.Transaction;
+ _deleteCommand.CommandType = CommandType.Text;
+ _deleteCommand.UpdatedRowSource = UpdateRowSource.None;
+
+ string query = String.Format ("DELETE FROM {0}", QuoteIdentifier (TableName));
+ string whereClause = CreateOptWhereClause (_deleteCommand);
+
+ query = String.Format ("{0} WHERE ({1})", query, whereClause);
+ _deleteCommand.CommandText = query;
+ return _deleteCommand;
}
+#if NET_2_0
[MonoTODO]
+ public new OdbcCommand GetDeleteCommand (bool option)
+ {
+ // FIXME: check validity of adapter
+ if (_deleteCommand != null)
+ return _deleteCommand;
+
+ if (_schema == null)
+ RefreshSchema ();
+
+ if (option == false) {
+ return GetDeleteCommand ();
+ } else {
+ throw new NotImplementedException ();
+ }
+ }
+#endif // NET_2_0
+
public
#if NET_2_0
override
#endif // NET_2_0
void RefreshSchema ()
{
- throw new NotImplementedException ();
+ // creates metadata
+ if (SelectCommand == null)
+ throw new InvalidOperationException ("SelectCommand should be valid");
+ if (SelectCommand.Connection == null)
+ throw new InvalidOperationException ("SelectCommand's Connection should be valid");
+
+ CommandBehavior behavior = CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
+ if (SelectCommand.Connection.State != ConnectionState.Open) {
+ SelectCommand.Connection.Open ();
+ behavior |= CommandBehavior.CloseConnection;
+ }
+
+ OdbcDataReader reader = SelectCommand.ExecuteReader (behavior);
+ _schema = reader.GetSchemaTable ();
+ reader.Close ();
+
+ // force creation of commands
+ _insertCommand = null;
+ _updateCommand = null;
+ _deleteCommand = null;
+ _tableName = String.Empty;
}
#if NET_2_0
[MonoTODO]
- protected override void ApplyParameterInfo (IDbDataParameter dbParameter, DataRow row)
+ protected override void ApplyParameterInfo (DbParameter dbParameter,
+ DataRow row,
+ StatementType statementType,
+ bool whereClause)
{
throw new NotImplementedException ();
}
{
throw new NotImplementedException ();
}
-
[MonoTODO]
- protected override string GetParameterPlaceholder (int position)
+ protected override string GetParameterName (string parameterName)
{
throw new NotImplementedException ();
}
[MonoTODO]
- protected override DbProviderFactory ProviderFactory
+ protected override string GetParameterPlaceholder (int position)
{
- get {throw new NotImplementedException ();}
+ throw new NotImplementedException ();
}
-
+
[MonoTODO]
protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
{
}
#endif // NET_2_0
+
+#if NET_2_0
+ [MonoTODO]
+ public override
+#else
+ private
+#endif
+ string QuoteIdentifier (string unquotedIdentifier)
+ {
+ /*
+#if NET_2_0
+ throw new NotImplementedException ();
+#else
+ */
+ if (unquotedIdentifier == null || unquotedIdentifier == String.Empty)
+ return unquotedIdentifier;
+ return String.Format ("{0}{1}{2}", QuotePrefix,
+ unquotedIdentifier, QuoteSuffix);
+ /*
+#endif
+ */
+ }
+
+#if NET_2_0
+ [MonoTODO]
+ public override
+#else
+ private
+#endif
+ string UnquoteIdentifier (string quotedIdentifier)
+ {
+#if NET_2_0
+ throw new NotImplementedException ();
+#else
+ if (quotedIdentifier == null || quotedIdentifier == String.Empty)
+ return quotedIdentifier;
+
+ StringBuilder sb = new StringBuilder (quotedIdentifier.Length);
+ sb.Append (quotedIdentifier);
+ if (quotedIdentifier.StartsWith (QuotePrefix))
+ sb.Remove (0,QuotePrefix.Length);
+ if (quotedIdentifier.EndsWith (QuoteSuffix))
+ sb.Remove (sb.Length - QuoteSuffix.Length, QuoteSuffix.Length );
+ return sb.ToString ();
+#endif
+ }
+ private void OnRowUpdating (object sender, OdbcRowUpdatingEventArgs args)
+ {
+ 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;
+ }
+ }
+
#endregion // Methods
}