NpgsqlCommand.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. // created on 21/5/2002 at 20:03
  2. // Npgsql.NpgsqlCommand.cs
  3. //
  4. // Author:
  5. // Francisco Jr. ([email protected])
  6. //
  7. // Copyright (C) 2002 The Npgsql Development Team
  8. // [email protected]
  9. // http://gborg.postgresql.org/project/npgsql/projdisplay.php
  10. //
  11. // This library is free software; you can redistribute it and/or
  12. // modify it under the terms of the GNU Lesser General Public
  13. // License as published by the Free Software Foundation; either
  14. // version 2.1 of the License, or (at your option) any later version.
  15. //
  16. // This library is distributed in the hope that it will be useful,
  17. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. // Lesser General Public License for more details.
  20. //
  21. // You should have received a copy of the GNU Lesser General Public
  22. // License along with this library; if not, write to the Free Software
  23. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24. using System;
  25. using System.Data;
  26. using System.Text;
  27. using System.Resources;
  28. using System.ComponentModel;
  29. using System.Collections;
  30. using NpgsqlTypes;
  31. using Npgsql.Design;
  32. namespace Npgsql
  33. {
  34. /// <summary>
  35. /// Represents a SQL statement or function (stored procedure) to execute
  36. /// against a PostgreSQL database. This class cannot be inherited.
  37. /// </summary>
  38. [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlCommand)), ToolboxItem(true)]
  39. public sealed class NpgsqlCommand : Component, IDbCommand
  40. {
  41. // Logging related values
  42. private static readonly String CLASSNAME = "NpgsqlCommand";
  43. private static ResourceManager resman = new ResourceManager(typeof(NpgsqlCommand));
  44. private NpgsqlConnection connection;
  45. private NpgsqlConnector connector;
  46. private NpgsqlTransaction transaction;
  47. private String text;
  48. private Int32 timeout;
  49. private CommandType type;
  50. private NpgsqlParameterCollection parameters;
  51. private String planName;
  52. private static Int32 planIndex = 0;
  53. private static Int32 portalIndex = 0;
  54. private NpgsqlParse parse;
  55. private NpgsqlBind bind;
  56. // Constructors
  57. /// <summary>
  58. /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class.
  59. /// </summary>
  60. public NpgsqlCommand() : this(String.Empty, null, null)
  61. {}
  62. /// <summary>
  63. /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class with the text of the query.
  64. /// </summary>
  65. /// <param name="cmdText">The text of the query.</param>
  66. public NpgsqlCommand(String cmdText) : this(cmdText, null, null)
  67. {}
  68. /// <summary>
  69. /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class with the text of the query and a <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
  70. /// </summary>
  71. /// <param name="cmdText">The text of the query.</param>
  72. /// <param name="connection">A <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> that represents the connection to a PostgreSQL server.</param>
  73. public NpgsqlCommand(String cmdText, NpgsqlConnection connection) : this(cmdText, connection, null)
  74. {}
  75. /// <summary>
  76. /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class with the text of the query, a <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>, and the <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>.
  77. /// </summary>
  78. /// <param name="cmdText">The text of the query.</param>
  79. /// <param name="connection">A <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> that represents the connection to a PostgreSQL server.</param>
  80. /// <param name="transaction">The <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> in which the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> executes.</param>
  81. public NpgsqlCommand(String cmdText, NpgsqlConnection connection, NpgsqlTransaction transaction)
  82. {
  83. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
  84. planName = String.Empty;
  85. text = cmdText;
  86. this.connection = connection;
  87. if (this.connection != null)
  88. this.connector = connection.Connector;
  89. parameters = new NpgsqlParameterCollection();
  90. timeout = 20;
  91. type = CommandType.Text;
  92. this.Transaction = transaction;
  93. }
  94. /// <summary>
  95. /// Used to execute internal commands.
  96. /// </summary>
  97. internal NpgsqlCommand(String cmdText, NpgsqlConnector connector)
  98. {
  99. resman = new System.Resources.ResourceManager(this.GetType());
  100. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
  101. planName = String.Empty;
  102. text = cmdText;
  103. this.connector = connector;
  104. type = CommandType.Text;
  105. }
  106. // Public properties.
  107. /// <summary>
  108. /// Gets or sets the SQL statement or function (stored procedure) to execute at the data source.
  109. /// </summary>
  110. /// <value>The Transact-SQL statement or stored procedure to execute. The default is an empty string.</value>
  111. [Category("Data"), DefaultValue("")]
  112. public String CommandText {
  113. get
  114. {
  115. return text;
  116. }
  117. set
  118. {
  119. // [TODO] Validate commandtext.
  120. NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "CommandText", value);
  121. text = value;
  122. planName = String.Empty;
  123. parse = null;
  124. bind = null;
  125. }
  126. }
  127. /// <summary>
  128. /// Gets or sets the wait time before terminating the attempt
  129. /// to execute a command and generating an error.
  130. /// </summary>
  131. /// <value>The time (in seconds) to wait for the command to execute.
  132. /// The default is 20 seconds.</value>
  133. [DefaultValue(20)]
  134. public Int32 CommandTimeout {
  135. get
  136. {
  137. return timeout;
  138. }
  139. set
  140. {
  141. if (value < 0)
  142. throw new ArgumentOutOfRangeException(resman.GetString("Exception_CommandTimeoutLessZero"));
  143. timeout = value;
  144. NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "CommandTimeout", value);
  145. }
  146. }
  147. /// <summary>
  148. /// Gets or sets a value indicating how the
  149. /// <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> property is to be interpreted.
  150. /// </summary>
  151. /// <value>One of the <see cref="System.Data.CommandType">CommandType</see> values. The default is <see cref="System.Data.CommandType">CommandType.Text</see>.</value>
  152. [Category("Data"), DefaultValue(CommandType.Text)]
  153. public CommandType CommandType {
  154. get
  155. {
  156. return type;
  157. }
  158. set
  159. {
  160. type = value;
  161. NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "CommandType", value);
  162. }
  163. }
  164. IDbConnection IDbCommand.Connection {
  165. get
  166. {
  167. return Connection;
  168. }
  169. set
  170. {
  171. Connection = (NpgsqlConnection) value;
  172. NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "IDbCommand.Connection", value);
  173. }
  174. }
  175. /// <summary>
  176. /// Gets or sets the <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>
  177. /// used by this instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
  178. /// </summary>
  179. /// <value>The connection to a data source. The default value is a null reference.</value>
  180. [Category("Behavior"), DefaultValue(null)]
  181. public NpgsqlConnection Connection {
  182. get
  183. {
  184. NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Connection");
  185. return connection;
  186. }
  187. set
  188. {
  189. if (this.transaction != null && this.transaction.Connection == null)
  190. this.transaction = null;
  191. if (this.connection != null && this.Connector.Transaction != null)
  192. throw new InvalidOperationException(resman.GetString("Exception_SetConnectionInTransaction"));
  193. this.connection = value;
  194. if (this.connection != null)
  195. connector = this.connection.Connector;
  196. NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "Connection", value);
  197. }
  198. }
  199. internal NpgsqlConnector Connector {
  200. get
  201. {
  202. if (connector == null && this.connection != null)
  203. connector = this.connection.Connector;
  204. return connector;
  205. }
  206. }
  207. IDataParameterCollection IDbCommand.Parameters {
  208. get
  209. {
  210. return Parameters;
  211. }
  212. }
  213. /// <summary>
  214. /// Gets the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see>.
  215. /// </summary>
  216. /// <value>The parameters of the SQL statement or function (stored procedure). The default is an empty collection.</value>
  217. [Category("Data"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  218. public NpgsqlParameterCollection Parameters {
  219. get
  220. {
  221. NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Parameters");
  222. return parameters;
  223. }
  224. }
  225. /// <summary>
  226. /// Gets or sets the <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
  227. /// within which the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> executes.
  228. /// </summary>
  229. /// <value>The <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>.
  230. /// The default value is a null reference.</value>
  231. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  232. public IDbTransaction Transaction {
  233. get
  234. {
  235. NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Transaction");
  236. if (this.transaction != null && this.transaction.Connection == null)
  237. {
  238. this.transaction = null;
  239. }
  240. return this.transaction;
  241. }
  242. set
  243. {
  244. NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "Transaction" ,value);
  245. this.transaction = (NpgsqlTransaction) value;
  246. }
  247. }
  248. /// <summary>
  249. /// Gets or sets how command results are applied to the <see cref="System.Data.DataRow">DataRow</see>
  250. /// when used by the <see cref="System.Data.Common.DbDataAdapter.Update">Update</see>
  251. /// method of the <see cref="System.Data.Common.DbDataAdapter">DbDataAdapter</see>.
  252. /// </summary>
  253. /// <value>One of the <see cref="System.Data.UpdateRowSource">UpdateRowSource</see> values.</value>
  254. [Category("Behavior"), DefaultValue(UpdateRowSource.Both)]
  255. public UpdateRowSource UpdatedRowSource {
  256. get
  257. {
  258. NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "UpdatedRowSource");
  259. return UpdateRowSource.Both;
  260. }
  261. set
  262. {
  263. throw new NotImplementedException();
  264. }
  265. }
  266. /// <summary>
  267. /// Attempts to cancel the execution of a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
  268. /// </summary>
  269. /// <remarks>This Method isn't implemented yet.</remarks>
  270. public void Cancel()
  271. {
  272. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Cancel");
  273. // [TODO] Finish method implementation.
  274. throw new NotImplementedException();
  275. }
  276. /// <summary>
  277. /// Creates a new instance of an <see cref="System.Data.IDbDataParameter">IDbDataParameter</see> object.
  278. /// </summary>
  279. /// <returns>An <see cref="System.Data.IDbDataParameter">IDbDataParameter</see> object.</returns>
  280. IDbDataParameter IDbCommand.CreateParameter()
  281. {
  282. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbCommand.CreateParameter");
  283. return (NpgsqlParameter) CreateParameter();
  284. }
  285. /// <summary>
  286. /// Creates a new instance of a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.
  287. /// </summary>
  288. /// <returns>A <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns>
  289. public NpgsqlParameter CreateParameter()
  290. {
  291. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateParameter");
  292. return new NpgsqlParameter();
  293. }
  294. /// <summary>
  295. /// Executes a SQL statement against the connection and returns the number of rows affected.
  296. /// </summary>
  297. /// <returns>The number of rows affected.</returns>
  298. public Int32 ExecuteNonQuery()
  299. {
  300. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteNonQuery");
  301. ExecuteCommand();
  302. // If nothing is returned, just return -1.
  303. if(Connector.Mediator.CompletedResponses.Count == 0) {
  304. return -1;
  305. }
  306. // Check if the response is available.
  307. String firstCompletedResponse = (String)Connector.Mediator.CompletedResponses[0];
  308. if (firstCompletedResponse == null)
  309. return -1;
  310. String[] ret_string_tokens = firstCompletedResponse.Split(null); // whitespace separator.
  311. // Check if the command was insert, delete or update.
  312. // Only theses commands return rows affected.
  313. // [FIXME] Is there a better way to check this??
  314. if ((String.Compare(ret_string_tokens[0], "INSERT", true) == 0) ||
  315. (String.Compare(ret_string_tokens[0], "UPDATE", true) == 0) ||
  316. (String.Compare(ret_string_tokens[0], "DELETE", true) == 0))
  317. // The number of rows affected is in the third token for insert queries
  318. // and in the second token for update and delete queries.
  319. // In other words, it is the last token in the 0-based array.
  320. return Int32.Parse(ret_string_tokens[ret_string_tokens.Length - 1]);
  321. else
  322. return -1;
  323. }
  324. /// <summary>
  325. /// Sends the <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> to
  326. /// the <see cref="Npgsql.NpgsqlConnection">Connection</see> and builds a
  327. /// <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see>.
  328. /// </summary>
  329. /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns>
  330. IDataReader IDbCommand.ExecuteReader()
  331. {
  332. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbCommand.ExecuteReader");
  333. return (NpgsqlDataReader) ExecuteReader();
  334. }
  335. /// <summary>
  336. /// Sends the <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> to
  337. /// the <see cref="Npgsql.NpgsqlConnection">Connection</see> and builds a
  338. /// <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see>
  339. /// using one of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values.
  340. /// </summary>
  341. /// <param name="cb">One of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values.</param>
  342. /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns>
  343. IDataReader IDbCommand.ExecuteReader(CommandBehavior cb)
  344. {
  345. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbCommand.ExecuteReader", cb);
  346. return (NpgsqlDataReader) ExecuteReader(cb);
  347. }
  348. /// <summary>
  349. /// Sends the <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> to
  350. /// the <see cref="Npgsql.NpgsqlConnection">Connection</see> and builds a
  351. /// <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see>.
  352. /// </summary>
  353. /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns>
  354. public NpgsqlDataReader ExecuteReader()
  355. {
  356. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader");
  357. return ExecuteReader(CommandBehavior.Default);
  358. }
  359. /// <summary>
  360. /// Sends the <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> to
  361. /// the <see cref="Npgsql.NpgsqlConnection">Connection</see> and builds a
  362. /// <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see>
  363. /// using one of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values.
  364. /// </summary>
  365. /// <param name="cb">One of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values.</param>
  366. /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns>
  367. /// <remarks>Currently the CommandBehavior parameter is ignored.</remarks>
  368. public NpgsqlDataReader ExecuteReader(CommandBehavior cb)
  369. {
  370. // [FIXME] No command behavior handling.
  371. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader", cb);
  372. ExecuteCommand();
  373. // Get the resultsets and create a Datareader with them.
  374. return new NpgsqlDataReader(Connector.Mediator.ResultSets, Connector.Mediator.CompletedResponses, connection, cb);
  375. }
  376. ///<summary>
  377. /// This method binds the parameters from parameters collection to the bind
  378. /// message.
  379. /// </summary>
  380. private void BindParameters()
  381. {
  382. if (parameters.Count != 0)
  383. {
  384. Object[] parameterValues = new Object[parameters.Count];
  385. for (Int32 i = 0; i < parameters.Count; i++)
  386. {
  387. // Do not quote strings, or escape existing quotes - this will be handled by the backend.
  388. parameterValues[i] = parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, true);
  389. }
  390. bind.ParameterValues = parameterValues;
  391. }
  392. Connector.Bind(bind);
  393. Connector.Mediator.RequireReadyForQuery = false;
  394. Connector.Flush();
  395. connector.CheckErrorsAndNotifications();
  396. }
  397. /// <summary>
  398. /// Executes the query, and returns the first column of the first row
  399. /// in the result set returned by the query. Extra columns or rows are ignored.
  400. /// </summary>
  401. /// <returns>The first column of the first row in the result set,
  402. /// or a null reference if the result set is empty.</returns>
  403. public Object ExecuteScalar()
  404. {
  405. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteScalar");
  406. /*if ((type == CommandType.Text) || (type == CommandType.StoredProcedure))
  407. if (parse == null)
  408. connection.Query(this);
  409. else
  410. {
  411. BindParameters();
  412. connection.Execute(new NpgsqlExecute(bind.PortalName, 0));
  413. }
  414. else
  415. throw new NotImplementedException(resman.GetString("Exception_CommandTypeTableDirect"));
  416. */
  417. ExecuteCommand();
  418. // Now get the results.
  419. // Only the first column of the first row must be returned.
  420. // Get ResultSets.
  421. ArrayList resultSets = Connector.Mediator.ResultSets;
  422. // First data is the RowDescription object.
  423. // Check all resultsets as insert commands could have been sent along
  424. // with resultset queries. The insert commands return null and and some queries
  425. // may return empty resultsets, so, if we find one of these, skip to next resultset.
  426. // If no resultset is found, return null as per specification.
  427. NpgsqlAsciiRow ascii_row = null;
  428. foreach( NpgsqlResultSet nrs in resultSets )
  429. {
  430. if( (nrs != null) && (nrs.Count > 0) )
  431. {
  432. ascii_row = (NpgsqlAsciiRow) nrs[0];
  433. return ascii_row[0];
  434. }
  435. }
  436. return null;
  437. }
  438. /// <summary>
  439. /// Creates a prepared version of the command on a PostgreSQL server.
  440. /// </summary>
  441. public void Prepare()
  442. {
  443. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Prepare");
  444. // Check the connection state.
  445. CheckConnectionState();
  446. if (! Connector.SupportsPrepare) {
  447. return; // Do nothing.
  448. }
  449. if (connector.BackendProtocolVersion == ProtocolVersion.Version2)
  450. {
  451. NpgsqlCommand command = new NpgsqlCommand(GetPrepareCommandText(), connector );
  452. command.ExecuteNonQuery();
  453. }
  454. else
  455. {
  456. // Use the extended query parsing...
  457. planName = "NpgsqlPlan" + System.Threading.Interlocked.Increment(ref planIndex);
  458. String portalName = "NpgsqlPortal" + System.Threading.Interlocked.Increment(ref portalIndex);
  459. parse = new NpgsqlParse(planName, GetParseCommandText(), new Int32[] {});
  460. Connector.Parse(parse);
  461. Connector.Mediator.RequireReadyForQuery = false;
  462. Connector.Flush();
  463. // Check for errors and/or notifications and do the Right Thing.
  464. connector.CheckErrorsAndNotifications();
  465. bind = new NpgsqlBind(portalName, planName, new Int16[] {0}, null, new Int16[] {0});
  466. }
  467. }
  468. /*
  469. /// <summary>
  470. /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
  471. /// </summary>
  472. protected override void Dispose (bool disposing)
  473. {
  474. if (disposing)
  475. {
  476. // Only if explicitly calling Close or dispose we still have access to
  477. // managed resources.
  478. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
  479. if (connection != null)
  480. {
  481. connection.Dispose();
  482. }
  483. base.Dispose(disposing);
  484. }
  485. }*/
  486. ///<summary>
  487. /// This method checks the connection state to see if the connection
  488. /// is set or it is open. If one of this conditions is not met, throws
  489. /// an InvalidOperationException
  490. ///</summary>
  491. private void CheckConnectionState()
  492. {
  493. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CheckConnectionState");
  494. // Check the connection state.
  495. if (Connector == null || Connector.State != ConnectionState.Open) {
  496. throw new InvalidOperationException(resman.GetString("Exception_ConnectionNotOpen"));
  497. }
  498. }
  499. /// <summary>
  500. /// This method substitutes the <see cref="Npgsql.NpgsqlCommand.Parameters">Parameters</see>, if exist, in the command
  501. /// to their actual values.
  502. /// The parameter name format is <b>:ParameterName</b>.
  503. /// </summary>
  504. /// <returns>A version of <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> with the <see cref="Npgsql.NpgsqlCommand.Parameters">Parameters</see> inserted.</returns>
  505. internal String GetCommandText()
  506. {
  507. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetCommandText");
  508. if (planName == String.Empty)
  509. return GetClearCommandText();
  510. else
  511. return GetPreparedCommandText();
  512. }
  513. private String GetClearCommandText()
  514. {
  515. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetClearCommandText");
  516. String result = text;
  517. if (type == CommandType.StoredProcedure)
  518. if (Connector.SupportsPrepare)
  519. result = "select * from " + result; // This syntax is only available in 7.3+ as well SupportsPrepare.
  520. else
  521. result = "select " + result; // Only a single result return supported. 7.2 and earlier.
  522. else if (type == CommandType.TableDirect)
  523. return "select * from " + result; // There is no parameter support on table direct.
  524. if (parameters == null || parameters.Count == 0)
  525. return result;
  526. //CheckParameters();
  527. for (Int32 i = 0; i < parameters.Count; i++)
  528. {
  529. NpgsqlParameter Param = parameters[i];
  530. // FIXME DEBUG ONLY
  531. // adding the '::<datatype>' on the end of a parameter is a highly
  532. // questionable practice, but it is great for debugging!
  533. result = ReplaceParameterValue(
  534. result,
  535. Param.ParameterName,
  536. Param.TypeInfo.ConvertToBackend(Param.Value, false) + "::" + Param.TypeInfo.Name
  537. );
  538. }
  539. return result;
  540. }
  541. private String GetPreparedCommandText()
  542. {
  543. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetPreparedCommandText");
  544. if (parameters.Count == 0)
  545. return "execute " + planName;
  546. StringBuilder result = new StringBuilder("execute " + planName + '(');
  547. for (Int32 i = 0; i < parameters.Count; i++)
  548. {
  549. result.Append(parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, false) + ',');
  550. }
  551. result = result.Remove(result.Length - 1, 1);
  552. result.Append(')');
  553. return result.ToString();
  554. }
  555. private String GetParseCommandText()
  556. {
  557. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetParseCommandText");
  558. String parseCommand = text;
  559. if (type == CommandType.StoredProcedure)
  560. parseCommand = "select * from " + parseCommand; // This syntax is only available in 7.3+ as well SupportsPrepare.
  561. else if (type == CommandType.TableDirect)
  562. return "select * from " + parseCommand; // There is no parameter support on TableDirect.
  563. if (parameters.Count > 0)
  564. {
  565. // The ReplaceParameterValue below, also checks if the parameter is present.
  566. String parameterName;
  567. Int32 i;
  568. for (i = 0; i < parameters.Count; i++)
  569. {
  570. //result = result.Replace(":" + parameterName, parameters[i].Value.ToString());
  571. parameterName = parameters[i].ParameterName;
  572. //textCommand = textCommand.Replace(':' + parameterName, "$" + (i+1));
  573. parseCommand = ReplaceParameterValue(parseCommand, parameterName, "$" + (i+1));
  574. }
  575. }
  576. return parseCommand;
  577. }
  578. private String GetPrepareCommandText()
  579. {
  580. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetPrepareCommandText");
  581. planName = "NpgsqlPlan" + System.Threading.Interlocked.Increment(ref planIndex);
  582. StringBuilder command = new StringBuilder("prepare " + planName);
  583. String textCommand = text;
  584. if (type == CommandType.StoredProcedure)
  585. textCommand = "select * from " + textCommand;
  586. else if (type == CommandType.TableDirect)
  587. return "select * from " + textCommand; // There is no parameter support on TableDirect.
  588. if (parameters.Count > 0)
  589. {
  590. // The ReplaceParameterValue below, also checks if the parameter is present.
  591. String parameterName;
  592. Int32 i;
  593. for (i = 0; i < parameters.Count; i++)
  594. {
  595. //result = result.Replace(":" + parameterName, parameters[i].Value.ToString());
  596. parameterName = parameters[i].ParameterName;
  597. // The space in front of '$' fixes a parsing problem in 7.3 server
  598. // which gives errors of operator when finding the caracters '=$' in
  599. // prepare text
  600. textCommand = ReplaceParameterValue(textCommand, parameterName, " $" + (i+1));
  601. }
  602. //[TODO] Check if there is any missing parameters in the query.
  603. // For while, an error is thrown saying about the ':' char.
  604. command.Append('(');
  605. for (i = 0; i < parameters.Count; i++)
  606. {
  607. // command.Append(NpgsqlTypesHelper.GetDefaultTypeInfo(parameters[i].DbType));
  608. command.Append(parameters[i].TypeInfo.Name);
  609. command.Append(',');
  610. }
  611. command = command.Remove(command.Length - 1, 1);
  612. command.Append(')');
  613. }
  614. command.Append(" as ");
  615. command.Append(textCommand);
  616. return command.ToString();
  617. }
  618. private String ReplaceParameterValue(String result, String parameterName, String paramVal)
  619. {
  620. Int32 resLen = result.Length;
  621. Int32 paramStart = result.IndexOf(parameterName);
  622. Int32 paramLen = parameterName.Length;
  623. Int32 paramEnd = paramStart + paramLen;
  624. Boolean found = false;
  625. while(paramStart > -1)
  626. {
  627. if((resLen > paramEnd) &&
  628. (result[paramEnd] == ' ' ||
  629. result[paramEnd] == ',' ||
  630. result[paramEnd] == ')' ||
  631. result[paramEnd] == ';' ||
  632. result[paramEnd] == '\n' ||
  633. result[paramEnd] == '\t'))
  634. {
  635. result = result.Substring(0, paramStart) + paramVal + result.Substring(paramEnd);
  636. found = true;
  637. }
  638. else if(resLen == paramEnd)
  639. {
  640. result = result.Substring(0, paramStart)+ paramVal;
  641. found = true;
  642. }
  643. else
  644. break;
  645. resLen = result.Length;
  646. paramStart = result.IndexOf(parameterName, paramStart);
  647. paramEnd = paramStart + paramLen;
  648. }//while
  649. if(!found)
  650. throw new IndexOutOfRangeException (String.Format(resman.GetString("Exception_ParamNotInQuery"), parameterName));
  651. return result;
  652. }//ReplaceParameterValue
  653. private void ExecuteCommand()
  654. {
  655. // Check the connection state first.
  656. CheckConnectionState();
  657. // reset any responses just before getting new ones
  658. connector.Mediator.ResetResponses();
  659. if (parse == null) {
  660. Connector.Query(this);
  661. // Check for errors and/or notifications and do the Right Thing.
  662. connector.CheckErrorsAndNotifications();
  663. }
  664. else
  665. {
  666. try
  667. {
  668. BindParameters();
  669. // Check for errors and/or notifications and do the Right Thing.
  670. connector.CheckErrorsAndNotifications();
  671. connector.Execute(new NpgsqlExecute(bind.PortalName, 0));
  672. // Check for errors and/or notifications and do the Right Thing.
  673. connector.CheckErrorsAndNotifications();
  674. }
  675. finally
  676. {
  677. // As per documentation:
  678. // "[...] When an error is detected while processing any extended-query message,
  679. // the backend issues ErrorResponse, then reads and discards messages until a
  680. // Sync is reached, then issues ReadyForQuery and returns to normal message processing.[...]"
  681. // So, send a sync command if we get any problems.
  682. connector.Sync();
  683. }
  684. }
  685. }
  686. }
  687. }