2003-10-15: 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 Byte[]                                                          null_map_array;
49                 private Int16                                                                   num_fields;
50                 private readonly Int16  READ_BUFFER_SIZE = 300; //[FIXME] Is this enough??
51                 private NpgsqlRowDescription row_desc;
52                 private Hashtable                                                       oid_to_name_mapping;
53           private Int32                 protocol_version; 
54                 
55                 
56                 
57                 public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, Hashtable oidToNameMapping, Int32 protocolVersion)
58                 {
59                   NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
60                                                                 
61                         data = new ArrayList();
62                         row_desc = rowDesc;
63                         null_map_array = new Byte[(row_desc.NumFields + 7)/8];
64                         oid_to_name_mapping = oidToNameMapping;
65                   protocol_version = protocolVersion;
66                   
67                         //num_fields = numFields;
68                                 
69                         
70                 }
71                 
72                 
73                 public void ReadFromStream(Stream inputStream, Encoding encoding)
74                 {
75                   NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream()");
76                   
77                         Byte[] input_buffer = new Byte[READ_BUFFER_SIZE]; 
78                         
79                         
80                         if (protocol_version == ProtocolVersion.Version2)
81                         {
82                         
83                         Array.Clear(null_map_array, 0, null_map_array.Length);
84                         
85                         // Read the null fields bitmap.
86                         PGUtil.CheckedStreamRead(inputStream, null_map_array, 0, null_map_array.Length );
87                         
88                         // Get the data.
89                         for (Int16 field_count = 0; field_count < row_desc.NumFields; field_count++)
90                         {
91                                 
92                                 // Check if this field isn't null
93                                 if (IsNull(field_count))
94                                 {
95                                         // Field is null just keep next field.
96                                         
97                                         //[FIXME] See this[] method.
98                                         data.Add(null);
99                                         continue;
100                                 }
101                                 
102                                 // Read the first data of the first row.
103                                                                 
104                                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, 4);
105                                                                 
106                                 Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
107                                 field_value_size -= 4;                  
108                                 Int32 bytes_left = field_value_size;
109                                 
110                                 StringBuilder result = new StringBuilder();
111                                 
112                                 while (bytes_left > READ_BUFFER_SIZE)
113                                 {
114                                         // Now, read just the field value.
115                                         PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
116                                         
117                                         // Read the bytes as string.
118                                         result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
119                                                                         
120                                         bytes_left -= READ_BUFFER_SIZE;
121                                 }
122                                 
123                                 // Now, read just the field value.
124                                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
125                                 
126                                 // Read the bytes as string.
127                                 result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
128                                 
129                                 
130                                 // Add them to the AsciiRow data.
131                                 data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(oid_to_name_mapping, result.ToString(), row_desc[field_count].type_oid, row_desc[field_count].type_modifier));
132                                 
133                         }
134                         }
135                         else
136                         {
137                                                   
138                           PGUtil.ReadInt32(inputStream, input_buffer);
139                           Int16 numCols = PGUtil.ReadInt16(inputStream, input_buffer);
140                           
141                           for (Int16 field_count = 0; field_count < numCols; field_count++)
142                           {
143                             Int32 field_value_size = PGUtil.ReadInt32(inputStream, input_buffer);
144                             
145                             if (field_value_size == -1) // Null value
146                             {
147                               // Field is null just keep next field.
148                                         
149                                         //[FIXME] See this[] method.
150                                         data.Add(null);
151                                         continue;
152                               
153                             }
154                                 Int32 bytes_left = field_value_size;
155                                 
156                                 StringBuilder result = new StringBuilder();
157                                 
158                                 while (bytes_left > READ_BUFFER_SIZE)
159                                 {
160                                         // Now, read just the field value.
161                                         PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
162                                         
163                                         // Read the bytes as string.
164                                         result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
165                                                                         
166                                         bytes_left -= READ_BUFFER_SIZE;
167                                 }
168                                 
169                                 // Now, read just the field value.
170                                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
171                                 
172                                 if (row_desc[field_count].format_code == FormatCode.Text) 
173                                 {
174                                   // Read the bytes as string.
175                                   result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
176                                   // Add them to the AsciiRow data.
177                                   data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(oid_to_name_mapping, result.ToString(), row_desc[field_count].type_oid, row_desc[field_count].type_modifier));
178                                 }
179                                 else
180                                   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));
181                           }
182                           
183                         }
184                         
185                 }
186                 
187                 
188                 public Boolean IsNull(Int32 index)
189                 {
190                   
191                   NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsNull", index);
192                         // [FIXME] Check more optimized way of doing this.
193                         // Should this be public or internal?
194                         
195                         // Check valid index range.
196                         if ((index < 0) || (index >= row_desc.NumFields))
197                                         throw new ArgumentOutOfRangeException("index");
198                         
199                         // Check if the value (index) of the field is null 
200                         
201                         // Get the byte that holds the bit index position.
202                         Byte test_byte = null_map_array[index/8];
203                         
204                         // Now, check if index bit is set.
205                         // To this, get its position in the byte, shift to 
206                         // MSB and test it with the byte 10000000.
207         return (((test_byte << (index%8)) & 0x80) == 0);
208                 }
209                         
210                 
211                 public Object this[Int32 index]
212                 {
213                         get
214                         {
215                                 
216                                 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, index);
217                           
218                                 if ((index < 0) || (index >= row_desc.NumFields))
219                                         throw new ArgumentOutOfRangeException("this[] index value");
220                                 // [FIXME] Should return null or something else
221                                 // more meaningful?
222                                 
223                                 //[FIXME] This code assumes that the data arraylist has the null and non null values
224                                 // in order, but just the non-null values are added. 
225                                 // It is necessary to map the index value with the elements in the array list.
226                                 // For now, the workaround is to insert the null values in the array list. 
227                                 // But this is a hack. :)
228                                 
229                                 //return (IsNull(index) ? null : data[index]);
230                                 return data[index];
231                                 
232                                 
233                                 
234                         }
235                 }
236         }
237         
238 }