Fix operator != handling of LHS null
[mono.git] / mcs / class / Npgsql / Npgsql / PGUtil.cs
1 // created on 1/6/2002 at 22:27
2
3 // Npgsql.PGUtil.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.Collections;
29 using System.IO;
30 using System.Text;
31 using System.Net.Sockets;
32 using System.Net;
33 using System.Resources;
34
35 namespace Npgsql
36 {
37     /// <summary>
38     /// Represent the frontend/backend protocol version.
39     /// </summary>
40     public enum ProtocolVersion
41     {
42         Unknown,
43         Version2,
44         Version3
45     }
46
47     /// <summary>
48     /// Represent the backend server version.
49     /// </summary>
50     public sealed class ServerVersion
51     {
52         public static readonly Int32 ProtocolVersion2 = 2 << 16; // 131072
53         public static readonly Int32 ProtocolVersion3 = 3 << 16; // 196608
54
55         private Int32   _Major;
56         private Int32   _Minor;
57         private Int32   _Patch;
58
59         internal ServerVersion(Int32 Major, Int32 Minor, Int32 Patch)
60         {
61             _Major = Major;
62             _Minor = Minor;
63             _Patch = Patch;
64         }
65
66         /// <summary>
67         /// Server version major number.
68         /// </summary>
69         public Int32 Major
70         { get { return _Major; } }
71
72         /// <summary>
73         /// Server version minor number.
74         /// </summary>
75         public Int32 Minor
76         { get { return _Minor; } }
77
78         /// <summary>
79         /// Server version patch level number.
80         /// </summary>
81         public Int32 Patch
82         { get { return _Patch; } }
83
84         public static bool operator == (ServerVersion One, ServerVersion TheOther)
85         {
86             if (((Object)One == null) || ((Object)TheOther == null))
87                  return false;
88             return
89               One._Major == TheOther._Major &&
90               One._Minor == TheOther._Minor &&
91               One._Patch == TheOther._Patch;
92         }
93
94         public static bool operator != (ServerVersion One, ServerVersion TheOther)
95         {
96             if (((Object)One == null) || ((Object)TheOther == null))
97                 return true;
98             
99             return ! (One == TheOther);
100         }
101
102         public static bool operator > (ServerVersion One, ServerVersion TheOther)
103         {
104             return
105                 (One._Major > TheOther._Major) ||
106                 (One._Major == TheOther._Major && One._Minor > TheOther._Minor) ||
107                 (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch > TheOther._Patch);
108         }
109
110         public static bool operator >= (ServerVersion One, ServerVersion TheOther)
111         {
112             return
113                 (One._Major > TheOther._Major) ||
114                 (One._Major == TheOther._Major && One._Minor > TheOther._Minor) ||
115                 (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch >= TheOther._Patch);
116         }
117
118         public static bool operator < (ServerVersion One, ServerVersion TheOther)
119         {
120             return
121                 (One._Major < TheOther._Major) ||
122                 (One._Major == TheOther._Major && One._Minor < TheOther._Minor) ||
123                 (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch < TheOther._Patch);
124         }
125
126         public static bool operator <= (ServerVersion One, ServerVersion TheOther)
127         {
128             return
129                 (One._Major < TheOther._Major) ||
130                 (One._Major == TheOther._Major && One._Minor < TheOther._Minor) ||
131                 (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch <= TheOther._Patch);
132         }
133
134         public override bool Equals(object O)
135         {
136             if (O == null)
137                 return false;
138             
139             return (O.GetType() == this.GetType() && this == (ServerVersion)O);
140         }
141
142         public override int GetHashCode()
143         {
144             return _Major ^ _Minor ^ _Patch;
145         }
146
147         /// <summary>
148         /// Returns the string representation of this version in three place dot notation (Major.Minor.Patch).
149         /// </summary>
150         public override String ToString()
151         {
152             return string.Format("{0}.{1}.{2}", _Major, _Minor, _Patch);
153         }
154     }
155
156     internal enum FormatCode:
157     short
158     {
159         Text = 0,
160         Binary = 1
161     }
162
163     ///<summary>
164     /// This class provides many util methods to handle
165     /// reading and writing of PostgreSQL protocol messages.
166     /// </summary>
167     internal abstract class PGUtil
168     {
169         // Logging related values
170         private static readonly String CLASSNAME = "PGUtil";
171         private static ResourceManager resman = new ResourceManager(typeof(PGUtil));
172
173         ///<summary>
174         /// This method takes a ProtocolVersion and returns an integer
175         /// version number that the Postgres backend will recognize in a
176         /// startup packet.
177         /// </summary>
178         public static Int32 ConvertProtocolVersion(ProtocolVersion Ver)
179         {
180             switch (Ver) {
181             case ProtocolVersion.Version2 :
182                 return ServerVersion.ProtocolVersion2;
183
184             case ProtocolVersion.Version3 :
185                 return ServerVersion.ProtocolVersion3;
186
187             }
188
189             // CHECKME
190             // should we throw?
191             return 0;
192         }
193
194         /// <summary>
195         /// This method takes a version string as returned by SELECT VERSION() and returns
196         /// a valid version string ("7.2.2" for example).
197         /// This is only needed when running protocol version 2.
198         /// This does not do any validity checks.
199         /// </summary>
200         public static string ExtractServerVersion (string VersionString)
201         {
202             Int32               Start = 0, End = 0;
203
204             // find the first digit and assume this is the start of the version number
205             for ( ; Start < VersionString.Length && ! char.IsDigit(VersionString[Start]) ; Start++);
206
207             End = Start;
208
209             // read until hitting whitespace, which should terminate the version number
210             for ( ; End < VersionString.Length && ! char.IsWhiteSpace(VersionString[End]) ; End++);
211
212             return VersionString.Substring(Start, End - Start + 1);
213         }
214
215         /// <summary>
216         /// This method takes a version string ("7.4.1" for example) and produces
217         /// the required integer version numbers (7, 4, and 1).
218         /// </summary>
219         public static ServerVersion ParseServerVersion (string VersionString)
220         {
221             String[]        Parts;
222
223             Parts = VersionString.Split('.');
224
225             if (Parts.Length < 2) {
226                 throw new FormatException(String.Format("Internal: Backend sent bad version string: {0}", VersionString));
227             }
228
229             try {
230                 if (Parts.Length == 2) {
231                     // Coerce it into a 3-part version.
232                     return new ServerVersion(ConvertBeginToInt32(Parts[0]), ConvertBeginToInt32(Parts[1]), 0);
233                 } else {
234                     // If there are more than 3 parts, just ignore the extras, rather than rejecting it.
235                     return new ServerVersion(ConvertBeginToInt32(Parts[0]), ConvertBeginToInt32(Parts[1]), ConvertBeginToInt32(Parts[2]));
236                 }
237             } catch (Exception E) {
238                 throw new FormatException(String.Format("Internal: Backend sent bad version string: {0}", VersionString), E);
239             }
240         }
241
242         /// <summary>
243         /// Convert the beginning numeric part of the given string to Int32.
244         /// For example:
245         ///   Strings "12345ABCD" and "12345.54321" would both be converted to int 12345.
246         /// </summary>
247         private static Int32 ConvertBeginToInt32(String Raw)
248         {
249             Int32         Length = 0;
250             for ( ; Length < Raw.Length && Char.IsNumber(Raw[Length]) ; Length++);
251             return Convert.ToInt32(Raw.Substring(0, Length));
252         }
253
254         ///<summary>
255         /// This method gets a C NULL terminated string from the network stream.
256         /// It keeps reading a byte in each time until a NULL byte is returned.
257         /// It returns the resultant string of bytes read.
258         /// This string is sent from backend.
259         /// </summary>
260         public static String ReadString(Stream network_stream, Encoding encoding)
261         {
262             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadString");
263
264             ArrayList     buffer = new ArrayList();
265             Byte          b;
266             String        string_read;
267
268             // [FIXME] Is this cast always safe?
269             b = (Byte)network_stream.ReadByte();
270             while(b != 0)
271             {
272                 buffer.Add(b);
273                 b = (Byte)network_stream.ReadByte();
274             }
275
276             string_read = encoding.GetString((Byte[])buffer.ToArray(typeof(Byte)));
277             NpgsqlEventLog.LogMsg(resman, "Log_StringRead", LogLevel.Debug, string_read);
278
279             return string_read;
280         }
281
282         ///<summary>
283         /// This method gets a length terminated string from a network stream.
284         /// It returns the resultant string of bytes read.
285         /// This string is sent from backend.
286         /// </summary>
287         public static String ReadString(Stream network_stream, Encoding encoding, Int32 length)
288         {
289             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadString");
290
291             ArrayList     buffer = new ArrayList();
292             Byte          b;
293             String        string_read;
294
295             for (int C = 0 ; C < length ; C++)
296             {
297                 // [FIXME] Is this cast always safe?
298                 b = (Byte)network_stream.ReadByte();
299                 buffer.Add(b);
300             }
301
302             string_read = encoding.GetString((Byte[])buffer.ToArray(typeof(Byte)));
303             NpgsqlEventLog.LogMsg(resman, "Log_StringRead", LogLevel.Debug, string_read);
304
305             return string_read;
306         }
307
308         ///<summary>
309         /// This method writes a C NULL terminated string to the network stream.
310         /// It appends a NULL terminator to the end of the String.
311         /// </summary>
312         public static void WriteString(String the_string, Stream network_stream, Encoding encoding)
313         {
314             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteString");
315             
316             NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, the_string);
317
318             network_stream.Write(encoding.GetBytes(the_string + '\x00') , 0, encoding.GetByteCount(the_string) + 1);
319         }
320
321         ///<summary>
322         /// This method writes a C NULL terminated string limited in length to the
323         /// backend server.
324         /// It pads the string with null bytes to the size specified.
325         /// </summary>
326         public static void WriteLimString(String the_string, Int32 n, Stream network_stream, Encoding encoding)
327         {
328             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteLimString");
329
330             // [FIXME] Parameters should be validated. And what about strings
331             // larger than or equal to n?
332
333             // Pad the string to the specified value.
334             String string_padded = the_string.PadRight(n, '\x00');
335
336             network_stream.Write(encoding.GetBytes(string_padded), 0, n);
337         }
338
339         public static void CheckedStreamRead(Stream stream, Byte[] buffer, Int32 offset, Int32 size)
340         {
341             Int32 bytes_from_stream = 0;
342             Int32 total_bytes_read = 0;
343
344             do
345             {
346                 bytes_from_stream = stream.Read(buffer, offset + total_bytes_read, size);
347                 total_bytes_read += bytes_from_stream;
348                 size -= bytes_from_stream;
349             }
350             while(size > 0);
351         }
352
353
354         /// <summary>
355         /// Write a 32-bit integer to the given stream in the correct byte order.
356         /// </summary>
357         public static void WriteInt32(Stream stream, Int32 value)
358         {
359             stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)), 0, 4);
360         }
361
362         /// <summary>
363         /// Read a 32-bit integer from the given stream in the correct byte order.
364         /// </summary>
365         public static Int32 ReadInt32(Stream stream, Byte[] buffer)
366         {
367             CheckedStreamRead(stream, buffer, 0, 4);
368             return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, 0));
369
370         }
371
372         /// <summary>
373         /// Write a 16-bit integer to the given stream in the correct byte order.
374         /// </summary>
375         public static void WriteInt16(Stream stream, Int16 value)
376         {
377             stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)), 0, 2);
378         }
379
380         /// <summary>
381         /// Read a 16-bit integer from the given stream in the correct byte order.
382         /// </summary>
383         public static Int16 ReadInt16(Stream stream, Byte[] buffer)
384         {
385             CheckedStreamRead(stream, buffer, 0, 2);
386             return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, 0));
387
388         }
389     }
390 }