1 // ByteFX.Data data access components for .Net
\r
2 // Copyright (C) 2002-2003 ByteFX, Inc.
\r
4 // This library is free software; you can redistribute it and/or
\r
5 // modify it under the terms of the GNU Lesser General Public
\r
6 // License as published by the Free Software Foundation; either
\r
7 // version 2.1 of the License, or (at your option) any later version.
\r
9 // This library is distributed in the hope that it will be useful,
\r
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
12 // Lesser General Public License for more details.
\r
14 // You should have received a copy of the GNU Lesser General Public
\r
15 // License along with this library; if not, write to the Free Software
\r
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
19 using System.ComponentModel;
\r
23 namespace ByteFX.Data.MySQLClient
\r
26 /// Summary description for CommandBuilder.
\r
28 [ToolboxItem(false)]
\r
29 public sealed class MySQLCommandBuilder : Component
\r
31 private MySQLDataAdapter _adapter;
\r
32 private string _QuotePrefix;
\r
33 private string _QuoteSuffix;
\r
34 private DataTable _schema;
\r
35 private string _tableName;
\r
37 private MySQLCommand _updateCmd;
\r
38 private MySQLCommand _insertCmd;
\r
39 private MySQLCommand _deleteCmd;
\r
41 #region Constructors
\r
42 public MySQLCommandBuilder()
\r
46 public MySQLCommandBuilder( MySQLDataAdapter adapter )
\r
49 _adapter.RowUpdating += new MySQLRowUpdatingEventHandler( OnRowUpdating );
\r
54 public MySQLDataAdapter DataAdapter
\r
56 get { return _adapter; }
\r
59 if (_adapter != null)
\r
61 _adapter.RowUpdating -= new MySQLRowUpdatingEventHandler( OnRowUpdating );
\r
67 public string QuotePrefix
\r
69 get { return _QuotePrefix; }
\r
70 set { _QuotePrefix = value; }
\r
73 public string QuoteSuffix
\r
75 get { return _QuoteSuffix; }
\r
76 set { _QuoteSuffix = value; }
\r
81 #region Public Methods
\r
82 public static void DeriveParameters(MySQLCommand command)
\r
84 throw new MySQLException("DeriveParameters is not supported (due to MySQL not supporting SP)");
\r
87 public MySQLCommand GetDeleteCommand()
\r
89 if (_schema == null)
\r
91 return CreateDeleteCommand();
\r
94 public MySQLCommand GetInsertCommand()
\r
96 if (_schema == null)
\r
98 return CreateInsertCommand();
\r
101 public MySQLCommand GetUpdateCommand()
\r
103 if (_schema == null)
\r
105 return CreateUpdateCommand();
\r
108 public void RefreshSchema()
\r
117 #region Private Methods
\r
119 private void GenerateSchema()
\r
121 if (_adapter == null)
\r
122 throw new MySQLException("Improper MySQLCommandBuilder state: adapter is null");
\r
123 if (_adapter.SelectCommand == null)
\r
124 throw new MySQLException("Improper MySQLCommandBuilder state: adapter's SelectCommand is null");
\r
126 MySQLDataReader dr = _adapter.SelectCommand.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
\r
127 _schema = dr.GetSchemaTable();
\r
130 // make sure we got at least one unique or key field and count base table names
\r
131 bool hasKeyOrUnique=false;
\r
133 foreach (DataRow row in _schema.Rows)
\r
135 if (true == (bool)row["IsKey"] || true == (bool)row["IsUnique"])
\r
136 hasKeyOrUnique=true;
\r
137 if (_tableName == null)
\r
138 _tableName = (string)row["BaseTableName"];
\r
139 else if (_tableName != (string)row["BaseTableName"])
\r
140 throw new InvalidOperationException("MySQLCommandBuilder does not support multi-table statements");
\r
142 if (! hasKeyOrUnique)
\r
143 throw new InvalidOperationException("MySQLCommandBuilder cannot operate on tables with no unique or key columns");
\r
146 private string Quote(string table_or_column)
\r
148 if (_QuotePrefix == null || _QuoteSuffix == null)
\r
149 return table_or_column;
\r
150 return _QuotePrefix + table_or_column + _QuoteSuffix;
\r
153 private MySQLParameter CreateParameter(DataRow row, bool Original)
\r
157 p = new MySQLParameter( "@Original_" + (string)row["ColumnName"], (MySQLDbType)row["ProviderType"],
\r
158 ParameterDirection.Input, (string)row["ColumnName"], DataRowVersion.Original, null );
\r
160 p = new MySQLParameter( "@" + (string)row["ColumnName"], (MySQLDbType)row["ProviderType"],
\r
161 (string)row["ColumnName"]);
\r
165 private MySQLCommand CreateBaseCommand()
\r
167 MySQLCommand cmd = new MySQLCommand();
\r
168 cmd.Connection = _adapter.SelectCommand.Connection;
\r
169 cmd.CommandTimeout = _adapter.SelectCommand.CommandTimeout;
\r
170 cmd.Transaction = _adapter.SelectCommand.Transaction;
\r
174 private MySQLCommand CreateDeleteCommand()
\r
176 if (_deleteCmd != null) return _deleteCmd;
\r
178 MySQLCommand cmd = CreateBaseCommand();
\r
180 cmd.CommandText = "DELETE FROM " + Quote(_tableName) +
\r
181 " WHERE " + CreateOriginalWhere(cmd);
\r
187 private string CreateFinalSelect(bool forinsert)
\r
189 StringBuilder sel = new StringBuilder();
\r
190 StringBuilder where = new StringBuilder();
\r
192 foreach (DataRow row in _schema.Rows)
\r
194 string colname = (string)row["ColumnName"];
\r
195 if (sel.Length > 0)
\r
197 sel.Append( colname );
\r
198 if ((bool)row["IsKey"] == false) continue;
\r
199 if (where.Length > 0)
\r
200 where.Append(" AND ");
\r
201 where.Append( "(" + colname + "=" );
\r
202 if ((bool)row["IsAutoIncrement"] && forinsert)
\r
203 where.Append("last_insert_id()");
\r
205 where.Append("@Original_" + colname);
\r
208 return "SELECT " + sel.ToString() + " FROM " + Quote(_tableName) +
\r
209 " WHERE " + where.ToString();
\r
212 private string CreateOriginalWhere(MySQLCommand cmd)
\r
214 StringBuilder wherestr = new StringBuilder();
\r
216 foreach (DataRow row in _schema.Rows)
\r
218 if (! IncludedInWhereClause(row)) continue;
\r
220 // first update the where clause since it will contain all parameters
\r
221 if (wherestr.Length > 0)
\r
222 wherestr.Append(" AND ");
\r
223 string colname = Quote((string)row["ColumnName"]);
\r
225 MySQLParameter op = CreateParameter(row, true);
\r
226 cmd.Parameters.Add(op);
\r
228 wherestr.Append( "(" + colname + "=" + op.ParameterName);
\r
229 if ((bool)row["AllowDBNull"] == true)
\r
230 wherestr.Append( " or " + colname + " is null and " + op.ParameterName + " is null");
\r
231 wherestr.Append(")");
\r
233 return wherestr.ToString();
\r
236 private MySQLCommand CreateUpdateCommand()
\r
238 if (_updateCmd != null) return _updateCmd;
\r
240 MySQLCommand cmd = CreateBaseCommand();
\r
242 StringBuilder setstr = new StringBuilder();
\r
244 foreach (DataRow schemaRow in _schema.Rows)
\r
246 string colname = Quote((string)schemaRow["ColumnName"]);
\r
248 if (! IncludedInUpdate(schemaRow)) continue;
\r
250 if (setstr.Length > 0)
\r
251 setstr.Append(", ");
\r
253 MySQLParameter p = CreateParameter(schemaRow, false);
\r
254 cmd.Parameters.Add(p);
\r
256 setstr.Append( colname + "=" + p.ParameterName );
\r
259 cmd.CommandText = "UPDATE " + Quote(_tableName) + " SET " + setstr.ToString() +
\r
260 " WHERE " + CreateOriginalWhere(cmd);
\r
261 cmd.CommandText += "; " + CreateFinalSelect(false);
\r
267 private MySQLCommand CreateInsertCommand()
\r
269 if (_insertCmd != null) return _insertCmd;
\r
271 MySQLCommand cmd = CreateBaseCommand();
\r
273 StringBuilder setstr = new StringBuilder();
\r
274 StringBuilder valstr = new StringBuilder();
\r
275 foreach (DataRow schemaRow in _schema.Rows)
\r
277 string colname = Quote((string)schemaRow["ColumnName"]);
\r
279 if (!IncludedInInsert(schemaRow)) continue;
\r
281 if (setstr.Length > 0)
\r
283 setstr.Append(", ");
\r
284 valstr.Append(", ");
\r
287 MySQLParameter p = CreateParameter(schemaRow, false);
\r
288 cmd.Parameters.Add(p);
\r
290 setstr.Append( colname );
\r
291 valstr.Append( p.ParameterName );
\r
294 cmd.CommandText = "INSERT INTO " + Quote(_tableName) + " (" + setstr.ToString() + ") " +
\r
295 " VALUES (" + valstr.ToString() + ")";
\r
296 cmd.CommandText += "; " + CreateFinalSelect(true);
\r
302 private bool IncludedInInsert (DataRow schemaRow)
\r
304 // If the parameter has one of these properties, then we don't include it in the insert:
\r
305 // AutoIncrement, Hidden, Expression, RowVersion, ReadOnly
\r
307 if ((bool) schemaRow ["IsAutoIncrement"])
\r
309 /* if ((bool) schemaRow ["IsHidden"])
\r
311 if ((bool) schemaRow ["IsExpression"])
\r
313 if ((bool) schemaRow ["IsRowVersion"])
\r
315 if ((bool) schemaRow ["IsReadOnly"])
\r
320 private bool IncludedInUpdate (DataRow schemaRow)
\r
322 // If the parameter has one of these properties, then we don't include it in the insert:
\r
323 // AutoIncrement, Hidden, RowVersion
\r
325 if ((bool) schemaRow ["IsAutoIncrement"])
\r
327 // if ((bool) schemaRow ["IsHidden"])
\r
329 if ((bool) schemaRow ["IsRowVersion"])
\r
334 private bool IncludedInWhereClause (DataRow schemaRow)
\r
336 if ((bool) schemaRow ["IsLong"])
\r
341 private void SetParameterValues(MySQLCommand cmd, DataRow dataRow)
\r
343 foreach (MySQLParameter p in cmd.Parameters)
\r
345 if (p.ParameterName.Length >= 9 && p.ParameterName.Substring(0, 9).Equals("@Original"))
\r
346 p.Value = dataRow[ p.SourceColumn, DataRowVersion.Original ];
\r
348 p.Value = dataRow[ p.SourceColumn, DataRowVersion.Current ];
\r
352 private void OnRowUpdating(object sender, MySQLRowUpdatingEventArgs args)
\r
354 // make sure we are still to proceed
\r
355 if (args.Status != UpdateStatus.Continue) return;
\r
357 if (_schema == null)
\r
360 if (StatementType.Delete == args.StatementType)
\r
361 args.Command = CreateDeleteCommand();
\r
362 else if (StatementType.Update == args.StatementType)
\r
363 args.Command = CreateUpdateCommand();
\r
364 else if (StatementType.Insert == args.StatementType)
\r
365 args.Command = CreateInsertCommand();
\r
366 else if (StatementType.Select == args.StatementType)
\r
369 SetParameterValues(args.Command, args.Row);
\r