SqlCommand.cs 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. //
  2. // System.Data.SqlClient.SqlCommand.cs
  3. //
  4. // Author:
  5. // Rodrigo Moya ([email protected])
  6. // Daniel Morgan ([email protected])
  7. // Tim Coleman ([email protected])
  8. // Diego Caravana ([email protected])
  9. //
  10. // (C) Ximian, Inc 2002 http://www.ximian.com/
  11. // (C) Daniel Morgan, 2002
  12. // Copyright (C) Tim Coleman, 2002
  13. //
  14. //
  15. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  16. //
  17. // Permission is hereby granted, free of charge, to any person obtaining
  18. // a copy of this software and associated documentation files (the
  19. // "Software"), to deal in the Software without restriction, including
  20. // without limitation the rights to use, copy, modify, merge, publish,
  21. // distribute, sublicense, and/or sell copies of the Software, and to
  22. // permit persons to whom the Software is furnished to do so, subject to
  23. // the following conditions:
  24. //
  25. // The above copyright notice and this permission notice shall be
  26. // included in all copies or substantial portions of the Software.
  27. //
  28. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  29. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  30. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  31. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  32. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  33. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  34. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  35. //
  36. using Mono.Data.Tds;
  37. using Mono.Data.Tds.Protocol;
  38. using System;
  39. using System.IO;
  40. using System.Collections;
  41. using System.Collections.Specialized;
  42. using System.ComponentModel;
  43. using System.Data;
  44. using System.Data.Common;
  45. #if NET_2_0
  46. using System.Data.Sql;
  47. #endif
  48. using System.Runtime.InteropServices;
  49. using System.Text;
  50. using System.Xml;
  51. namespace System.Data.SqlClient {
  52. [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
  53. [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
  54. #if NET_2_0
  55. [DefaultEventAttribute ("RecordsAffected")]
  56. public sealed class SqlCommand : DbCommand, IDbCommand, ICloneable
  57. #else
  58. public sealed class SqlCommand : Component, IDbCommand, ICloneable
  59. #endif // NET_2_0
  60. {
  61. #region Fields
  62. const int DEFAULT_COMMAND_TIMEOUT = 30;
  63. int commandTimeout;
  64. bool designTimeVisible;
  65. string commandText;
  66. CommandType commandType;
  67. SqlConnection connection;
  68. SqlTransaction transaction;
  69. UpdateRowSource updatedRowSource;
  70. CommandBehavior behavior = CommandBehavior.Default;
  71. SqlParameterCollection parameters;
  72. string preparedStatement;
  73. #if NET_2_0
  74. bool disposed;
  75. SqlNotificationRequest notification;
  76. bool notificationAutoEnlist;
  77. #endif
  78. #endregion // Fields
  79. #region Constructors
  80. public SqlCommand()
  81. : this (String.Empty, null, null)
  82. {
  83. }
  84. public SqlCommand (string cmdText)
  85. : this (cmdText, null, null)
  86. {
  87. }
  88. public SqlCommand (string cmdText, SqlConnection connection)
  89. : this (cmdText, connection, null)
  90. {
  91. }
  92. public SqlCommand (string cmdText, SqlConnection connection, SqlTransaction transaction)
  93. {
  94. this.commandText = cmdText;
  95. this.connection = connection;
  96. this.transaction = transaction;
  97. this.commandType = CommandType.Text;
  98. this.updatedRowSource = UpdateRowSource.Both;
  99. this.commandTimeout = DEFAULT_COMMAND_TIMEOUT;
  100. #if NET_2_0
  101. notificationAutoEnlist = true;
  102. #endif
  103. designTimeVisible = true;
  104. parameters = new SqlParameterCollection (this);
  105. }
  106. private SqlCommand(string commandText, SqlConnection connection, SqlTransaction transaction, CommandType commandType, UpdateRowSource updatedRowSource, bool designTimeVisible, int commandTimeout, SqlParameterCollection parameters)
  107. {
  108. this.commandText = commandText;
  109. this.connection = connection;
  110. this.transaction = transaction;
  111. this.commandType = commandType;
  112. this.updatedRowSource = updatedRowSource;
  113. this.designTimeVisible = designTimeVisible;
  114. this.commandTimeout = commandTimeout;
  115. this.parameters = new SqlParameterCollection(this);
  116. for (int i = 0;i < parameters.Count;i++)
  117. this.parameters.Add(((ICloneable)parameters[i]).Clone());
  118. }
  119. #endregion // Constructors
  120. #region Properties
  121. internal CommandBehavior CommandBehavior {
  122. get { return behavior; }
  123. }
  124. #if !NET_2_0
  125. [DataSysDescription ("Command text to execute.")]
  126. #endif
  127. [DefaultValue ("")]
  128. [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
  129. [RefreshProperties (RefreshProperties.All)]
  130. public
  131. #if NET_2_0
  132. override
  133. #endif //NET_2_0
  134. string CommandText {
  135. get {
  136. if (commandText == null)
  137. return string.Empty;
  138. return commandText;
  139. }
  140. set {
  141. if (value != commandText && preparedStatement != null)
  142. Unprepare ();
  143. commandText = value;
  144. }
  145. }
  146. #if !NET_2_0
  147. [DataSysDescription ("Time to wait for command to execute.")]
  148. [DefaultValue (DEFAULT_COMMAND_TIMEOUT)]
  149. #endif
  150. public
  151. #if NET_2_0
  152. override
  153. #endif //NET_2_0
  154. int CommandTimeout {
  155. get { return commandTimeout; }
  156. set {
  157. if (value < 0)
  158. #if NET_2_0
  159. throw new ArgumentException ("The property value assigned is less than 0.",
  160. "CommandTimeout");
  161. #else
  162. throw new ArgumentException ("The property value assigned is less than 0.");
  163. #endif
  164. commandTimeout = value;
  165. }
  166. }
  167. #if !NET_2_0
  168. [DataSysDescription ("How to interpret the CommandText.")]
  169. #endif
  170. [DefaultValue (CommandType.Text)]
  171. [RefreshProperties (RefreshProperties.All)]
  172. public
  173. #if NET_2_0
  174. override
  175. #endif //NET_2_0
  176. CommandType CommandType {
  177. get { return commandType; }
  178. set {
  179. if (value == CommandType.TableDirect)
  180. #if NET_2_0
  181. throw new ArgumentOutOfRangeException ("CommandType.TableDirect is not supported " +
  182. "by the Mono SqlClient Data Provider.");
  183. #else
  184. throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono SqlClient Data Provider.");
  185. #endif
  186. ExceptionHelper.CheckEnumValue (typeof (CommandType), value);
  187. commandType = value;
  188. }
  189. }
  190. [DefaultValue (null)]
  191. #if !NET_2_0
  192. [DataSysDescription ("Connection used by the command.")]
  193. #endif
  194. [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
  195. public
  196. #if NET_2_0
  197. new
  198. #endif //NET_2_0
  199. SqlConnection Connection {
  200. get { return connection; }
  201. set
  202. {
  203. #if ONLY_1_1
  204. if (connection != null && connection.DataReader != null)
  205. throw new InvalidOperationException ("The connection is busy fetching data.");
  206. #endif
  207. connection = value;
  208. }
  209. }
  210. [Browsable (false)]
  211. [DefaultValue (true)]
  212. [DesignOnly (true)]
  213. #if NET_2_0
  214. [EditorBrowsable (EditorBrowsableState.Never)]
  215. #endif
  216. public
  217. #if NET_2_0
  218. override
  219. #endif //NET_2_0
  220. bool DesignTimeVisible {
  221. get { return designTimeVisible; }
  222. set { designTimeVisible = value; }
  223. }
  224. #if !NET_2_0
  225. [DataSysDescription ("The parameters collection.")]
  226. #endif
  227. [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
  228. public
  229. #if NET_2_0
  230. new
  231. #endif //NET_2_0
  232. SqlParameterCollection Parameters {
  233. get { return parameters; }
  234. }
  235. internal Tds Tds {
  236. get { return Connection.Tds; }
  237. }
  238. #if !NET_2_0
  239. IDbConnection IDbCommand.Connection {
  240. get { return Connection; }
  241. set {
  242. if (!(value == null || value is SqlConnection))
  243. throw new InvalidCastException ("The value was not a valid SqlConnection.");
  244. Connection = (SqlConnection) value;
  245. }
  246. }
  247. IDataParameterCollection IDbCommand.Parameters {
  248. get { return Parameters; }
  249. }
  250. IDbTransaction IDbCommand.Transaction {
  251. get { return Transaction; }
  252. set {
  253. if (!(value == null || value is SqlTransaction))
  254. throw new ArgumentException ();
  255. Transaction = (SqlTransaction) value;
  256. }
  257. }
  258. #endif
  259. [Browsable (false)]
  260. #if !NET_2_0
  261. [DataSysDescription ("The transaction used by the command.")]
  262. #endif
  263. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  264. public new SqlTransaction Transaction {
  265. get {
  266. if (transaction != null && !transaction.IsOpen)
  267. transaction = null;
  268. return transaction;
  269. }
  270. set
  271. {
  272. #if ONLY_1_1
  273. if (connection != null && connection.DataReader != null)
  274. throw new InvalidOperationException ("The connection is busy fetching data.");
  275. #endif
  276. transaction = value;
  277. }
  278. }
  279. #if !NET_2_0
  280. [DataSysDescription ("When used by a DataAdapter.Update, how command results are applied to the current DataRow.")]
  281. #endif
  282. [DefaultValue (UpdateRowSource.Both)]
  283. public
  284. #if NET_2_0
  285. override
  286. #endif // NET_2_0
  287. UpdateRowSource UpdatedRowSource {
  288. get { return updatedRowSource; }
  289. set {
  290. ExceptionHelper.CheckEnumValue (typeof (UpdateRowSource), value);
  291. updatedRowSource = value;
  292. }
  293. }
  294. #if NET_2_0
  295. [Browsable (false)]
  296. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  297. public SqlNotificationRequest Notification {
  298. get { return notification; }
  299. set { notification = value; }
  300. }
  301. [DefaultValue (true)]
  302. public bool NotificationAutoEnlist {
  303. get { return notificationAutoEnlist; }
  304. set { notificationAutoEnlist = value; }
  305. }
  306. #endif
  307. #endregion // Fields
  308. #region Methods
  309. public
  310. #if NET_2_0
  311. override
  312. #endif // NET_2_0
  313. void Cancel ()
  314. {
  315. if (Connection == null || Connection.Tds == null)
  316. return;
  317. Connection.Tds.Cancel ();
  318. }
  319. #if NET_2_0
  320. public SqlCommand Clone ()
  321. {
  322. return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout, parameters);
  323. }
  324. #endif // NET_2_0
  325. internal void CloseDataReader ()
  326. {
  327. if (Connection != null) {
  328. Connection.DataReader = null;
  329. if ((behavior & CommandBehavior.CloseConnection) != 0)
  330. Connection.Close ();
  331. if (Tds != null)
  332. Tds.SequentialAccess = false;
  333. }
  334. // Reset the behavior
  335. behavior = CommandBehavior.Default;
  336. }
  337. public new SqlParameter CreateParameter ()
  338. {
  339. return new SqlParameter ();
  340. }
  341. private string EscapeProcName (string name, bool schema)
  342. {
  343. string procName;
  344. string tmpProcName = name.Trim ();
  345. int procNameLen = tmpProcName.Length;
  346. char[] brkts = new char [] {'[', ']'};
  347. bool foundMatching = false;
  348. int start = 0, count = procNameLen;
  349. int sindex = -1, eindex = -1;
  350. // We try to match most of the "brackets" combination here, however
  351. // there could be other combinations that may generate a different type
  352. // of exception in MS.NET
  353. if (procNameLen > 1) {
  354. if ((sindex = tmpProcName.IndexOf ('[')) <= 0)
  355. foundMatching = true;
  356. else
  357. foundMatching = false;
  358. if (foundMatching == true && sindex > -1) {
  359. eindex = tmpProcName.IndexOf (']');
  360. if (sindex > eindex && eindex != -1) {
  361. foundMatching = false;
  362. } else if (eindex == procNameLen-1) {
  363. if (tmpProcName.IndexOfAny (brkts, 1, procNameLen-2) != -1) {
  364. foundMatching = false;
  365. } else {
  366. start = 1;
  367. count = procNameLen - 2;
  368. }
  369. } else if (eindex == -1 && schema) {
  370. foundMatching = true;
  371. } else {
  372. foundMatching = false;
  373. }
  374. }
  375. if (foundMatching)
  376. procName = tmpProcName.Substring (start, count);
  377. else
  378. throw new ArgumentException (String.Format ("SqlCommand.CommandText property value is an invalid multipart name {0}, incorrect usage of quotes", CommandText));
  379. } else {
  380. procName = tmpProcName;
  381. }
  382. return procName;
  383. }
  384. internal void DeriveParameters ()
  385. {
  386. if (commandType != CommandType.StoredProcedure)
  387. throw new InvalidOperationException (String.Format ("SqlCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
  388. ValidateCommand ("DeriveParameters", false);
  389. string procName = CommandText;
  390. string schemaName = String.Empty;
  391. int dotPosition = procName.IndexOf ('.');
  392. if (dotPosition >= 0) {
  393. schemaName = procName.Substring (0, dotPosition);
  394. procName = procName.Substring (dotPosition + 1);
  395. }
  396. procName = EscapeProcName (procName, false);
  397. schemaName = EscapeProcName (schemaName, true);
  398. SqlParameterCollection localParameters = new SqlParameterCollection (this);
  399. localParameters.Add ("@procedure_name", SqlDbType.NVarChar, procName.Length).Value = procName;
  400. if (schemaName.Length > 0)
  401. localParameters.Add ("@procedure_schema", SqlDbType.NVarChar, schemaName.Length).Value = schemaName;
  402. string sql = "sp_procedure_params_rowset";
  403. try {
  404. Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
  405. } catch (TdsTimeoutException ex) {
  406. Connection.Tds.Reset ();
  407. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  408. } catch (TdsInternalException ex) {
  409. Connection.Close ();
  410. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  411. }
  412. SqlDataReader reader = new SqlDataReader (this);
  413. parameters.Clear ();
  414. object[] dbValues = new object[reader.FieldCount];
  415. while (reader.Read ()) {
  416. reader.GetValues (dbValues);
  417. parameters.Add (new SqlParameter (dbValues));
  418. }
  419. reader.Close ();
  420. if (parameters.Count == 0)
  421. throw new InvalidOperationException ("Stored procedure '" + procName + "' does not exist.");
  422. }
  423. private void Execute (bool wantResults)
  424. {
  425. int index = 0;
  426. Connection.Tds.RecordsAffected = -1;
  427. TdsMetaParameterCollection parms = Parameters.MetaParameters;
  428. foreach (TdsMetaParameter param in parms) {
  429. param.Validate (index++);
  430. }
  431. if (preparedStatement == null) {
  432. bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
  433. bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
  434. StringBuilder sql1 = new StringBuilder ();
  435. StringBuilder sql2 = new StringBuilder ();
  436. if (schemaOnly || keyInfo)
  437. sql1.Append ("SET FMTONLY OFF;");
  438. if (keyInfo) {
  439. sql1.Append ("SET NO_BROWSETABLE ON;");
  440. sql2.Append ("SET NO_BROWSETABLE OFF;");
  441. }
  442. if (schemaOnly) {
  443. sql1.Append ("SET FMTONLY ON;");
  444. sql2.Append ("SET FMTONLY OFF;");
  445. }
  446. switch (CommandType) {
  447. case CommandType.StoredProcedure:
  448. try {
  449. if (keyInfo || schemaOnly)
  450. Connection.Tds.Execute (sql1.ToString ());
  451. Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
  452. if (keyInfo || schemaOnly)
  453. Connection.Tds.Execute (sql2.ToString ());
  454. } catch (TdsTimeoutException ex) {
  455. // If it is a timeout exception there can be many reasons:
  456. // 1) Network is down/server is down/not reachable
  457. // 2) Somebody has an exclusive lock on Table/DB
  458. // In any of these cases, don't close the connection. Let the user do it
  459. Connection.Tds.Reset ();
  460. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  461. } catch (TdsInternalException ex) {
  462. Connection.Close ();
  463. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  464. }
  465. break;
  466. case CommandType.Text:
  467. string sql;
  468. if (sql2.Length > 0) {
  469. sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
  470. } else {
  471. sql = String.Format ("{0}{1}", sql1.ToString (), CommandText);
  472. }
  473. try {
  474. Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
  475. } catch (TdsTimeoutException ex) {
  476. Connection.Tds.Reset ();
  477. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  478. } catch (TdsInternalException ex) {
  479. Connection.Close ();
  480. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  481. }
  482. break;
  483. }
  484. }
  485. else {
  486. try {
  487. Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
  488. } catch (TdsTimeoutException ex) {
  489. Connection.Tds.Reset ();
  490. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  491. } catch (TdsInternalException ex) {
  492. Connection.Close ();
  493. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  494. }
  495. }
  496. }
  497. public
  498. #if NET_2_0
  499. override
  500. #endif // NET_2_0
  501. int ExecuteNonQuery ()
  502. {
  503. ValidateCommand ("ExecuteNonQuery", false);
  504. int result = 0;
  505. behavior = CommandBehavior.Default;
  506. try {
  507. Execute (false);
  508. result = Connection.Tds.RecordsAffected;
  509. } catch (TdsTimeoutException e) {
  510. Connection.Tds.Reset ();
  511. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  512. }
  513. GetOutputParameters ();
  514. return result;
  515. }
  516. public new SqlDataReader ExecuteReader ()
  517. {
  518. return ExecuteReader (CommandBehavior.Default);
  519. }
  520. public new SqlDataReader ExecuteReader (CommandBehavior behavior)
  521. {
  522. ValidateCommand ("ExecuteReader", false);
  523. if ((behavior & CommandBehavior.SingleRow) != 0)
  524. behavior |= CommandBehavior.SingleResult;
  525. this.behavior = behavior;
  526. if ((behavior & CommandBehavior.SequentialAccess) != 0)
  527. Tds.SequentialAccess = true;
  528. Execute (true);
  529. Connection.DataReader = new SqlDataReader (this);
  530. return Connection.DataReader;
  531. }
  532. public
  533. #if NET_2_0
  534. override
  535. #endif // NET_2_0
  536. object ExecuteScalar ()
  537. {
  538. try {
  539. object result = null;
  540. #if NET_2_0
  541. ValidateCommand ("ExecuteScalar", false);
  542. #else
  543. ValidateCommand ("ExecuteReader", false);
  544. #endif
  545. behavior = CommandBehavior.Default;
  546. Execute (true);
  547. try {
  548. if (Connection.Tds.NextResult () && Connection.Tds.NextRow ())
  549. result = Connection.Tds.ColumnValues[0];
  550. if (commandType == CommandType.StoredProcedure) {
  551. Connection.Tds.SkipToEnd ();
  552. GetOutputParameters ();
  553. }
  554. } catch (TdsTimeoutException ex) {
  555. Connection.Tds.Reset ();
  556. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  557. } catch (TdsInternalException ex) {
  558. Connection.Close ();
  559. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  560. }
  561. return result;
  562. } finally {
  563. CloseDataReader ();
  564. }
  565. }
  566. public XmlReader ExecuteXmlReader ()
  567. {
  568. ValidateCommand ("ExecuteXmlReader", false);
  569. behavior = CommandBehavior.Default;
  570. try {
  571. Execute (true);
  572. } catch (TdsTimeoutException e) {
  573. Connection.Tds.Reset ();
  574. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  575. }
  576. SqlDataReader dataReader = new SqlDataReader (this);
  577. SqlXmlTextReader textReader = new SqlXmlTextReader (dataReader);
  578. XmlReader xmlReader = new XmlTextReader (textReader);
  579. return xmlReader;
  580. }
  581. internal void GetOutputParameters ()
  582. {
  583. IList list = Connection.Tds.OutputParameters;
  584. if (list != null && list.Count > 0) {
  585. int index = 0;
  586. foreach (SqlParameter parameter in parameters) {
  587. if (parameter.Direction != ParameterDirection.Input &&
  588. parameter.Direction != ParameterDirection.ReturnValue) {
  589. parameter.Value = list [index];
  590. index += 1;
  591. }
  592. if (index >= list.Count)
  593. break;
  594. }
  595. }
  596. }
  597. object ICloneable.Clone ()
  598. {
  599. return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout, parameters);
  600. }
  601. #if !NET_2_0
  602. IDbDataParameter IDbCommand.CreateParameter ()
  603. {
  604. return CreateParameter ();
  605. }
  606. IDataReader IDbCommand.ExecuteReader ()
  607. {
  608. return ExecuteReader ();
  609. }
  610. IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
  611. {
  612. return ExecuteReader (behavior);
  613. }
  614. #endif
  615. #if NET_2_0
  616. protected override void Dispose (bool disposing)
  617. {
  618. if (disposed) return;
  619. if (disposing) {
  620. parameters.Clear();
  621. }
  622. base.Dispose (disposing);
  623. disposed = true;
  624. }
  625. #endif
  626. public
  627. #if NET_2_0
  628. override
  629. #endif // NET_2_0
  630. void Prepare ()
  631. {
  632. #if NET_2_0
  633. if (Connection == null)
  634. throw new NullReferenceException ();
  635. #endif
  636. if (CommandType == CommandType.StoredProcedure || CommandType == CommandType.Text && Parameters.Count == 0)
  637. return;
  638. ValidateCommand ("Prepare", false);
  639. try {
  640. foreach (SqlParameter param in Parameters)
  641. param.CheckIfInitialized ();
  642. } catch (Exception e) {
  643. throw new InvalidOperationException ("SqlCommand.Prepare requires " + e.Message);
  644. }
  645. preparedStatement = Connection.Tds.Prepare (CommandText, Parameters.MetaParameters);
  646. }
  647. public void ResetCommandTimeout ()
  648. {
  649. commandTimeout = DEFAULT_COMMAND_TIMEOUT;
  650. }
  651. private void Unprepare ()
  652. {
  653. Connection.Tds.Unprepare (preparedStatement);
  654. preparedStatement = null;
  655. }
  656. private void ValidateCommand (string method, bool async)
  657. {
  658. if (Connection == null)
  659. throw new InvalidOperationException (String.Format ("{0}: A Connection object is required to continue.", method));
  660. if (Transaction == null && Connection.Transaction != null)
  661. throw new InvalidOperationException (String.Format (
  662. "{0} requires a transaction if the command's connection is in a pending transaction.",
  663. #if NET_2_0
  664. method));
  665. #else
  666. "Execute"));
  667. #endif
  668. if (Transaction != null && Transaction.Connection != Connection)
  669. throw new InvalidOperationException ("The connection does not have the same transaction as the command.");
  670. if (Connection.State != ConnectionState.Open)
  671. throw new InvalidOperationException (String.Format ("{0} requires an open connection to continue. This connection is closed.", method));
  672. if (CommandText.Length == 0)
  673. throw new InvalidOperationException (String.Format ("{0}: CommandText has not been set for this Command.", method));
  674. if (Connection.DataReader != null)
  675. throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
  676. if (Connection.XmlReader != null)
  677. throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first.");
  678. #if NET_2_0
  679. if (async && !Connection.AsyncProcessing)
  680. throw new InvalidOperationException ("This Connection object is not " +
  681. "in Asynchronous mode. Use 'Asynchronous" +
  682. " Processing = true' to set it.");
  683. #endif // NET_2_0
  684. }
  685. #if NET_2_0
  686. protected override DbParameter CreateDbParameter ()
  687. {
  688. return CreateParameter ();
  689. }
  690. protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
  691. {
  692. return ExecuteReader (behavior);
  693. }
  694. protected override DbConnection DbConnection {
  695. get { return Connection; }
  696. set { Connection = (SqlConnection) value; }
  697. }
  698. protected override DbParameterCollection DbParameterCollection {
  699. get { return Parameters; }
  700. }
  701. protected override DbTransaction DbTransaction {
  702. get { return Transaction; }
  703. set { Transaction = (SqlTransaction) value; }
  704. }
  705. #endif // NET_2_0
  706. #endregion // Methods
  707. #if NET_2_0
  708. #region Asynchronous Methods
  709. internal IAsyncResult BeginExecuteInternal (CommandBehavior behavior,
  710. bool wantResults,
  711. AsyncCallback callback,
  712. object state)
  713. {
  714. IAsyncResult ar = null;
  715. Connection.Tds.RecordsAffected = -1;
  716. TdsMetaParameterCollection parms = Parameters.MetaParameters;
  717. if (preparedStatement == null) {
  718. bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
  719. bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
  720. StringBuilder sql1 = new StringBuilder ();
  721. StringBuilder sql2 = new StringBuilder ();
  722. if (schemaOnly || keyInfo)
  723. sql1.Append ("SET FMTONLY OFF;");
  724. if (keyInfo) {
  725. sql1.Append ("SET NO_BROWSETABLE ON;");
  726. sql2.Append ("SET NO_BROWSETABLE OFF;");
  727. }
  728. if (schemaOnly) {
  729. sql1.Append ("SET FMTONLY ON;");
  730. sql2.Append ("SET FMTONLY OFF;");
  731. }
  732. switch (CommandType) {
  733. case CommandType.StoredProcedure:
  734. string prolog = "";
  735. string epilog = "";
  736. if (keyInfo || schemaOnly)
  737. prolog = sql1.ToString ();
  738. if (keyInfo || schemaOnly)
  739. epilog = sql2.ToString ();
  740. try {
  741. Connection.Tds.BeginExecuteProcedure (prolog,
  742. epilog,
  743. CommandText,
  744. !wantResults,
  745. parms,
  746. callback,
  747. state);
  748. } catch (TdsTimeoutException ex) {
  749. Connection.Tds.Reset ();
  750. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  751. } catch (TdsInternalException ex) {
  752. Connection.Close ();
  753. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  754. }
  755. break;
  756. case CommandType.Text:
  757. string sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
  758. try {
  759. if (wantResults)
  760. ar = Connection.Tds.BeginExecuteQuery (sql, parms, callback, state);
  761. else
  762. ar = Connection.Tds.BeginExecuteNonQuery (sql, parms, callback, state);
  763. } catch (TdsTimeoutException ex) {
  764. Connection.Tds.Reset ();
  765. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  766. } catch (TdsInternalException ex) {
  767. Connection.Close ();
  768. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  769. }
  770. break;
  771. }
  772. }
  773. else {
  774. try {
  775. Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
  776. } catch (TdsTimeoutException ex) {
  777. Connection.Tds.Reset ();
  778. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  779. } catch (TdsInternalException ex) {
  780. Connection.Close ();
  781. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  782. }
  783. }
  784. return ar;
  785. }
  786. internal void EndExecuteInternal (IAsyncResult ar)
  787. {
  788. SqlAsyncResult sqlResult = ( (SqlAsyncResult) ar);
  789. Connection.Tds.WaitFor (sqlResult.InternalResult);
  790. Connection.Tds.CheckAndThrowException (sqlResult.InternalResult);
  791. }
  792. public IAsyncResult BeginExecuteNonQuery ()
  793. {
  794. return BeginExecuteNonQuery (null, null);
  795. }
  796. public IAsyncResult BeginExecuteNonQuery (AsyncCallback callback, object stateObject)
  797. {
  798. ValidateCommand ("BeginExecuteNonQuery", true);
  799. SqlAsyncResult ar = new SqlAsyncResult (callback, stateObject);
  800. ar.EndMethod = "EndExecuteNonQuery";
  801. ar.InternalResult = BeginExecuteInternal (CommandBehavior.Default, false, ar.BubbleCallback, ar);
  802. return ar;
  803. }
  804. public int EndExecuteNonQuery (IAsyncResult asyncResult)
  805. {
  806. ValidateAsyncResult (asyncResult, "EndExecuteNonQuery");
  807. EndExecuteInternal (asyncResult);
  808. int ret = Connection.Tds.RecordsAffected;
  809. GetOutputParameters ();
  810. ((SqlAsyncResult) asyncResult).Ended = true;
  811. return ret;
  812. }
  813. public IAsyncResult BeginExecuteReader ()
  814. {
  815. return BeginExecuteReader (null, null, CommandBehavior.Default);
  816. }
  817. public IAsyncResult BeginExecuteReader (CommandBehavior behavior)
  818. {
  819. return BeginExecuteReader (null, null, behavior);
  820. }
  821. public IAsyncResult BeginExecuteReader (AsyncCallback callback, object stateObject)
  822. {
  823. return BeginExecuteReader (callback, stateObject, CommandBehavior.Default);
  824. }
  825. public IAsyncResult BeginExecuteReader (AsyncCallback callback, object stateObject, CommandBehavior behavior)
  826. {
  827. ValidateCommand ("BeginExecuteReader", true);
  828. this.behavior = behavior;
  829. SqlAsyncResult ar = new SqlAsyncResult (callback, stateObject);
  830. ar.EndMethod = "EndExecuteReader";
  831. IAsyncResult tdsResult = BeginExecuteInternal (behavior, true,
  832. ar.BubbleCallback, stateObject);
  833. ar.InternalResult = tdsResult;
  834. return ar;
  835. }
  836. public SqlDataReader EndExecuteReader (IAsyncResult asyncResult)
  837. {
  838. ValidateAsyncResult (asyncResult, "EndExecuteReader");
  839. EndExecuteInternal (asyncResult);
  840. SqlDataReader reader = null;
  841. try {
  842. reader = new SqlDataReader (this);
  843. } catch (TdsTimeoutException e) {
  844. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  845. } catch (TdsInternalException e) {
  846. // if behavior is closeconnection, even if it throws exception
  847. // the connection has to be closed.
  848. if ((behavior & CommandBehavior.CloseConnection) != 0)
  849. Connection.Close ();
  850. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  851. }
  852. ((SqlAsyncResult) asyncResult).Ended = true;
  853. return reader;
  854. }
  855. public IAsyncResult BeginExecuteXmlReader (AsyncCallback callback, object stateObject)
  856. {
  857. ValidateCommand ("BeginExecuteXmlReader", true);
  858. SqlAsyncResult ar = new SqlAsyncResult (callback, stateObject);
  859. ar.EndMethod = "EndExecuteXmlReader";
  860. ar.InternalResult = BeginExecuteInternal (behavior, true,
  861. ar.BubbleCallback, stateObject);
  862. return ar;
  863. }
  864. public IAsyncResult BeginExecuteXmlReader ()
  865. {
  866. return BeginExecuteXmlReader (null, null);
  867. }
  868. public XmlReader EndExecuteXmlReader (IAsyncResult asyncResult)
  869. {
  870. ValidateAsyncResult (asyncResult, "EndExecuteXmlReader");
  871. EndExecuteInternal (asyncResult);
  872. SqlDataReader reader = new SqlDataReader (this);
  873. SqlXmlTextReader textReader = new SqlXmlTextReader (reader);
  874. XmlReader xmlReader = new XmlTextReader (textReader);
  875. ((SqlAsyncResult) asyncResult).Ended = true;
  876. return xmlReader;
  877. }
  878. internal void ValidateAsyncResult (IAsyncResult ar, string endMethod)
  879. {
  880. if (ar == null)
  881. throw new ArgumentException ("result passed is null!");
  882. if (! (ar is SqlAsyncResult))
  883. throw new ArgumentException (String.Format ("cannot test validity of types {0}",
  884. ar.GetType ()));
  885. SqlAsyncResult result = (SqlAsyncResult) ar;
  886. if (result.EndMethod != endMethod)
  887. throw new InvalidOperationException (String.Format ("Mismatched {0} called for AsyncResult. " +
  888. "Expected call to {1} but {0} is called instead.",
  889. endMethod, result.EndMethod));
  890. if (result.Ended)
  891. throw new InvalidOperationException (String.Format ("The method {0} cannot be called " +
  892. "more than once for the same AsyncResult.", endMethod));
  893. }
  894. #endregion // Asynchronous Methods
  895. public event StatementCompletedEventHandler StatementCompleted;
  896. #endif // NET_2_0
  897. }
  898. }