2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlAsciiRow.cs
1 // created on 13/6/2002 at 21:06
2
3 // Npgsql.NpgsqlAsciiRow.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 using System;
27 using System.Collections;
28 using System.IO;
29 using System.Text;
30 using System.Net;
31
32 using NpgsqlTypes;
33
34 namespace Npgsql
35 {
36
37     /// <summary>
38     /// This class represents the AsciiRow (version 2) and DataRow (version 3+)
39     /// message sent from the PostgreSQL server.
40     /// </summary>
41     internal sealed class NpgsqlAsciiRow : NpgsqlRow
42     {
43         // Logging related values
44         private static readonly String CLASSNAME = "NpgsqlAsciiRow";
45
46         private readonly Int16        READ_BUFFER_SIZE = 300; //[FIXME] Is this enough??
47
48         public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, ProtocolVersion protocolVersion)
49                 : base(rowDesc, protocolVersion)
50         {
51             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
52         }
53
54         public override void ReadFromStream(Stream inputStream, Encoding encoding)
55         {
56             switch (protocol_version)
57             {
58             case ProtocolVersion.Version2 :
59                 ReadFromStream_Ver_2(inputStream, encoding);
60                 break;
61
62             case ProtocolVersion.Version3 :
63                 ReadFromStream_Ver_3(inputStream, encoding);
64                 break;
65
66             }
67         }
68
69         private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding)
70         {
71             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
72
73             Byte[]       input_buffer = new Byte[READ_BUFFER_SIZE];
74             Byte[]       null_map_array = new Byte[(row_desc.NumFields + 7)/8];
75
76             Array.Clear(null_map_array, 0, null_map_array.Length);
77
78
79             // Decoders used to get decoded chars when using unicode like encodings which may have chars crossing the byte buffer bounds.
80
81             Decoder decoder = encoding.GetDecoder();
82             Char[] chars = null;
83             Int32 charCount;
84
85
86             // Read the null fields bitmap.
87             PGUtil.CheckedStreamRead(inputStream, null_map_array, 0, null_map_array.Length );
88
89             // Get the data.
90             for (Int16 field_count = 0; field_count < row_desc.NumFields; field_count++)
91             {
92                 // Check if this field is null
93                 if (IsBackendNull(null_map_array, field_count))
94                 {
95                     data.Add(DBNull.Value);
96                     continue;
97                 }
98
99                 // Read the first data of the first row.
100
101                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, 4);
102
103                 NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
104                 Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
105                 field_value_size -= 4;
106                 Int32 bytes_left = field_value_size;
107
108                 StringBuilder result = new StringBuilder();
109
110                 while (bytes_left > READ_BUFFER_SIZE)
111                 {
112                     // Now, read just the field value.
113                     PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
114
115                     charCount = decoder.GetCharCount(input_buffer, 0, READ_BUFFER_SIZE);
116
117                     chars = new Char[charCount];
118
119                     decoder.GetChars(input_buffer, 0, READ_BUFFER_SIZE, chars, 0);
120
121                     result.Append(new String(chars));
122
123                     bytes_left -= READ_BUFFER_SIZE;
124                 }
125
126                 // Now, read just the field value.
127                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
128
129
130                 charCount = decoder.GetCharCount(input_buffer, 0, bytes_left);
131                 chars = new Char[charCount];
132                 decoder.GetChars(input_buffer, 0, bytes_left, chars, 0);
133
134                 result.Append(new String(chars));
135
136
137                 // Add them to the AsciiRow data.
138                 data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result.ToString(), field_descr.type_size, field_descr.type_modifier));
139
140             }
141         }
142
143         private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding)
144         {
145             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
146
147             Byte[] input_buffer = new Byte[READ_BUFFER_SIZE];
148
149             PGUtil.ReadInt32(inputStream, input_buffer);
150             Int16 numCols = PGUtil.ReadInt16(inputStream, input_buffer);
151
152             Decoder decoder = encoding.GetDecoder();
153             Char[] chars = null;
154             Int32 charCount;
155
156             for (Int16 field_count = 0; field_count < numCols; field_count++)
157             {
158                 Int32 field_value_size = PGUtil.ReadInt32(inputStream, input_buffer);
159
160                 // Check if this field is null
161                 if (field_value_size == -1) // Null value
162                 {
163                     data.Add(DBNull.Value);
164                     continue;
165                 }
166
167                 NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
168                 Int32           bytes_left = field_value_size;
169                 StringBuilder   result = new StringBuilder();
170
171                 while (bytes_left > READ_BUFFER_SIZE)
172                 {
173
174                     // Now, read just the field value.
175                     PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
176
177                     // Read the bytes as string.
178                     //result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
179                     charCount = decoder.GetCharCount(input_buffer, 0, READ_BUFFER_SIZE);
180
181                     chars = new Char[charCount];
182
183                     decoder.GetChars(input_buffer, 0, READ_BUFFER_SIZE, chars, 0);
184
185                     result.Append(new String(chars));
186
187                     bytes_left -= READ_BUFFER_SIZE;
188
189                     // Now, read just the field value.
190                     /*PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
191
192                     // Read the bytes as string.
193                     result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
194
195                     bytes_left -= READ_BUFFER_SIZE;*/
196                 }
197
198                 // Now, read just the field value.
199                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
200
201                 if (row_desc[field_count].format_code == FormatCode.Text)
202                 {
203                     // Read the bytes as string.
204                     //result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
205
206
207                     charCount = decoder.GetCharCount(input_buffer, 0, bytes_left);
208                     chars = new Char[charCount];
209                     decoder.GetChars(input_buffer, 0, bytes_left, chars, 0);
210
211                     result.Append(new String(chars));
212
213                     // Add them to the AsciiRow data.
214                     data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result.ToString(), field_descr.type_size, field_descr.type_modifier));
215
216                 }
217                 else
218                     // FIXME: input_buffer isn't holding all the field value. This code isn't handling binary data correctly.
219                     data.Add(NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.type_info, input_buffer, encoding, field_value_size, field_descr.type_modifier));
220             }
221         }
222
223         // Using the given null field map (provided by the backend),
224         // determine if the given field index is mapped null by the backend.
225         // We only need to do this for version 2 protocol.
226         private static Boolean IsBackendNull(Byte[] null_map_array, Int32 index)
227         {
228             // Get the byte that holds the bit index position.
229             Byte test_byte = null_map_array[index/8];
230
231             // Now, check if index bit is set.
232             // To do this, get its position in the byte, shift to
233             // MSB and test it with the byte 10000000.
234             return (((test_byte << (index%8)) & 0x80) == 0);
235         }
236     }
237 }