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