SqlCommand.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  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. using System.Data.Sql;
  46. using System.Runtime.InteropServices;
  47. using System.Text;
  48. using System.Threading;
  49. using System.Threading.Tasks;
  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. [DefaultEventAttribute ("RecordsAffected")]
  55. public sealed class SqlCommand : DbCommand, IDbCommand, ICloneable
  56. {
  57. #region Fields
  58. const int DEFAULT_COMMAND_TIMEOUT = 30;
  59. int commandTimeout;
  60. bool designTimeVisible;
  61. string commandText;
  62. CommandType commandType;
  63. SqlConnection connection;
  64. SqlTransaction transaction;
  65. UpdateRowSource updatedRowSource;
  66. CommandBehavior behavior = CommandBehavior.Default;
  67. SqlParameterCollection parameters;
  68. string preparedStatement;
  69. bool disposed;
  70. SqlNotificationRequest notification;
  71. bool notificationAutoEnlist;
  72. #endregion // Fields
  73. #region Constructors
  74. public SqlCommand()
  75. : this (String.Empty, null, null)
  76. {
  77. }
  78. public SqlCommand (string cmdText)
  79. : this (cmdText, null, null)
  80. {
  81. }
  82. public SqlCommand (string cmdText, SqlConnection connection)
  83. : this (cmdText, connection, null)
  84. {
  85. }
  86. public SqlCommand (string cmdText, SqlConnection connection, SqlTransaction transaction)
  87. {
  88. this.commandText = cmdText;
  89. this.connection = connection;
  90. this.transaction = transaction;
  91. this.commandType = CommandType.Text;
  92. this.updatedRowSource = UpdateRowSource.Both;
  93. this.commandTimeout = DEFAULT_COMMAND_TIMEOUT;
  94. notificationAutoEnlist = true;
  95. designTimeVisible = true;
  96. parameters = new SqlParameterCollection (this);
  97. }
  98. private SqlCommand(string commandText, SqlConnection connection, SqlTransaction transaction, CommandType commandType, UpdateRowSource updatedRowSource, bool designTimeVisible, int commandTimeout, SqlParameterCollection parameters)
  99. {
  100. this.commandText = commandText;
  101. this.connection = connection;
  102. this.transaction = transaction;
  103. this.commandType = commandType;
  104. this.updatedRowSource = updatedRowSource;
  105. this.designTimeVisible = designTimeVisible;
  106. this.commandTimeout = commandTimeout;
  107. this.parameters = new SqlParameterCollection(this);
  108. for (int i = 0;i < parameters.Count;i++)
  109. this.parameters.Add(((ICloneable)parameters[i]).Clone());
  110. }
  111. #endregion // Constructors
  112. #region Properties
  113. internal CommandBehavior CommandBehavior {
  114. get { return behavior; }
  115. }
  116. [DefaultValue ("")]
  117. [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
  118. [RefreshProperties (RefreshProperties.All)]
  119. public
  120. override
  121. string CommandText {
  122. get {
  123. if (commandText == null)
  124. return string.Empty;
  125. return commandText;
  126. }
  127. set {
  128. if (value != commandText && preparedStatement != null)
  129. Unprepare ();
  130. commandText = value;
  131. }
  132. }
  133. public
  134. override
  135. int CommandTimeout {
  136. get { return commandTimeout; }
  137. set {
  138. if (value < 0)
  139. throw new ArgumentException ("The property value assigned is less than 0.",
  140. "CommandTimeout");
  141. commandTimeout = value;
  142. }
  143. }
  144. [DefaultValue (CommandType.Text)]
  145. [RefreshProperties (RefreshProperties.All)]
  146. public
  147. override
  148. CommandType CommandType {
  149. get { return commandType; }
  150. set {
  151. if (value == CommandType.TableDirect)
  152. throw new ArgumentOutOfRangeException ("CommandType.TableDirect is not supported " +
  153. "by the Mono SqlClient Data Provider.");
  154. ExceptionHelper.CheckEnumValue (typeof (CommandType), value);
  155. commandType = value;
  156. }
  157. }
  158. [DefaultValue (null)]
  159. [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
  160. public
  161. new
  162. SqlConnection Connection {
  163. get { return connection; }
  164. set
  165. {
  166. connection = value;
  167. }
  168. }
  169. [Browsable (false)]
  170. [DefaultValue (true)]
  171. [DesignOnly (true)]
  172. [EditorBrowsable (EditorBrowsableState.Never)]
  173. public
  174. override
  175. bool DesignTimeVisible {
  176. get { return designTimeVisible; }
  177. set { designTimeVisible = value; }
  178. }
  179. [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
  180. public
  181. new
  182. SqlParameterCollection Parameters {
  183. get { return parameters; }
  184. }
  185. internal Tds Tds {
  186. get { return Connection.Tds; }
  187. }
  188. [Browsable (false)]
  189. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  190. public new SqlTransaction Transaction {
  191. get {
  192. if (transaction != null && !transaction.IsOpen)
  193. transaction = null;
  194. return transaction;
  195. }
  196. set
  197. {
  198. transaction = value;
  199. }
  200. }
  201. [DefaultValue (UpdateRowSource.Both)]
  202. public
  203. override
  204. UpdateRowSource UpdatedRowSource {
  205. get { return updatedRowSource; }
  206. set {
  207. ExceptionHelper.CheckEnumValue (typeof (UpdateRowSource), value);
  208. updatedRowSource = value;
  209. }
  210. }
  211. [Browsable (false)]
  212. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  213. public SqlNotificationRequest Notification {
  214. get { return notification; }
  215. set { notification = value; }
  216. }
  217. [DefaultValue (true)]
  218. public bool NotificationAutoEnlist {
  219. get { return notificationAutoEnlist; }
  220. set { notificationAutoEnlist = value; }
  221. }
  222. #endregion // Fields
  223. #region Methods
  224. public
  225. override
  226. void Cancel ()
  227. {
  228. if (Connection == null || Connection.Tds == null)
  229. return;
  230. Connection.Tds.Cancel ();
  231. }
  232. public SqlCommand Clone ()
  233. {
  234. return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout, parameters);
  235. }
  236. internal void CloseDataReader ()
  237. {
  238. if (Connection != null) {
  239. Connection.DataReader = null;
  240. if ((behavior & CommandBehavior.CloseConnection) != 0)
  241. Connection.Close ();
  242. if (Tds != null)
  243. Tds.SequentialAccess = false;
  244. }
  245. // Reset the behavior
  246. behavior = CommandBehavior.Default;
  247. }
  248. public new SqlParameter CreateParameter ()
  249. {
  250. return new SqlParameter ();
  251. }
  252. private string EscapeProcName (string name, bool schema)
  253. {
  254. string procName;
  255. string tmpProcName = name.Trim ();
  256. int procNameLen = tmpProcName.Length;
  257. char[] brkts = new char [] {'[', ']'};
  258. bool foundMatching = false;
  259. int start = 0, count = procNameLen;
  260. int sindex = -1, eindex = -1;
  261. // We try to match most of the "brackets" combination here, however
  262. // there could be other combinations that may generate a different type
  263. // of exception in MS.NET
  264. if (procNameLen > 1) {
  265. if ((sindex = tmpProcName.IndexOf ('[')) <= 0)
  266. foundMatching = true;
  267. else
  268. foundMatching = false;
  269. if (foundMatching == true && sindex > -1) {
  270. eindex = tmpProcName.IndexOf (']');
  271. if (sindex > eindex && eindex != -1) {
  272. foundMatching = false;
  273. } else if (eindex == procNameLen-1) {
  274. if (tmpProcName.IndexOfAny (brkts, 1, procNameLen-2) != -1) {
  275. foundMatching = false;
  276. } else {
  277. start = 1;
  278. count = procNameLen - 2;
  279. }
  280. } else if (eindex == -1 && schema) {
  281. foundMatching = true;
  282. } else {
  283. foundMatching = false;
  284. }
  285. }
  286. if (foundMatching)
  287. procName = tmpProcName.Substring (start, count);
  288. else
  289. throw new ArgumentException (String.Format ("SqlCommand.CommandText property value is an invalid multipart name {0}, incorrect usage of quotes", CommandText));
  290. } else {
  291. procName = tmpProcName;
  292. }
  293. return procName;
  294. }
  295. internal void DeriveParameters ()
  296. {
  297. if (commandType != CommandType.StoredProcedure)
  298. throw new InvalidOperationException (String.Format ("SqlCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
  299. ValidateCommand ("DeriveParameters", false);
  300. string procName = CommandText;
  301. string schemaName = String.Empty;
  302. int dotPosition = procName.LastIndexOf ('.');
  303. // Procedure name can be: [database].[user].[procname]
  304. if (dotPosition >= 0) {
  305. schemaName = procName.Substring (0, dotPosition);
  306. procName = procName.Substring (dotPosition + 1);
  307. if ((dotPosition = schemaName.LastIndexOf ('.')) >= 0)
  308. schemaName = schemaName.Substring (dotPosition + 1);
  309. }
  310. procName = EscapeProcName (procName, false);
  311. schemaName = EscapeProcName (schemaName, true);
  312. SqlParameterCollection localParameters = new SqlParameterCollection (this);
  313. localParameters.Add ("@procedure_name", SqlDbType.NVarChar, procName.Length).Value = procName;
  314. if (schemaName.Length > 0)
  315. localParameters.Add ("@procedure_schema", SqlDbType.NVarChar, schemaName.Length).Value = schemaName;
  316. string sql = "sp_procedure_params_rowset";
  317. try {
  318. Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
  319. } catch (TdsTimeoutException ex) {
  320. Connection.Tds.Reset ();
  321. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  322. } catch (TdsInternalException ex) {
  323. Connection.Close ();
  324. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  325. }
  326. SqlDataReader reader = new SqlDataReader (this);
  327. parameters.Clear ();
  328. object[] dbValues = new object[reader.FieldCount];
  329. while (reader.Read ()) {
  330. reader.GetValues (dbValues);
  331. parameters.Add (new SqlParameter (dbValues));
  332. }
  333. reader.Close ();
  334. if (parameters.Count == 0)
  335. throw new InvalidOperationException ("Stored procedure '" + procName + "' does not exist.");
  336. }
  337. private void Execute (bool wantResults)
  338. {
  339. int index = 0;
  340. Connection.Tds.RecordsAffected = -1;
  341. TdsMetaParameterCollection parms = Parameters.MetaParameters;
  342. foreach (TdsMetaParameter param in parms) {
  343. param.Validate (index++);
  344. }
  345. if (preparedStatement == null) {
  346. bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
  347. bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
  348. StringBuilder sql1 = new StringBuilder ();
  349. StringBuilder sql2 = new StringBuilder ();
  350. if (schemaOnly || keyInfo)
  351. sql1.Append ("SET FMTONLY OFF;");
  352. if (keyInfo) {
  353. sql1.Append ("SET NO_BROWSETABLE ON;");
  354. sql2.Append ("SET NO_BROWSETABLE OFF;");
  355. }
  356. if (schemaOnly) {
  357. sql1.Append ("SET FMTONLY ON;");
  358. sql2.Append ("SET FMTONLY OFF;");
  359. }
  360. switch (CommandType) {
  361. case CommandType.StoredProcedure:
  362. try {
  363. if (keyInfo || schemaOnly)
  364. Connection.Tds.Execute (sql1.ToString ());
  365. Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
  366. if (keyInfo || schemaOnly)
  367. Connection.Tds.Execute (sql2.ToString ());
  368. } catch (TdsTimeoutException ex) {
  369. // If it is a timeout exception there can be many reasons:
  370. // 1) Network is down/server is down/not reachable
  371. // 2) Somebody has an exclusive lock on Table/DB
  372. // In any of these cases, don't close the connection. Let the user do it
  373. Connection.Tds.Reset ();
  374. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  375. } catch (TdsInternalException ex) {
  376. Connection.Close ();
  377. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  378. }
  379. break;
  380. case CommandType.Text:
  381. string sql;
  382. if (sql2.Length > 0) {
  383. sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
  384. } else {
  385. sql = String.Format ("{0}{1}", sql1.ToString (), CommandText);
  386. }
  387. try {
  388. Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
  389. } catch (TdsTimeoutException ex) {
  390. Connection.Tds.Reset ();
  391. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  392. } catch (TdsInternalException ex) {
  393. Connection.Close ();
  394. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  395. }
  396. break;
  397. }
  398. }
  399. else {
  400. try {
  401. Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
  402. } catch (TdsTimeoutException ex) {
  403. Connection.Tds.Reset ();
  404. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  405. } catch (TdsInternalException ex) {
  406. Connection.Close ();
  407. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  408. }
  409. }
  410. }
  411. public
  412. override
  413. int ExecuteNonQuery ()
  414. {
  415. ValidateCommand ("ExecuteNonQuery", false);
  416. int result = 0;
  417. behavior = CommandBehavior.Default;
  418. try {
  419. Execute (false);
  420. result = Connection.Tds.RecordsAffected;
  421. } catch (TdsTimeoutException e) {
  422. Connection.Tds.Reset ();
  423. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  424. }
  425. GetOutputParameters ();
  426. return result;
  427. }
  428. public new SqlDataReader ExecuteReader ()
  429. {
  430. return ExecuteReader (CommandBehavior.Default);
  431. }
  432. public new SqlDataReader ExecuteReader (CommandBehavior behavior)
  433. {
  434. ValidateCommand ("ExecuteReader", false);
  435. if ((behavior & CommandBehavior.SingleRow) != 0)
  436. behavior |= CommandBehavior.SingleResult;
  437. this.behavior = behavior;
  438. if ((behavior & CommandBehavior.SequentialAccess) != 0)
  439. Tds.SequentialAccess = true;
  440. try {
  441. Execute (true);
  442. Connection.DataReader = new SqlDataReader (this);
  443. return Connection.DataReader;
  444. } catch {
  445. if ((behavior & CommandBehavior.CloseConnection) != 0)
  446. Connection.Close ();
  447. throw;
  448. }
  449. }
  450. [MonoTODO]
  451. public new Task<SqlDataReader> ExecuteReaderAsync ()
  452. {
  453. throw new NotImplementedException ();
  454. }
  455. [MonoTODO]
  456. public new Task<SqlDataReader> ExecuteReaderAsync (CancellationToken cancellationToken)
  457. {
  458. throw new NotImplementedException ();
  459. }
  460. [MonoTODO]
  461. public new Task<SqlDataReader> ExecuteReaderAsync (CommandBehavior behavior)
  462. {
  463. throw new NotImplementedException ();
  464. }
  465. [MonoTODO]
  466. public new Task<SqlDataReader> ExecuteReaderAsync (CommandBehavior behavior, CancellationToken cancellationToken)
  467. {
  468. throw new NotImplementedException ();
  469. }
  470. [MonoTODO]
  471. public Task<XmlReader> ExecuteXmlReaderAsync ()
  472. {
  473. throw new NotImplementedException ();
  474. }
  475. [MonoTODO]
  476. public Task<XmlReader> ExecuteXmlReaderAsync (CancellationToken cancellationToken)
  477. {
  478. throw new NotImplementedException ();
  479. }
  480. public
  481. override
  482. object ExecuteScalar ()
  483. {
  484. try {
  485. object result = null;
  486. ValidateCommand ("ExecuteScalar", false);
  487. behavior = CommandBehavior.Default;
  488. Execute (true);
  489. try {
  490. if (Connection.Tds.NextResult () && Connection.Tds.NextRow ())
  491. result = Connection.Tds.ColumnValues[0];
  492. if (commandType == CommandType.StoredProcedure) {
  493. Connection.Tds.SkipToEnd ();
  494. GetOutputParameters ();
  495. }
  496. } catch (TdsTimeoutException ex) {
  497. Connection.Tds.Reset ();
  498. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  499. } catch (TdsInternalException ex) {
  500. Connection.Close ();
  501. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  502. }
  503. return result;
  504. } finally {
  505. CloseDataReader ();
  506. }
  507. }
  508. public XmlReader ExecuteXmlReader ()
  509. {
  510. ValidateCommand ("ExecuteXmlReader", false);
  511. behavior = CommandBehavior.Default;
  512. try {
  513. Execute (true);
  514. } catch (TdsTimeoutException e) {
  515. Connection.Tds.Reset ();
  516. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  517. }
  518. SqlDataReader dataReader = new SqlDataReader (this);
  519. SqlXmlTextReader textReader = new SqlXmlTextReader (dataReader);
  520. XmlReader xmlReader = new XmlTextReader (textReader);
  521. return xmlReader;
  522. }
  523. internal void GetOutputParameters ()
  524. {
  525. IList list = Connection.Tds.OutputParameters;
  526. if (list != null && list.Count > 0) {
  527. int index = 0;
  528. foreach (SqlParameter parameter in parameters) {
  529. if (parameter.Direction != ParameterDirection.Input &&
  530. parameter.Direction != ParameterDirection.ReturnValue) {
  531. parameter.Value = list [index];
  532. index += 1;
  533. }
  534. if (index >= list.Count)
  535. break;
  536. }
  537. }
  538. }
  539. object ICloneable.Clone ()
  540. {
  541. return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout, parameters);
  542. }
  543. protected override void Dispose (bool disposing)
  544. {
  545. if (disposed) return;
  546. if (disposing) {
  547. parameters.Clear();
  548. if (Connection != null)
  549. Connection.DataReader = null;
  550. }
  551. base.Dispose (disposing);
  552. disposed = true;
  553. }
  554. public
  555. override
  556. void Prepare ()
  557. {
  558. if (Connection == null)
  559. throw new NullReferenceException ();
  560. if (CommandType == CommandType.StoredProcedure || CommandType == CommandType.Text && Parameters.Count == 0)
  561. return;
  562. ValidateCommand ("Prepare", false);
  563. try {
  564. foreach (SqlParameter param in Parameters)
  565. param.CheckIfInitialized ();
  566. } catch (Exception e) {
  567. throw new InvalidOperationException ("SqlCommand.Prepare requires " + e.Message);
  568. }
  569. preparedStatement = Connection.Tds.Prepare (CommandText, Parameters.MetaParameters);
  570. }
  571. public void ResetCommandTimeout ()
  572. {
  573. commandTimeout = DEFAULT_COMMAND_TIMEOUT;
  574. }
  575. private void Unprepare ()
  576. {
  577. Connection.Tds.Unprepare (preparedStatement);
  578. preparedStatement = null;
  579. }
  580. private void ValidateCommand (string method, bool async)
  581. {
  582. if (Connection == null)
  583. throw new InvalidOperationException (String.Format ("{0}: A Connection object is required to continue.", method));
  584. if (Transaction == null && Connection.Transaction != null)
  585. throw new InvalidOperationException (String.Format (
  586. "{0} requires a transaction if the command's connection is in a pending transaction.",
  587. method));
  588. if (Transaction != null && Transaction.Connection != Connection)
  589. throw new InvalidOperationException ("The connection does not have the same transaction as the command.");
  590. if (Connection.State != ConnectionState.Open)
  591. throw new InvalidOperationException (String.Format ("{0} requires an open connection to continue. This connection is closed.", method));
  592. if (CommandText.Length == 0)
  593. throw new InvalidOperationException (String.Format ("{0}: CommandText has not been set for this Command.", method));
  594. if (Connection.DataReader != null)
  595. throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
  596. if (Connection.XmlReader != null)
  597. throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first.");
  598. if (async && !Connection.AsyncProcessing)
  599. throw new InvalidOperationException ("This Connection object is not " +
  600. "in Asynchronous mode. Use 'Asynchronous" +
  601. " Processing = true' to set it.");
  602. }
  603. protected override DbParameter CreateDbParameter ()
  604. {
  605. return CreateParameter ();
  606. }
  607. protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
  608. {
  609. return ExecuteReader (behavior);
  610. }
  611. protected override DbConnection DbConnection {
  612. get { return Connection; }
  613. set { Connection = (SqlConnection) value; }
  614. }
  615. protected override DbParameterCollection DbParameterCollection {
  616. get { return Parameters; }
  617. }
  618. protected override DbTransaction DbTransaction {
  619. get { return Transaction; }
  620. set { Transaction = (SqlTransaction) value; }
  621. }
  622. #endregion // Methods
  623. #region Asynchronous Methods
  624. internal IAsyncResult BeginExecuteInternal (CommandBehavior behavior,
  625. bool wantResults,
  626. AsyncCallback callback,
  627. object state)
  628. {
  629. IAsyncResult ar = null;
  630. Connection.Tds.RecordsAffected = -1;
  631. TdsMetaParameterCollection parms = Parameters.MetaParameters;
  632. if (preparedStatement == null) {
  633. bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
  634. bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
  635. StringBuilder sql1 = new StringBuilder ();
  636. StringBuilder sql2 = new StringBuilder ();
  637. if (schemaOnly || keyInfo)
  638. sql1.Append ("SET FMTONLY OFF;");
  639. if (keyInfo) {
  640. sql1.Append ("SET NO_BROWSETABLE ON;");
  641. sql2.Append ("SET NO_BROWSETABLE OFF;");
  642. }
  643. if (schemaOnly) {
  644. sql1.Append ("SET FMTONLY ON;");
  645. sql2.Append ("SET FMTONLY OFF;");
  646. }
  647. switch (CommandType) {
  648. case CommandType.StoredProcedure:
  649. string prolog = "";
  650. string epilog = "";
  651. if (keyInfo || schemaOnly)
  652. prolog = sql1.ToString ();
  653. if (keyInfo || schemaOnly)
  654. epilog = sql2.ToString ();
  655. try {
  656. ar = Connection.Tds.BeginExecuteProcedure (prolog,
  657. epilog,
  658. CommandText,
  659. !wantResults,
  660. parms,
  661. callback,
  662. state);
  663. } catch (TdsTimeoutException ex) {
  664. Connection.Tds.Reset ();
  665. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  666. } catch (TdsInternalException ex) {
  667. Connection.Close ();
  668. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  669. }
  670. break;
  671. case CommandType.Text:
  672. string sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
  673. try {
  674. if (wantResults)
  675. ar = Connection.Tds.BeginExecuteQuery (sql, parms, callback, state);
  676. else
  677. ar = Connection.Tds.BeginExecuteNonQuery (sql, parms, callback, state);
  678. } catch (TdsTimeoutException ex) {
  679. Connection.Tds.Reset ();
  680. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  681. } catch (TdsInternalException ex) {
  682. Connection.Close ();
  683. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  684. }
  685. break;
  686. }
  687. }
  688. else {
  689. try {
  690. Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
  691. } catch (TdsTimeoutException ex) {
  692. Connection.Tds.Reset ();
  693. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  694. } catch (TdsInternalException ex) {
  695. Connection.Close ();
  696. throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
  697. }
  698. }
  699. return ar;
  700. }
  701. internal void EndExecuteInternal (IAsyncResult ar)
  702. {
  703. SqlAsyncResult sqlResult = ( (SqlAsyncResult) ar);
  704. Connection.Tds.WaitFor (sqlResult.InternalResult);
  705. Connection.Tds.CheckAndThrowException (sqlResult.InternalResult);
  706. }
  707. public IAsyncResult BeginExecuteNonQuery ()
  708. {
  709. return BeginExecuteNonQuery (null, null);
  710. }
  711. public IAsyncResult BeginExecuteNonQuery (AsyncCallback callback, object stateObject)
  712. {
  713. ValidateCommand ("BeginExecuteNonQuery", true);
  714. SqlAsyncResult ar = new SqlAsyncResult (callback, stateObject);
  715. ar.EndMethod = "EndExecuteNonQuery";
  716. ar.InternalResult = BeginExecuteInternal (CommandBehavior.Default, false, ar.BubbleCallback, ar);
  717. return ar;
  718. }
  719. public int EndExecuteNonQuery (IAsyncResult asyncResult)
  720. {
  721. ValidateAsyncResult (asyncResult, "EndExecuteNonQuery");
  722. EndExecuteInternal (asyncResult);
  723. int ret = Connection.Tds.RecordsAffected;
  724. GetOutputParameters ();
  725. ((SqlAsyncResult) asyncResult).Ended = true;
  726. return ret;
  727. }
  728. public IAsyncResult BeginExecuteReader ()
  729. {
  730. return BeginExecuteReader (null, null, CommandBehavior.Default);
  731. }
  732. public IAsyncResult BeginExecuteReader (CommandBehavior behavior)
  733. {
  734. return BeginExecuteReader (null, null, behavior);
  735. }
  736. public IAsyncResult BeginExecuteReader (AsyncCallback callback, object stateObject)
  737. {
  738. return BeginExecuteReader (callback, stateObject, CommandBehavior.Default);
  739. }
  740. public IAsyncResult BeginExecuteReader (AsyncCallback callback, object stateObject, CommandBehavior behavior)
  741. {
  742. ValidateCommand ("BeginExecuteReader", true);
  743. this.behavior = behavior;
  744. SqlAsyncResult ar = new SqlAsyncResult (callback, stateObject);
  745. ar.EndMethod = "EndExecuteReader";
  746. IAsyncResult tdsResult = BeginExecuteInternal (behavior, true,
  747. ar.BubbleCallback, stateObject);
  748. ar.InternalResult = tdsResult;
  749. return ar;
  750. }
  751. public SqlDataReader EndExecuteReader (IAsyncResult asyncResult)
  752. {
  753. ValidateAsyncResult (asyncResult, "EndExecuteReader");
  754. EndExecuteInternal (asyncResult);
  755. SqlDataReader reader = null;
  756. try {
  757. reader = new SqlDataReader (this);
  758. } catch (TdsTimeoutException e) {
  759. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  760. } catch (TdsInternalException e) {
  761. // if behavior is closeconnection, even if it throws exception
  762. // the connection has to be closed.
  763. if ((behavior & CommandBehavior.CloseConnection) != 0)
  764. Connection.Close ();
  765. throw SqlException.FromTdsInternalException ((TdsInternalException) e);
  766. }
  767. ((SqlAsyncResult) asyncResult).Ended = true;
  768. return reader;
  769. }
  770. public IAsyncResult BeginExecuteXmlReader (AsyncCallback callback, object stateObject)
  771. {
  772. ValidateCommand ("BeginExecuteXmlReader", true);
  773. SqlAsyncResult ar = new SqlAsyncResult (callback, stateObject);
  774. ar.EndMethod = "EndExecuteXmlReader";
  775. ar.InternalResult = BeginExecuteInternal (behavior, true,
  776. ar.BubbleCallback, stateObject);
  777. return ar;
  778. }
  779. public IAsyncResult BeginExecuteXmlReader ()
  780. {
  781. return BeginExecuteXmlReader (null, null);
  782. }
  783. public XmlReader EndExecuteXmlReader (IAsyncResult asyncResult)
  784. {
  785. ValidateAsyncResult (asyncResult, "EndExecuteXmlReader");
  786. EndExecuteInternal (asyncResult);
  787. SqlDataReader reader = new SqlDataReader (this);
  788. SqlXmlTextReader textReader = new SqlXmlTextReader (reader);
  789. XmlReader xmlReader = new XmlTextReader (textReader);
  790. ((SqlAsyncResult) asyncResult).Ended = true;
  791. return xmlReader;
  792. }
  793. internal void ValidateAsyncResult (IAsyncResult ar, string endMethod)
  794. {
  795. if (ar == null)
  796. throw new ArgumentException ("result passed is null!");
  797. if (! (ar is SqlAsyncResult))
  798. throw new ArgumentException (String.Format ("cannot test validity of types {0}",
  799. ar.GetType ()));
  800. SqlAsyncResult result = (SqlAsyncResult) ar;
  801. if (result.EndMethod != endMethod)
  802. throw new InvalidOperationException (String.Format ("Mismatched {0} called for AsyncResult. " +
  803. "Expected call to {1} but {0} is called instead.",
  804. endMethod, result.EndMethod));
  805. if (result.Ended)
  806. throw new InvalidOperationException (String.Format ("The method {0} cannot be called " +
  807. "more than once for the same AsyncResult.", endMethod));
  808. }
  809. #endregion // Asynchronous Methods
  810. public event StatementCompletedEventHandler StatementCompleted;
  811. }
  812. }