This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlState.cs
1 // created on 6/14/2002 at 7:56 PM
2
3 // Npgsql.NpgsqlState.cs
4 //
5 // Author:
6 //      Dave Joyner <d4ljoyn@yahoo.com>
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.IO;
30 using System.Net;
31 using System.Net.Sockets;
32 using System.Collections;
33 using System.Text;
34 using System.Resources;
35
36 namespace Npgsql
37 {
38     ///<summary> This class represents the base class for the state pattern design pattern
39     /// implementation.
40     /// </summary>
41     ///
42
43     internal abstract class NpgsqlState
44     {
45         private readonly String CLASSNAME = "NpgsqlState";
46         protected ResourceManager resman = null;
47
48         public virtual void Open(NpgsqlConnection context)
49         {
50             throw new InvalidOperationException("Internal Error! " + this);
51         }
52         public virtual void Startup(NpgsqlConnection context)
53         {
54             throw new InvalidOperationException("Internal Error! " + this);
55         }
56         public virtual void Authenticate(NpgsqlConnection context, string password)
57         {
58             throw new InvalidOperationException("Internal Error! " + this);
59         }
60         public virtual void Query(NpgsqlConnection context, NpgsqlCommand command)
61         {
62             throw new InvalidOperationException("Internal Error! " + this);
63         }
64         public virtual void Ready( NpgsqlConnection context )
65         {
66             throw new InvalidOperationException("Internal Error! " + this);
67         }
68         public virtual void FunctionCall(NpgsqlConnection context, NpgsqlCommand command)
69         {
70             throw new InvalidOperationException("Internal Error! " + this);
71         }
72         public virtual void Parse(NpgsqlConnection context, NpgsqlParse parse)
73         {
74             throw new InvalidOperationException("Internal Error! " + this);
75         }
76         public virtual void Flush(NpgsqlConnection context)
77         {
78             throw new InvalidOperationException("Internal Error! " + this);
79         }
80         public virtual void Sync(NpgsqlConnection context)
81         {
82             throw new InvalidOperationException("Internal Error! " + this);
83         }
84         public virtual void Bind(NpgsqlConnection context, NpgsqlBind bind)
85         {
86             throw new InvalidOperationException("Internal Error! " + this);
87         }
88         public virtual void Execute(NpgsqlConnection context, NpgsqlExecute execute)
89         {
90             throw new InvalidOperationException("Internal Error! " + this);
91         }
92
93         public NpgsqlState()
94         {
95             resman = new ResourceManager(this.GetType());
96         }
97
98         public virtual void Close( NpgsqlConnection context )
99         {
100             /*NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close");
101             if ( context.State == ConnectionState.Open )
102             {
103                 Stream stream = context.Stream;
104                 if ( stream.CanWrite )
105                 {
106                     stream.WriteByte((Byte)'X');
107                     if (context.BackendProtocolVersion >= ProtocolVersion.Version3)
108                         PGUtil.WriteInt32(stream, 4);
109                     stream.Flush();
110                 }
111             }*/
112
113             // CHECKME!!!
114             // The close logic is pretty messed up I think.  Needs lots of work.
115 /*
116             if (! context.Connector.Shared) {
117                 if (context.Connector.Stream != null) {
118                     try {
119                         context.Connector.Stream.Close();
120                     } catch {}
121                 }
122             }
123 */
124             //ChangeState( context, NpgsqlClosedState.Instance );
125         }
126
127         ///<summary>
128         ///This method is used by the states to change the state of the context.
129         /// </summary>
130         protected virtual void ChangeState(NpgsqlConnection context, NpgsqlState newState)
131         {
132             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeState");
133             context.CurrentState = newState;
134         }
135
136         ///<summary>
137         /// This method is responsible to handle all protocol messages sent from the backend.
138         /// It holds all the logic to do it.
139         /// To exchange data, it uses a Mediator object from which it reads/writes information
140         /// to handle backend requests.
141         /// </summary>
142         ///
143         protected virtual void ProcessBackendResponses( NpgsqlConnection context )
144         {
145             // reset any responses just before getting new ones
146             context.Mediator.ResetResponses();
147
148             try {
149                 switch (context.BackendProtocolVersion) {
150                 case ProtocolVersion.Version2 :
151                     ProcessBackendResponses_Ver_2(context);
152                     break;
153
154                 case ProtocolVersion.Version3 :
155                     ProcessBackendResponses_Ver_3(context);
156                     break;
157
158                 }
159             } finally {
160                 // reset expectations right after getting new responses
161                 context.Mediator.ResetExpectations();
162             }
163         }
164
165         protected virtual void ProcessBackendResponses_Ver_2( NpgsqlConnection context )
166         {
167             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");
168
169             BufferedStream      stream = new BufferedStream(context.Stream);
170             NpgsqlMediator mediator = context.Mediator;
171
172             // Often used buffer
173             Byte[] inputBuffer = new Byte[ 4 ];
174
175             Boolean readyForQuery = false;
176
177             while (!readyForQuery)
178             {
179                 // Check the first Byte of response.
180                 switch ( stream.ReadByte() )
181                 {
182                 case NpgsqlMessageTypes_Ver_2.ErrorResponse :
183
184                     {
185                         NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
186                         error.ReadFromStream(stream, context.Encoding);
187
188                         mediator.Errors.Add(error);
189
190                         NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
191                     }
192
193                     // Return imediately if it is in the startup state or connected state as
194                     // there is no more messages to consume.
195                     // Possible error in the NpgsqlStartupState:
196                     //          Invalid password.
197                     // Possible error in the NpgsqlConnectedState:
198                     //          No pg_hba.conf configured.
199
200                     if (! mediator.RequireReadyForQuery) {
201                         return;
202                     }
203
204                     break;
205
206
207                 case NpgsqlMessageTypes_Ver_2.AuthenticationRequest :
208
209                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");
210
211                     {
212                         Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);
213
214                         if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk )
215                         {
216                             NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
217
218                             break;
219                         }
220
221                         if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword )
222                         {
223                             NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);
224
225                             // Send the PasswordPacket.
226
227                             ChangeState( context, NpgsqlStartupState.Instance );
228                             context.Authenticate(context.Password);
229
230                             break;
231                         }
232
233
234                         if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password )
235                         {
236                             NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
237                             // Now do the "MD5-Thing"
238                             // for this the Password has to be:
239                             // 1. md5-hashed with the username as salt
240                             // 2. md5-hashed again with the salt we get from the backend
241
242
243                             MD5 md5 = MD5.Create();
244
245
246                             // 1.
247                             byte[] passwd = context.Encoding.GetBytes(context.Password);
248                             byte[] saltUserName = context.Encoding.GetBytes(context.UserName);
249
250                             byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];
251
252                             passwd.CopyTo(crypt_buf, 0);
253                             saltUserName.CopyTo(crypt_buf, passwd.Length);
254
255
256
257                             StringBuilder sb = new StringBuilder ();
258                             byte[] hashResult = md5.ComputeHash(crypt_buf);
259                             foreach (byte b in hashResult)
260                             sb.Append (b.ToString ("x2"));
261
262
263                             String prehash = sb.ToString();
264
265                             byte[] prehashbytes = context.Encoding.GetBytes(prehash);
266
267
268
269                             byte[] saltServer = new byte[4];
270                             stream.Read(saltServer, 0, 4);
271                             // Send the PasswordPacket.
272                             ChangeState( context, NpgsqlStartupState.Instance );
273
274
275                             // 2.
276
277                             crypt_buf = new byte[prehashbytes.Length + saltServer.Length];
278                             prehashbytes.CopyTo(crypt_buf, 0);
279                             saltServer.CopyTo(crypt_buf, prehashbytes.Length);
280
281                             sb = new StringBuilder ("md5"); // This is needed as the backend expects md5 result starts with "md5"
282                             hashResult = md5.ComputeHash(crypt_buf);
283                             foreach (byte b in hashResult)
284                             sb.Append (b.ToString ("x2"));
285
286                             context.Authenticate(sb.ToString ());
287
288                             break;
289                         }
290
291                         // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
292                         NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
293                         mediator.Errors.Add(String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType));
294                     }
295
296                     return;
297
298                 case NpgsqlMessageTypes_Ver_2.RowDescription:
299                     // This is the RowDescription message.
300                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
301
302                     {
303                         NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
304                         rd.ReadFromStream(stream, context.Encoding);
305
306                         // Initialize the array list which will contain the data from this rowdescription.
307                         mediator.AddRowDescription(rd);
308                     }
309
310                     // Now wait for the AsciiRow messages.
311                     break;
312
313                 case NpgsqlMessageTypes_Ver_2.AsciiRow:
314                     // This is the AsciiRow message.
315                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");
316
317                     {
318                         NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.OidToNameMapping, context.BackendProtocolVersion);
319                         asciiRow.ReadFromStream(stream, context.Encoding);
320
321                         // Add this row to the rows array.
322                         mediator.AddAsciiRow(asciiRow);
323                     }
324
325                     // Now wait for CompletedResponse message.
326                     break;
327
328                 case NpgsqlMessageTypes_Ver_2.BinaryRow:
329                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow");
330
331                     {
332                         NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription);
333                         binaryRow.ReadFromStream(stream, context.Encoding);
334
335                         mediator.AddBinaryRow(binaryRow);
336                     }
337
338                     break;
339
340                 case NpgsqlMessageTypes_Ver_2.ReadyForQuery :
341
342                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");
343                     readyForQuery = true;
344                     ChangeState( context, NpgsqlReadyState.Instance );
345                     break;
346
347                 case NpgsqlMessageTypes_Ver_2.BackendKeyData :
348
349                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
350                     // BackendKeyData message.
351                     NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
352                     backend_keydata.ReadFromStream(stream);
353                     mediator.SetBackendKeydata(backend_keydata);
354
355
356                     // Wait for ReadForQuery message
357                     break;
358                     ;
359
360                 case NpgsqlMessageTypes_Ver_2.NoticeResponse :
361
362                     {
363                         NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
364                         notice.ReadFromStream(stream, context.Encoding);
365
366                         mediator.Notices.Add(notice);
367
368                         NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
369                     }
370
371                     // Wait for ReadForQuery message
372                     break;
373
374                 case NpgsqlMessageTypes_Ver_2.CompletedResponse :
375                     // This is the CompletedResponse message.
376                     // Get the string returned.
377
378
379                     String result = PGUtil.ReadString(stream, context.Encoding);
380
381                     NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result);
382                     // Add result from the processing.
383
384                     mediator.AddCompletedResponse(result);
385
386                     // Now wait for ReadyForQuery message.
387                     break;
388
389                 case NpgsqlMessageTypes_Ver_2.CursorResponse :
390                     // This is the cursor response message.
391                     // It is followed by a C NULL terminated string with the name of
392                     // the cursor in a FETCH case or 'blank' otherwise.
393                     // In this case it should be always 'blank'.
394                     // [FIXME] Get another name for this function.
395                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");
396
397                     String cursorName = PGUtil.ReadString(stream, context.Encoding);
398                     // Continue waiting for ReadyForQuery message.
399                     break;
400
401                 case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse :
402                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
403                     PGUtil.ReadString(stream, context.Encoding);
404                     break;
405
406                 case NpgsqlMessageTypes_Ver_2.NotificationResponse  :
407
408                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");
409
410                     Int32 PID = PGUtil.ReadInt32(stream, inputBuffer);
411                     String notificationResponse = PGUtil.ReadString( stream, context.Encoding );
412                     mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
413
414                     // Wait for ReadForQuery message
415                     break;
416
417                 default :
418                     // This could mean a number of things
419                     //   We've gotten out of sync with the backend?
420                     //   We need to implement this type?
421                     //   Backend has gone insane?
422                     // FIXME
423                     // what exception should we really throw here?
424                     throw new NotSupportedException("Backend sent unrecognized response type");
425
426                 }
427             }
428         }
429
430         protected virtual void ProcessBackendResponses_Ver_3( NpgsqlConnection context )
431         {
432             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");
433
434             BufferedStream      stream = new BufferedStream(context.Stream);
435             NpgsqlMediator mediator = context.Mediator;
436
437             // Often used buffers
438             Byte[] inputBuffer = new Byte[ 4 ];
439             String Str;
440
441             Boolean readyForQuery = false;
442
443             while (!readyForQuery)
444             {
445                 // Check the first Byte of response.
446                 switch ( stream.ReadByte() )
447                 {
448                 case NpgsqlMessageTypes_Ver_3.ErrorResponse :
449
450                     {
451                         NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
452                         error.ReadFromStream(stream, context.Encoding);
453
454                         mediator.Errors.Add(error);
455
456                         NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
457                     }
458
459                     // Return imediately if it is in the startup state or connected state as
460                     // there is no more messages to consume.
461                     // Possible error in the NpgsqlStartupState:
462                     //          Invalid password.
463                     // Possible error in the NpgsqlConnectedState:
464                     //          No pg_hba.conf configured.
465
466                     if (! mediator.RequireReadyForQuery) {
467                         return;
468                     }
469
470                     break;
471
472
473                 case NpgsqlMessageTypes_Ver_3.AuthenticationRequest :
474
475                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");
476
477                     // Eat length
478                     PGUtil.ReadInt32(stream, inputBuffer);
479
480                     {
481                     Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);
482
483                         if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationOk )
484                         {
485                             NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
486
487                             break;
488                         }
489
490                         if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationClearTextPassword )
491                         {
492                             NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);
493
494                             // Send the PasswordPacket.
495
496                             ChangeState( context, NpgsqlStartupState.Instance );
497                             context.Authenticate(context.Password);
498
499                             break;
500                         }
501
502
503                         if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationMD5Password )
504                         {
505                             NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
506                             // Now do the "MD5-Thing"
507                             // for this the Password has to be:
508                             // 1. md5-hashed with the username as salt
509                             // 2. md5-hashed again with the salt we get from the backend
510
511
512                             MD5 md5 = MD5.Create();
513
514
515                             // 1.
516                             byte[] passwd = context.Encoding.GetBytes(context.Password);
517                             byte[] saltUserName = context.Encoding.GetBytes(context.UserName);
518
519                             byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];
520
521                             passwd.CopyTo(crypt_buf, 0);
522                             saltUserName.CopyTo(crypt_buf, passwd.Length);
523
524
525
526                             StringBuilder sb = new StringBuilder ();
527                             byte[] hashResult = md5.ComputeHash(crypt_buf);
528                             foreach (byte b in hashResult)
529                             sb.Append (b.ToString ("x2"));
530
531
532                             String prehash = sb.ToString();
533
534                             byte[] prehashbytes = context.Encoding.GetBytes(prehash);
535
536
537
538                             stream.Read(inputBuffer, 0, 4);
539                             // Send the PasswordPacket.
540                             ChangeState( context, NpgsqlStartupState.Instance );
541
542
543                             // 2.
544
545                             crypt_buf = new byte[prehashbytes.Length + 4];
546                             prehashbytes.CopyTo(crypt_buf, 0);
547                             inputBuffer.CopyTo(crypt_buf, prehashbytes.Length);
548
549                             sb = new StringBuilder ("md5"); // This is needed as the backend expects md5 result starts with "md5"
550                             hashResult = md5.ComputeHash(crypt_buf);
551                             foreach (byte b in hashResult)
552                             sb.Append (b.ToString ("x2"));
553
554                             context.Authenticate(sb.ToString ());
555
556                             break;
557                         }
558
559                         // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
560                         NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
561                         mediator.Errors.Add(String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType));
562                     }
563
564                     return;
565
566                 case NpgsqlMessageTypes_Ver_3.RowDescription:
567                     // This is the RowDescription message.
568                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
569                     {
570                         NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
571                         rd.ReadFromStream(stream, context.Encoding);
572
573                         mediator.AddRowDescription(rd);
574                     }
575
576                     // Now wait for the AsciiRow messages.
577                     break;
578
579                 case NpgsqlMessageTypes_Ver_3.DataRow:
580                     // This is the AsciiRow message.
581                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow");
582                     {
583                         NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.OidToNameMapping, context.BackendProtocolVersion);
584                         asciiRow.ReadFromStream(stream, context.Encoding);
585
586                         // Add this row to the rows array.
587                         mediator.AddAsciiRow(asciiRow);
588                     }
589
590                     // Now wait for CompletedResponse message.
591                     break;
592
593                 case NpgsqlMessageTypes_Ver_3.ReadyForQuery :
594
595                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");
596
597                     // Possible status bytes returned:
598                     //   I = Idle (no transaction active).
599                     //   T = In transaction, ready for more.
600                     //   E = Error in transaction, queries will fail until transaction aborted.
601                     // Just eat the status byte, we have no use for it at this time.
602                     PGUtil.ReadInt32(stream, inputBuffer);
603                     PGUtil.ReadString(stream, context.Encoding, 1);
604
605                     readyForQuery = true;
606                     ChangeState( context, NpgsqlReadyState.Instance );
607
608                     break;
609
610                 case NpgsqlMessageTypes_Ver_3.BackendKeyData :
611
612                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
613                     // BackendKeyData message.
614                     NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
615                     backend_keydata.ReadFromStream(stream);
616                     mediator.SetBackendKeydata(backend_keydata);
617
618
619                     // Wait for ReadForQuery message
620                     break;
621
622                 case NpgsqlMessageTypes_Ver_3.NoticeResponse :
623
624                     // Notices and errors are identical except that we
625                     // just throw notices away completely ignored.
626                     {
627                         NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
628                         notice.ReadFromStream(stream, context.Encoding);
629
630                         mediator.Notices.Add(notice);
631
632                         NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
633                     }
634
635                     // Wait for ReadForQuery message
636                     break;
637
638                 case NpgsqlMessageTypes_Ver_3.CompletedResponse :
639                     // This is the CompletedResponse message.
640                     // Get the string returned.
641
642                     PGUtil.ReadInt32(stream, inputBuffer);
643                     Str = PGUtil.ReadString(stream, context.Encoding);
644
645                     NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, Str);
646
647                     // Add result from the processing.
648                     mediator.AddCompletedResponse(Str);
649
650                     break;
651
652                 case NpgsqlMessageTypes_Ver_3.ParseComplete :
653                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
654                     // Just read up the message length.
655                     PGUtil.ReadInt32(stream, inputBuffer);
656                     readyForQuery = true;
657                     break;
658
659                 case NpgsqlMessageTypes_Ver_3.BindComplete :
660                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
661                     // Just read up the message length.
662                     PGUtil.ReadInt32(stream, inputBuffer);
663                     readyForQuery = true;
664                     break;
665
666                 case NpgsqlMessageTypes_Ver_3.EmptyQueryResponse :
667                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
668                     PGUtil.ReadInt32(stream, inputBuffer);
669                     break;
670
671                 case NpgsqlMessageTypes_Ver_3.NotificationResponse  :
672                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");
673
674                     // Eat the length
675                     PGUtil.ReadInt32(stream, inputBuffer);
676                     {
677                         // Process ID sending notification
678                         Int32 PID = PGUtil.ReadInt32(stream, inputBuffer);
679                         // Notification string
680                         String notificationResponse = PGUtil.ReadString( stream, context.Encoding );
681                         // Additional info, currently not implemented by PG (empty string always), eat it
682                         PGUtil.ReadString( stream, context.Encoding );
683                         mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
684                     }
685
686                     // Wait for ReadForQuery message
687                     break;
688
689                 case NpgsqlMessageTypes_Ver_3.ParameterStatus :
690                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
691                     NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus();
692                     parameterStatus.ReadFromStream(stream, context.Encoding);
693
694                     NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue);
695
696                     mediator.AddParameterStatus(parameterStatus.Parameter, parameterStatus);
697
698                     if (parameterStatus.Parameter == "server_version") {
699                         // Add this one under our own name so that if the parameter name
700                         // changes in a future backend version, we can handle it here in the 
701                         // protocol handler and leave everybody else put of it.
702                         mediator.AddParameterStatus("__npgsql_server_version", parameterStatus);
703 //                        context.ServerVersionString = parameterStatus.ParameterValue;
704                     }
705
706                     break;
707
708                 default :
709                     // This could mean a number of things
710                     //   We've gotten out of sync with the backend?
711                     //   We need to implement this type?
712                     //   Backend has gone insane?
713                     // FIXME
714                     // what exception should we really throw here?
715                     throw new NotSupportedException("Backend sent unrecognized response type");
716
717                 }
718             }
719         }
720     }
721 }