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