This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[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 (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 Hashtable             oid_to_name_mapping;
48
49         public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, Hashtable oidToNameMapping, ProtocolVersion protocolVersion)
50         : base(rowDesc, protocolVersion)
51         {
52             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
53
54             oid_to_name_mapping = oidToNameMapping;
55         }
56
57         public override void ReadFromStream(Stream inputStream, Encoding encoding)
58         {
59             switch (protocol_version) {
60             case ProtocolVersion.Version2 :
61                 ReadFromStream_Ver_2(inputStream, encoding);
62                 break;
63
64             case ProtocolVersion.Version3 :
65                 ReadFromStream_Ver_3(inputStream, encoding);
66                 break;
67
68             }
69         }
70
71         private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding)
72         {
73             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
74
75             Byte[]       input_buffer = new Byte[READ_BUFFER_SIZE];
76             Byte[]       null_map_array = new Byte[(row_desc.NumFields + 7)/8];
77
78             Array.Clear(null_map_array, 0, null_map_array.Length);
79
80             // Read the null fields bitmap.
81             PGUtil.CheckedStreamRead(inputStream, null_map_array, 0, null_map_array.Length );
82
83             // Get the data.
84             for (Int16 field_count = 0; field_count < row_desc.NumFields; field_count++)
85             {
86                 // Check if this field is null
87                 if (IsBackendNull(null_map_array, field_count))
88                 {
89                     data.Add(DBNull.Value);
90                     continue;
91                 }
92
93                 // Read the first data of the first row.
94
95                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, 4);
96
97                 Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
98                 field_value_size -= 4;
99                 Int32 bytes_left = field_value_size;
100
101                 StringBuilder result = new StringBuilder();
102
103                 while (bytes_left > READ_BUFFER_SIZE)
104                 {
105                     // Now, read just the field value.
106                     PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
107
108                     // Read the bytes as string.
109                     result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
110
111                     bytes_left -= READ_BUFFER_SIZE;
112                 }
113
114                 // Now, read just the field value.
115                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
116
117                 // Read the bytes as string.
118                 result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
119
120
121                 // Add them to the AsciiRow data.
122                 data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(oid_to_name_mapping, result.ToString(), row_desc[field_count].type_oid, row_desc[field_count].type_modifier));
123
124             }
125         }
126
127         private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding)
128         {
129             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
130
131             Byte[] input_buffer = new Byte[READ_BUFFER_SIZE];
132
133             PGUtil.ReadInt32(inputStream, input_buffer);
134             Int16 numCols = PGUtil.ReadInt16(inputStream, input_buffer);
135
136             for (Int16 field_count = 0; field_count < numCols; field_count++)
137             {
138                 Int32 field_value_size = PGUtil.ReadInt32(inputStream, input_buffer);
139
140                 // Check if this field is null
141                 if (field_value_size == -1) // Null value
142                 {
143                     data.Add(DBNull.Value);
144                     continue;
145                 }
146
147                 Int32           bytes_left = field_value_size;
148                 StringBuilder   result = new StringBuilder();
149
150                 while (bytes_left > READ_BUFFER_SIZE)
151                 {
152                     // Now, read just the field value.
153                     PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
154
155                     // Read the bytes as string.
156                     result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
157
158                     bytes_left -= READ_BUFFER_SIZE;
159                 }
160
161                 // Now, read just the field value.
162                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
163
164                 if (row_desc[field_count].format_code == FormatCode.Text)
165                 {
166                     // Read the bytes as string.
167                     result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
168                     // Add them to the AsciiRow data.
169                     data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(oid_to_name_mapping, result.ToString(), row_desc[field_count].type_oid, row_desc[field_count].type_modifier));
170                 }
171                 else
172                     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));
173             }
174         }
175
176         // Using the given null field map (provided by the backend),
177         // determine if the given field index is mapped null by the backend.
178         // We only need to do this for version 2 protocol.
179         private static Boolean IsBackendNull(Byte[] null_map_array, Int32 index)
180         {
181             // Get the byte that holds the bit index position.
182             Byte test_byte = null_map_array[index/8];
183
184             // Now, check if index bit is set.
185             // To do this, get its position in the byte, shift to
186             // MSB and test it with the byte 10000000.
187             return (((test_byte << (index%8)) & 0x80) == 0);
188         }
189     }
190 }