New test.
[mono.git] / mcs / class / IBM.Data.DB2 / IBM.Data.DB2 / DB2Command.cs
1
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 // 
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 // 
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 //
22 using System;
23 using System.Data;
24 using System.Runtime.InteropServices;
25
26
27 namespace IBM.Data.DB2
28 {
29
30         public class DB2Command : System.ComponentModel.Component, IDbCommand, ICloneable
31         {
32                 #region Private data members
33                 
34                 private WeakReference refDataReader;\r
35                 private string commandText;\r
36                 private CommandType commandType = CommandType.Text;\r
37                 private DB2Connection db2Conn;\r
38                 private DB2Transaction db2Trans;\r
39                 private int commandTimeout = 30;\r
40                 private bool prepared = false;\r
41                 private bool binded = false;\r
42                 private IntPtr hwndStmt = IntPtr.Zero;  //Our statement handle\r
43                 private DB2ParameterCollection parameters = new DB2ParameterCollection();\r
44                 private bool disposed = false;\r
45                 private bool statementOpen;\r
46                 private CommandBehavior previousBehavior;\r
47                 private UpdateRowSource updatedRowSource = UpdateRowSource.Both;\r
48                 private IntPtr statementParametersMemory;\r
49                 private int statementParametersMemorySize;
50
51                 #endregion
52
53                 #region Constructors
54
55                 public DB2Command()
56                 {
57                         hwndStmt = IntPtr.Zero;
58                 }
59                 public DB2Command(string commandStr):this()
60                 {
61                         commandText = commandStr;
62                         
63                 }
64                 public DB2Command(string commandStr, DB2Connection con) : this()
65                 {
66                         db2Conn = con;
67                         commandText = commandStr;
68                         if(con != null)\r
69                         {\r
70                                 con.AddCommand(this);\r
71                         }
72                 }
73                 public DB2Command (string commandStr, DB2Connection con, DB2Transaction trans)
74                 {
75                         commandText = commandStr;\r
76                         db2Conn = con;\r
77                         db2Trans = trans;\r
78                         if(con != null)\r
79                         {\r
80                                 con.AddCommand(this);\r
81                         }
82                 }
83                 #endregion
84
85                 #region Dispose\r
86                 public new void  Dispose()\r
87                 {\r
88                         Dispose(true);\r
89                         GC.SuppressFinalize(this);\r
90                 }\r
91 \r
92                 protected override void Dispose(bool disposing)\r
93                 {\r
94                         if(!disposed) \r
95                         {\r
96                                 if(disposing)\r
97                                 {\r
98                                         ConnectionClosed();\r
99                                         if(db2Conn != null)\r
100                                         {\r
101                                                 db2Conn.RemoveCommand(this);\r
102                                                 db2Conn = null;\r
103                                         }\r
104                                 }\r
105                                 if(statementParametersMemory != IntPtr.Zero)\r
106                                 {\r
107                                         Marshal.FreeHGlobal(statementParametersMemory);\r
108                                         statementParametersMemory = IntPtr.Zero;\r
109                                 }\r
110                         }\r
111                         base.Dispose(disposing);\r
112                         disposed = true;\r
113                 }\r
114 \r
115 \r
116                 ~DB2Command()\r
117                 {\r
118                         Dispose(false);\r
119                 }\r
120 \r
121                 internal void DataReaderClosed()\r
122                 {\r
123                         CloseStatementHandle(false);\r
124                         if((previousBehavior & CommandBehavior.CloseConnection) != 0)\r
125                                 Connection.Close();\r
126                         refDataReader = null;\r
127                 }\r
128 \r
129                 private void CloseStatementHandle(bool dispose)\r
130                 {\r
131                         if(hwndStmt != IntPtr.Zero)\r
132                         {\r
133                                 if(statementOpen)\r
134                                 {\r
135                                         short sqlRet = DB2CLIWrapper.SQLFreeStmt(hwndStmt, DB2Constants.SQL_CLOSE);\r
136                                 }\r
137                                 if((!prepared && statementOpen) ||\r
138                                         dispose)\r
139                                 {\r
140                                         short sqlRet = DB2CLIWrapper.SQLFreeHandle(DB2Constants.SQL_HANDLE_STMT, hwndStmt);\r
141 \r
142                                         hwndStmt = IntPtr.Zero;\r
143                                         prepared = false;\r
144                                 }\r
145                                 statementOpen = false;\r
146                         }\r
147                 }\r
148 \r
149                 internal void ConnectionClosed()\r
150                 {\r
151                         DB2DataReader reader = null;\r
152                         if((refDataReader != null) && refDataReader.IsAlive)\r
153                         {\r
154                                 reader = (DB2DataReader)refDataReader.Target;\r
155                         }\r
156                         if((reader != null) && refDataReader.IsAlive)\r
157                         {\r
158                                 reader.Dispose();\r
159                                 refDataReader = null;\r
160                         }\r
161                         CloseStatementHandle(true);\r
162 \r
163                         db2Trans = null;\r
164                 }\r
165 \r
166                 #endregion
167
168                 #region SelfDescribe property
169                 ///
170                 /// Property dictates whether or not any paramter markers will get their describe info
171                 /// from the database, or if the user will supply the information
172                 /// 
173                 bool selfDescribe = false;
174                 public bool SelfDescribe
175                 {
176                         get 
177                         {
178                                 return selfDescribe;
179                         }
180                         set 
181                         {
182                                 selfDescribe = value;
183                         }
184                 }
185                 #endregion
186
187                 #region CommandText property
188                 ///
189                 ///The query;  If it gets set, reset the prepared property
190                 ///
191                 public string CommandText
192                 {
193                         get
194                         {
195                                 return commandText;
196                         }
197                         set
198                         {
199                                 prepared = false;
200                                 commandText = value;
201                         }
202                 }
203                 #endregion
204
205                 #region CommandTimeout property
206                 ///
207                 /// The Timeout property states how long we wait for results to return
208                 /// 
209                 public int CommandTimeout\r
210                 {\r
211                         get\r
212                         {\r
213                                 return commandTimeout;\r
214                         }\r
215                         set \r
216                         {\r
217                                 commandTimeout = value;\r
218                                 if(hwndStmt != IntPtr.Zero)\r
219                                         SetStatementTimeout();\r
220                         }\r
221                 }
222                 #endregion
223
224                 #region CommandType property
225
226                 public CommandType CommandType
227                 {
228                         get
229                         {
230                                 return commandType;
231                         }
232                         set
233                         {
234                                 commandType = value;
235                         }
236                 }
237                 #endregion
238
239                 #region Connection property
240                 ///
241                 ///  The connection we'll be executing on.
242                 ///  
243                 IDbConnection IDbCommand.Connection
244                 {
245                         get
246                         {
247                                 return db2Conn;
248                         }
249                         set
250                         {
251                                 db2Conn = (DB2Connection)value;
252                         }
253                 }
254
255                 public DB2Connection Connection\r
256                 {\r
257                         get\r
258                         {\r
259                                 return db2Conn;\r
260                         }\r
261                         set\r
262                         {\r
263                                 if(db2Conn != null)\r
264                                 {\r
265                                         db2Conn.RemoveCommand(this);\r
266                                 }\r
267                                 db2Conn = value;\r
268                                 if(db2Conn != null)\r
269                                 {\r
270                                         db2Conn.AddCommand(this);\r
271                                 }\r
272                         }\r
273                 }
274                 #endregion
275
276                 #region Parameters property
277                 ///
278                 /// Parameter list, Not yet implemented
279                 /// 
280                 public DB2ParameterCollection Parameters
281                 {
282                         get
283                         {
284                                 return parameters;
285                         }
286                 }
287                 IDataParameterCollection IDbCommand.Parameters
288                 {
289                         get
290                         {
291                                 return parameters;
292                         }
293                 }
294                 #endregion
295
296                 #region Transaction property
297                         ///
298                         /// The transaction this command is associated with
299                         /// 
300                 IDbTransaction IDbCommand.Transaction
301                 {
302                         get
303                         {
304                                 return db2Trans;
305                         }
306                         set
307                         {
308                                 db2Trans = (DB2Transaction)value;
309                         }
310                 }
311
312                 public DB2Transaction Transaction\r
313                 {\r
314                         get\r
315                         {\r
316                                 return db2Trans;\r
317                         }\r
318                         set\r
319                         {\r
320                                 db2Trans = value;\r
321                         }\r
322                 }
323                 #endregion
324
325                 #region UpdatedRowSource property
326
327                 public UpdateRowSource UpdatedRowSource \r
328                 {\r
329                         get { return updatedRowSource; }\r
330                         set { updatedRowSource = value; }\r
331                 }
332                 #endregion
333
334                 #region Statement Handle
335                 ///
336                 /// returns the DB2Client statement handle
337                 /// 
338                 public IntPtr statementHandle
339                 {
340                         get
341                         {
342                                 return hwndStmt;
343                         }
344                 }
345                 #endregion
346
347                 #region AllocateStatement function
348
349                 internal void AllocateStatement(string location)\r
350                 {\r
351                         if (db2Conn.DBHandle.ToInt32() == 0) return;\r
352                         short sqlRet;\r
353                         sqlRet = DB2CLIWrapper.SQLAllocHandle(DB2Constants.SQL_HANDLE_STMT, db2Conn.DBHandle, out hwndStmt);\r
354                         if ((sqlRet != DB2Constants.SQL_SUCCESS) && (sqlRet != DB2Constants.SQL_SUCCESS_WITH_INFO))\r
355                                 throw new DB2Exception(DB2Constants.SQL_HANDLE_DBC, db2Conn.DBHandle, location +": Unable to allocate statement handle.");\r
356 \r
357                         parameters.HwndStmt = hwndStmt;\r
358 \r
359                         SetStatementTimeout();\r
360                 }
361
362                 private void SetStatementTimeout()\r
363                 {\r
364                         short sqlRet = DB2CLIWrapper.SQLSetStmtAttr(hwndStmt, DB2Constants.SQL_ATTR_QUERY_TIMEOUT, new IntPtr(commandTimeout), 0);\r
365                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Set statement timeout.", db2Conn);\r
366                 }
367                 #endregion
368
369                 #region Cancel
370                 /// <summary>
371                 /// Attempt to cancel an executing command
372                 /// </summary>
373                 public void Cancel()\r
374                 {\r
375                         if(hwndStmt == IntPtr.Zero)\r
376                         {\r
377                                 throw new InvalidOperationException("Nothing to Cancel.");\r
378                         }\r
379                         DB2CLIWrapper.SQLCancel(hwndStmt);\r
380                 }
381                 #endregion
382
383                 #region CreateParameter
384                 ///
385                 ///Returns a parameter
386                 ///
387                 public IDbDataParameter CreateParameter()
388                 {
389                         return new DB2Parameter();
390                 }
391                 #endregion
392
393                 #region ExecuteNonQuery
394
395                 public int ExecuteNonQuery()\r
396                 {\r
397                         ExecuteNonQueryInternal(CommandBehavior.Default);\r
398 \r
399                         int numRows;\r
400 \r
401                         //How many rows affected.  numRows will be -1 if we aren't dealing with an Insert, Delete or Update, or if the statement did not execute successfully\r
402                         short sqlRet = DB2CLIWrapper.SQLRowCount(hwndStmt, out numRows);\r
403                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "SQLExecDirect error.", db2Conn);\r
404 \r
405                         do\r
406                         {\r
407                                 sqlRet = DB2CLIWrapper.SQLMoreResults(this.hwndStmt);\r
408                                 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLMoreResults", db2Conn);\r
409                         } while(sqlRet != DB2Constants.SQL_NO_DATA_FOUND);\r
410 \r
411                         CloseStatementHandle(false);\r
412                         \r
413                         return numRows;\r
414                 }\r
415 \r
416                 public void ExecuteNonQueryInternal(CommandBehavior behavior)\r
417                 {\r
418                         short sqlRet;\r
419 \r
420                         if(prepared && binded)\r
421                         {\r
422                                 sqlRet = DB2CLIWrapper.SQLExecute(hwndStmt);\r
423                                 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "SQLExecute error.", db2Conn);\r
424                                 return;\r
425                         }\r
426 \r
427                         if((db2Conn == null) || (db2Conn.State != ConnectionState.Open))\r
428                                 throw new InvalidOperationException("Prepare needs an open connection");\r
429                         if((refDataReader != null) &&\r
430                                 (refDataReader.IsAlive))\r
431                                 throw new InvalidOperationException("There is already an open DataReader associated with this Connection which must be closed first.");\r
432                         DB2Transaction connectionTransaction = null;\r
433                         if(db2Conn.WeakRefTransaction != null)\r
434                                 connectionTransaction = (DB2Transaction)db2Conn.WeakRefTransaction.Target;\r
435                         if(!Object.ReferenceEquals(connectionTransaction, Transaction))\r
436                         {\r
437                                 if(Transaction == null)\r
438                                         throw new InvalidOperationException("A transaction was started in the connection, but the command doesn't specify a transaction");\r
439                                 throw new InvalidOperationException("The transaction specified at the connection doesn't belong to the connection");\r
440                         }\r
441 \r
442                         if (hwndStmt == IntPtr.Zero)\r
443                         {\r
444                                 AllocateStatement("InternalExecuteNonQuery");\r
445                                 previousBehavior = 0;\r
446                         }\r
447                         if(previousBehavior != behavior)\r
448                         {\r
449                                 if(((previousBehavior ^ behavior) & CommandBehavior.SchemaOnly) != 0)\r
450                                 {\r
451                                         sqlRet = DB2CLIWrapper.SQLSetStmtAttr(hwndStmt, DB2Constants.SQL_ATTR_DEFERRED_PREPARE, \r
452                                                 new IntPtr((behavior & CommandBehavior.SchemaOnly) != 0 ? 0 : 1), 0);\r
453                                         // TODO: don't check. what if it is not supported???\r
454                                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Defered prepare.", db2Conn);\r
455 \r
456                                         previousBehavior = (previousBehavior & ~CommandBehavior.SchemaOnly) | (behavior & CommandBehavior.SchemaOnly);\r
457                                 }\r
458                                 if(((previousBehavior ^ behavior) & CommandBehavior.SingleRow) != 0)\r
459                                 {\r
460                                         sqlRet = DB2CLIWrapper.SQLSetStmtAttr(hwndStmt, DB2Constants.SQL_ATTR_MAX_ROWS, \r
461                                                 new IntPtr((behavior & CommandBehavior.SingleRow) == 0 ? 0 : 1), 0);\r
462                                         // TODO: don't check. what if it is not supported???\r
463                                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Set max rows", db2Conn);\r
464 \r
465                                         previousBehavior = (previousBehavior & ~CommandBehavior.SingleRow) | (behavior & CommandBehavior.SingleRow);\r
466                                 }\r
467                                 previousBehavior = behavior;\r
468                         }\r
469                         if((Transaction == null) &&\r
470                                 !db2Conn.openConnection.autoCommit)\r
471                         {\r
472                                 sqlRet = DB2CLIWrapper.SQLSetConnectAttr(db2Conn.DBHandle, DB2Constants.SQL_ATTR_AUTOCOMMIT, new IntPtr(DB2Constants.SQL_AUTOCOMMIT_ON), 0);\r
473                                 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_DBC, db2Conn.DBHandle, "Error setting AUTOCOMMIT ON in transaction CTOR.", db2Conn);\r
474                                 db2Conn.openConnection.autoCommit = true;\r
475 \r
476                                 sqlRet = DB2CLIWrapper.SQLSetConnectAttr(db2Conn.DBHandle, DB2Constants.SQL_ATTR_TXN_ISOLATION, new IntPtr(DB2Constants.SQL_TXN_READ_COMMITTED), 0);\r
477                                 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_DBC, db2Conn.DBHandle, "Error setting isolation level.", db2Conn);\r
478                         }\r
479 \r
480 \r
481                         if ((commandText == null) ||(commandText.Length == 0))\r
482                                 throw new InvalidOperationException("Command string is empty");\r
483                                 \r
484                         if(CommandType.StoredProcedure == commandType && !commandText.StartsWith("CALL "))\r
485                                 commandText = "CALL " + commandText + " ()";\r
486                         \r
487                         if((behavior & CommandBehavior.SchemaOnly) != 0)\r
488                         {\r
489                                 if(!prepared)\r
490                                 {\r
491                                         Prepare();\r
492                                 }\r
493                         }\r
494                         else\r
495                         {\r
496                                 if(statementParametersMemory != IntPtr.Zero)\r
497                                 {\r
498                                         Marshal.FreeHGlobal(statementParametersMemory);\r
499                                         statementParametersMemory = IntPtr.Zero;\r
500                                 }\r
501 \r
502                                 BindParams();\r
503                                 \r
504                                 if (prepared)\r
505                                 {\r
506                                         sqlRet = DB2CLIWrapper.SQLExecute(hwndStmt);\r
507                                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "SQLExecute error.", db2Conn);\r
508                                 }\r
509                                 else\r
510                                 {\r
511                                         sqlRet = DB2CLIWrapper.SQLExecDirect(hwndStmt, commandText, commandText.Length);\r
512                                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "SQLExecDirect error.", db2Conn);\r
513                                 }\r
514                                 statementOpen = true;\r
515 \r
516                                 parameters.GetOutValues();\r
517 \r
518                                 if(statementParametersMemory != IntPtr.Zero)\r
519                                 {\r
520                                         Marshal.FreeHGlobal(statementParametersMemory);\r
521                                         statementParametersMemory = IntPtr.Zero;\r
522                                 }\r
523                         }\r
524                 }
525                 #endregion
526
527                 #region ExecuteReader calls
528                 ///
529                 ///ExecuteReader
530                 ///
531                 IDataReader IDbCommand.ExecuteReader()\r
532                 {\r
533                         return ExecuteReader(CommandBehavior.Default);\r
534                 }\r
535 \r
536                 IDataReader IDbCommand.ExecuteReader(CommandBehavior behavior)\r
537                 {\r
538                         return ExecuteReader(behavior);\r
539                 }\r
540 \r
541                 public DB2DataReader ExecuteReader()\r
542                 {\r
543                         return ExecuteReader(CommandBehavior.Default);\r
544                 }\r
545 \r
546                 public DB2DataReader ExecuteReader(CommandBehavior behavior)\r
547                 {\r
548                         if((db2Conn == null) || (db2Conn.State != ConnectionState.Open))\r
549                                 throw new InvalidOperationException("Prepare needs an open connection");\r
550 \r
551                         DB2DataReader reader;\r
552                         \r
553                         ExecuteNonQueryInternal(behavior);\r
554                         reader = new DB2DataReader(db2Conn, this, behavior);\r
555                         \r
556                         refDataReader = new WeakReference(reader);\r
557 \r
558                         return reader;\r
559                 }
560                 #endregion
561
562                 #region ExecuteScalar
563                 ///
564                 /// ExecuteScalar
565                 /// 
566                 public object ExecuteScalar()\r
567                 {\r
568                         if((db2Conn == null) || (db2Conn.State != ConnectionState.Open))\r
569                                 throw new InvalidOperationException("Prepare needs an open connection");\r
570 \r
571                         using(DB2DataReader reader = ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.SingleRow))\r
572                         {\r
573                                 if(reader.Read() && (reader.FieldCount > 0))\r
574                                 {\r
575                                         return reader[0];\r
576                                 }\r
577                         }\r
578                         return null;\r
579                 }
580                 #endregion
581
582                 #region Prepare ()
583 \r
584                 public void Prepare ()\r
585                 {\r
586                         if((db2Conn == null) || (db2Conn.State != ConnectionState.Open))\r
587                                 throw new InvalidOperationException("Prepare needs an open connection");\r
588 \r
589                         CloseStatementHandle(false);\r
590                         if (hwndStmt == IntPtr.Zero)\r
591                         {\r
592                                 AllocateStatement("InternalExecuteNonQuery");\r
593                         }\r
594 \r
595                         short sqlRet = 0;\r
596 \r
597                         IntPtr numParams = IntPtr.Zero;\r
598                         sqlRet = DB2CLIWrapper.SQLPrepare(hwndStmt, commandText, commandText.Length);\r
599                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "SQLPrepare error.", db2Conn);\r
600                         \r
601                         \r
602                         statementOpen = true;\r
603                         prepared=true;\r
604                 }
605                 #endregion
606
607                 private string AddCallParam( string _cmString)\r
608                 {
609                         if(_cmString.IndexOf("()") != -1)\r
610                         {\r
611                                 return _cmString.Replace("()","(?)");\r
612                         }\r
613                         return _cmString.Replace(")", ",?)");
614                 }
615
616                 private void BindParams()\r
617                 {
618                         if(parameters.Count > 0)\r
619                         {\r
620                                 statementParametersMemorySize = 0;\r
621                                 int offset = 0;\r
622                                 short sqlRet;\r
623                                 for(int i = 0; i < parameters.Count; i++) \r
624                                 {\r
625                                         if(commandType == CommandType.StoredProcedure)\r
626                                         {\r
627                                                 commandText = AddCallParam(commandText);\r
628                                         }\r
629                                         DB2Parameter param = parameters[i];\r
630                                         param.CalculateRequiredmemory();\r
631                                         statementParametersMemorySize += param.requiredMemory + 8;\r
632                                         param.internalBuffer = Marshal.AllocHGlobal(param.requiredMemory);\r
633                                         offset += param.requiredMemory;\r
634                                         param.internalLengthBuffer = Marshal.AllocHGlobal(4);\r
635                                         Marshal.WriteInt32(param.internalLengthBuffer, param.requiredMemory);\r
636                                         sqlRet = param.Bind(this.hwndStmt, (short)(i + 1));\r
637                                         DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Error binding parameter in DB2Command: ", db2Conn);\r
638                                 }\r
639                                 binded = true;\r
640                         }
641                 }
642
643                 #region ICloneable Members\r
644 \r
645                 object ICloneable.Clone()\r
646                 {\r
647                         DB2Command clone = new DB2Command();\r
648 \r
649                         clone.Connection = Connection;\r
650                         clone.commandText = commandText;\r
651                         clone.commandType = commandType;\r
652                         clone.Transaction = db2Trans;\r
653                         clone.commandTimeout = commandTimeout;\r
654                         clone.updatedRowSource = updatedRowSource;\r
655                         clone.parameters = new DB2ParameterCollection();\r
656                         for(int i = 0; i < parameters.Count; i++)\r
657                                 clone.Parameters.Add(((ICloneable)parameters[i]).Clone());\r
658 \r
659                         return clone;\r
660                 }\r
661 \r
662                 #endregion
663         }
664 }