2009-05-13 Atsushi Enomoto <atsushi@ximian.com>
[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         private byte[] _inputBuffer;
48         private char[] _chars;
49
50         public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, ProtocolVersion protocolVersion, byte[] inputBuffer, char[] chars)
51                 : base(rowDesc, protocolVersion)
52         {
53             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
54             _inputBuffer = inputBuffer;
55             _chars = chars;
56         }
57
58         public override void ReadFromStream(Stream inputStream, Encoding encoding)
59         {
60             switch (protocol_version)
61             {
62             case ProtocolVersion.Version2 :
63                 ReadFromStream_Ver_2(inputStream, encoding);
64                 break;
65
66             case ProtocolVersion.Version3 :
67                 ReadFromStream_Ver_3(inputStream, encoding);
68                 break;
69
70             }
71         }
72
73         private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding)
74         {
75             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
76
77             Byte[]       null_map_array = new Byte[(row_desc.NumFields + 7)/8];
78
79             Array.Clear(null_map_array, 0, null_map_array.Length);
80
81
82             // Decoders used to get decoded chars when using unicode like encodings which may have chars crossing the byte buffer bounds.
83
84             Decoder decoder = encoding.GetDecoder();
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, _inputBuffer, 0, 4);
102
103                 NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
104                 Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(_inputBuffer, 0));
105                 field_value_size -= 4;
106
107                                 string result = ReadStringFromStream(inputStream, field_value_size, decoder);
108                 // Add them to the AsciiRow data.
109                 data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result, field_descr.type_size, field_descr.type_modifier));
110
111             }
112         }
113
114         private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding)
115         {
116             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
117
118             PGUtil.ReadInt32(inputStream, _inputBuffer);
119             Int16 numCols = PGUtil.ReadInt16(inputStream, _inputBuffer);
120
121             Decoder decoder = encoding.GetDecoder();
122
123                         for (Int16 field_count = 0; field_count < numCols; field_count++)
124                         {
125                                 Int32 field_value_size = PGUtil.ReadInt32(inputStream, _inputBuffer);
126
127                                 // Check if this field is null
128                                 if (field_value_size == -1) // Null value
129                                 {
130                                         data.Add(DBNull.Value);
131                                         continue;
132                                 }
133
134                                 NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
135     
136                                 if (row_desc[field_count].format_code == FormatCode.Text)
137                                 {
138                                         string result = ReadStringFromStream(inputStream, field_value_size, decoder);
139                                         // Add them to the AsciiRow data.
140                                         data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result, field_descr.type_size, field_descr.type_modifier));
141                                 }
142                                 else
143                                 {
144                     Byte[] binary_data = ReadBytesFromStream(inputStream, field_value_size);
145
146                     data.Add(NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.type_info, binary_data, encoding,field_value_size, field_descr.type_modifier));
147                                 }
148             }
149         }
150
151         // Using the given null field map (provided by the backend),
152         // determine if the given field index is mapped null by the backend.
153         // We only need to do this for version 2 protocol.
154         private static Boolean IsBackendNull(Byte[] null_map_array, Int32 index)
155         {
156             // Get the byte that holds the bit index position.
157             Byte test_byte = null_map_array[index/8];
158
159             // Now, check if index bit is set.
160             // To do this, get its position in the byte, shift to
161             // MSB and test it with the byte 10000000.
162             return (((test_byte << (index%8)) & 0x80) == 0);
163         }
164
165                 private int GetCharsFromStream(Stream inputStream, int count, Decoder decoder, char[] chars)
166                 {
167                         // Now, read just the field value.
168                         PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, count);
169                         int charCount = decoder.GetCharCount(_inputBuffer, 0, count);
170                         decoder.GetChars(_inputBuffer, 0, count, chars, 0);
171                         return charCount;
172                 }
173
174                 private string ReadStringFromStream(Stream inputStream, int field_value_size, Decoder decoder)
175                 {
176                         int bytes_left = field_value_size;
177                         int charCount;
178
179                         if (field_value_size > _inputBuffer.Length)
180                         {
181                                 StringBuilder   result = new StringBuilder();
182
183                                 while (bytes_left > READ_BUFFER_SIZE)
184                                 {
185                                         charCount = GetCharsFromStream(inputStream, READ_BUFFER_SIZE, decoder, _chars);
186                                         result.Append(_chars, 0,charCount);
187                                         bytes_left -= READ_BUFFER_SIZE;
188                                 }
189
190                                 charCount = GetCharsFromStream(inputStream, bytes_left, decoder, _chars);
191                                 result.Append(_chars, 0,charCount);
192
193                                 return result.ToString();
194                         }
195                         else
196                         {
197                                 charCount = GetCharsFromStream(inputStream, bytes_left, decoder, _chars);
198
199                                 return new String(_chars, 0,charCount);
200                         }
201                 }
202   
203         private byte[] ReadBytesFromStream(Stream inputStream, int field_value_size)
204         {
205             byte[] binary_data = new byte[field_value_size];
206             int bytes_left = field_value_size;
207             if (field_value_size > _inputBuffer.Length)
208             {
209                 int i=0;
210                 while (bytes_left > READ_BUFFER_SIZE)
211                 {
212                     PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, READ_BUFFER_SIZE);
213                     _inputBuffer.CopyTo(binary_data, i*READ_BUFFER_SIZE);
214                     i++;
215                     bytes_left -= READ_BUFFER_SIZE;
216                 }
217             }
218             PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, bytes_left);
219             Int32 offset = field_value_size - bytes_left;
220             Array.Copy(_inputBuffer, 0, binary_data, offset, bytes_left);
221             return binary_data;
222         }
223     }
224 }