1 // ByteFX.Data data access components for .Net
2 // Copyright (C) 2002-2003 ByteFX, Inc.
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 using System.ComponentModel;
23 namespace ByteFX.Data.MySqlClient
26 /// Summary description for CommandBuilder.
29 [System.ComponentModel.DesignerCategory("Code")]
30 public sealed class MySqlCommandBuilder : Component
32 private MySqlDataAdapter _adapter;
33 private string _QuotePrefix;
34 private string _QuoteSuffix;
35 private DataTable _schema;
36 private string _tableName;
38 private MySqlCommand _updateCmd;
39 private MySqlCommand _insertCmd;
40 private MySqlCommand _deleteCmd;
43 public MySqlCommandBuilder()
47 public MySqlCommandBuilder( MySqlDataAdapter adapter )
50 _adapter.RowUpdating += new MySqlRowUpdatingEventHandler( OnRowUpdating );
57 /// Gets or sets a MySqlDataAdapter object for which SQL statements are automatically generated.
59 public MySqlDataAdapter DataAdapter
61 get { return _adapter; }
66 _adapter.RowUpdating -= new MySqlRowUpdatingEventHandler( OnRowUpdating );
73 /// Gets or sets the beginning character or characters to use when specifying MySql database objects (for example, tables or columns) whose names contain characters such as spaces or reserved tokens.
75 public string QuotePrefix
77 get { return _QuotePrefix; }
78 set { _QuotePrefix = value; }
82 /// Gets or sets the ending character or characters to use when specifying MySql database objects (for example, tables or columns) whose names contain characters such as spaces or reserved tokens.
84 public string QuoteSuffix
86 get { return _QuoteSuffix; }
87 set { _QuoteSuffix = value; }
92 #region Public Methods
93 public static void DeriveParameters(MySqlCommand command)
95 throw new MySqlException("DeriveParameters is not supported (due to MySql not supporting SP)");
99 /// Gets the automatically generated MySqlCommand object required to perform deletions on the database.
101 /// <returns></returns>
102 public MySqlCommand GetDeleteCommand()
106 return CreateDeleteCommand();
110 /// Gets the automatically generated MySqlCommand object required to perform insertions on the database.
112 /// <returns></returns>
113 public MySqlCommand GetInsertCommand()
117 return CreateInsertCommand();
121 /// Gets the automatically generated MySqlCommand object required to perform updates on the database.
123 /// <returns></returns>
124 public MySqlCommand GetUpdateCommand()
128 return CreateUpdateCommand();
132 /// Refreshes the database schema information used to generate INSERT, UPDATE, or DELETE statements.
134 public void RefreshSchema()
143 #region Private Methods
145 private void GenerateSchema()
147 if (_adapter == null)
148 throw new MySqlException("Improper MySqlCommandBuilder state: adapter is null");
149 if (_adapter.SelectCommand == null)
150 throw new MySqlException("Improper MySqlCommandBuilder state: adapter's SelectCommand is null");
152 MySqlDataReader dr = _adapter.SelectCommand.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
153 _schema = dr.GetSchemaTable();
156 // make sure we got at least one unique or key field and count base table names
157 bool hasKeyOrUnique=false;
159 foreach (DataRow row in _schema.Rows)
161 if (true == (bool)row["IsKey"] || true == (bool)row["IsUnique"])
163 if (_tableName == null)
164 _tableName = (string)row["BaseTableName"];
165 else if (_tableName != (string)row["BaseTableName"])
166 throw new InvalidOperationException("MySqlCommandBuilder does not support multi-table statements");
168 if (! hasKeyOrUnique)
169 throw new InvalidOperationException("MySqlCommandBuilder cannot operate on tables with no unique or key columns");
172 private string Quote(string table_or_column)
174 if (_QuotePrefix == null || _QuoteSuffix == null)
175 return table_or_column;
176 return _QuotePrefix + table_or_column + _QuoteSuffix;
179 private MySqlParameter CreateParameter(DataRow row, bool Original)
183 p = new MySqlParameter( "@Original_" + (string)row["ColumnName"], (MySqlDbType)row["ProviderType"],
184 ParameterDirection.Input, (string)row["ColumnName"], DataRowVersion.Original, DBNull.Value );
186 p = new MySqlParameter( "@" + (string)row["ColumnName"], (MySqlDbType)row["ProviderType"],
187 ParameterDirection.Input, (string)row["ColumnName"], DataRowVersion.Current, DBNull.Value );
191 private MySqlCommand CreateBaseCommand()
193 MySqlCommand cmd = new MySqlCommand();
194 cmd.Connection = _adapter.SelectCommand.Connection;
195 cmd.CommandTimeout = _adapter.SelectCommand.CommandTimeout;
196 cmd.Transaction = _adapter.SelectCommand.Transaction;
200 private MySqlCommand CreateDeleteCommand()
202 if (_deleteCmd != null) return _deleteCmd;
204 MySqlCommand cmd = CreateBaseCommand();
206 cmd.CommandText = "DELETE FROM " + Quote(_tableName) +
207 " WHERE " + CreateOriginalWhere(cmd);
213 private string CreateFinalSelect(bool forinsert)
215 StringBuilder sel = new StringBuilder();
216 StringBuilder where = new StringBuilder();
218 foreach (DataRow row in _schema.Rows)
220 string colname = (string)row["ColumnName"];
223 sel.Append( colname );
224 if ((bool)row["IsKey"] == false) continue;
225 if (where.Length > 0)
226 where.Append(" AND ");
227 where.Append( "(" + colname + "=" );
230 if ((bool)row["IsAutoIncrement"])
231 where.Append("last_insert_id()");
232 else if ((bool)row["IsKey"])
233 where.Append("@" + colname);
237 where.Append("@Original_" + colname);
241 return "SELECT " + sel.ToString() + " FROM " + Quote(_tableName) +
242 " WHERE " + where.ToString();
245 private string CreateOriginalWhere(MySqlCommand cmd)
247 StringBuilder wherestr = new StringBuilder();
249 foreach (DataRow row in _schema.Rows)
251 if (! IncludedInWhereClause(row)) continue;
253 // first update the where clause since it will contain all parameters
254 if (wherestr.Length > 0)
255 wherestr.Append(" AND ");
256 string colname = Quote((string)row["ColumnName"]);
258 MySqlParameter op = CreateParameter(row, true);
259 cmd.Parameters.Add(op);
261 wherestr.Append( "(" + colname + "=@" + op.ParameterName);
262 if ((bool)row["AllowDBNull"] == true)
263 wherestr.Append( " or (" + colname + " IS NULL and @" + op.ParameterName + " IS NULL)");
264 wherestr.Append(")");
266 return wherestr.ToString();
269 private MySqlCommand CreateUpdateCommand()
271 if (_updateCmd != null) return _updateCmd;
273 MySqlCommand cmd = CreateBaseCommand();
275 StringBuilder setstr = new StringBuilder();
277 foreach (DataRow schemaRow in _schema.Rows)
279 string colname = Quote((string)schemaRow["ColumnName"]);
281 if (! IncludedInUpdate(schemaRow)) continue;
283 if (setstr.Length > 0)
286 MySqlParameter p = CreateParameter(schemaRow, false);
287 cmd.Parameters.Add(p);
289 setstr.Append( colname + "=@" + p.ParameterName );
292 cmd.CommandText = "UPDATE " + Quote(_tableName) + " SET " + setstr.ToString() +
293 " WHERE " + CreateOriginalWhere(cmd);
294 cmd.CommandText += "; " + CreateFinalSelect(false);
300 private MySqlCommand CreateInsertCommand()
302 if (_insertCmd != null) return _insertCmd;
304 MySqlCommand cmd = CreateBaseCommand();
306 StringBuilder setstr = new StringBuilder();
307 StringBuilder valstr = new StringBuilder();
308 foreach (DataRow schemaRow in _schema.Rows)
310 string colname = Quote((string)schemaRow["ColumnName"]);
312 if (!IncludedInInsert(schemaRow)) continue;
314 if (setstr.Length > 0)
320 MySqlParameter p = CreateParameter(schemaRow, false);
321 cmd.Parameters.Add(p);
323 setstr.Append( colname );
324 valstr.Append( "@" + p.ParameterName );
327 cmd.CommandText = "INSERT INTO " + Quote(_tableName) + " (" + setstr.ToString() + ") " +
328 " VALUES (" + valstr.ToString() + ")";
329 cmd.CommandText += "; " + CreateFinalSelect(true);
335 private bool IncludedInInsert (DataRow schemaRow)
337 // If the parameter has one of these properties, then we don't include it in the insert:
338 // AutoIncrement, Hidden, Expression, RowVersion, ReadOnly
340 if ((bool) schemaRow ["IsAutoIncrement"])
342 /* if ((bool) schemaRow ["IsHidden"])
344 if ((bool) schemaRow ["IsExpression"])
346 if ((bool) schemaRow ["IsRowVersion"])
348 if ((bool) schemaRow ["IsReadOnly"])
353 private bool IncludedInUpdate (DataRow schemaRow)
355 // If the parameter has one of these properties, then we don't include it in the insert:
356 // AutoIncrement, Hidden, RowVersion
358 if ((bool) schemaRow ["IsAutoIncrement"])
360 // if ((bool) schemaRow ["IsHidden"])
362 if ((bool) schemaRow ["IsRowVersion"])
367 private bool IncludedInWhereClause (DataRow schemaRow)
369 // if ((bool) schemaRow ["IsLong"])
374 private void SetParameterValues(MySqlCommand cmd, DataRow dataRow)
376 foreach (MySqlParameter p in cmd.Parameters)
378 if (p.ParameterName.Length >= 8 && p.ParameterName.Substring(0, 8).Equals("Original"))
379 p.Value = dataRow[ p.SourceColumn, DataRowVersion.Original ];
381 p.Value = dataRow[ p.SourceColumn, DataRowVersion.Current ];
385 private void OnRowUpdating(object sender, MySqlRowUpdatingEventArgs args)
387 // make sure we are still to proceed
388 if (args.Status != UpdateStatus.Continue) return;
393 if (StatementType.Delete == args.StatementType)
394 args.Command = CreateDeleteCommand();
395 else if (StatementType.Update == args.StatementType)
396 args.Command = CreateUpdateCommand();
397 else if (StatementType.Insert == args.StatementType)
398 args.Command = CreateInsertCommand();
399 else if (StatementType.Select == args.StatementType)
402 SetParameterValues(args.Command, args.Row);