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