1 // created on 13/6/2002 at 21:06
3 // Npgsql.NpgsqlAsciiRow.cs
6 // Francisco Jr. (fxjrlists@yahoo.com.br)
8 // Copyright (C) 2002 The Npgsql Development Team
9 // npgsql-general@gborg.postgresql.org
10 // http://gborg.postgresql.org/project/npgsql/projdisplay.php
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.
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.
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
27 using System.Collections;
38 /// This class represents the AsciiRow message sent from PostgreSQL
42 internal sealed class NpgsqlAsciiRow
44 // Logging related values
45 private static readonly String CLASSNAME = "NpgsqlAsciiRow";
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;
55 public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, Hashtable oidToNameMapping, ProtocolVersion protocolVersion)
57 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
59 data = new ArrayList();
61 oid_to_name_mapping = oidToNameMapping;
62 protocol_version = protocolVersion;
65 public void ReadFromStream(Stream inputStream, Encoding encoding)
67 switch (protocol_version) {
68 case ProtocolVersion.Version2 :
69 ReadFromStream_Ver_2(inputStream, encoding);
72 case ProtocolVersion.Version3 :
73 ReadFromStream_Ver_3(inputStream, encoding);
79 private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding)
81 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
83 Byte[] input_buffer = new Byte[READ_BUFFER_SIZE];
84 Byte[] null_map_array = new Byte[(row_desc.NumFields + 7)/8];
86 Array.Clear(null_map_array, 0, null_map_array.Length);
88 // Read the null fields bitmap.
89 PGUtil.CheckedStreamRead(inputStream, null_map_array, 0, null_map_array.Length );
92 for (Int16 field_count = 0; field_count < row_desc.NumFields; field_count++)
95 // Check if this field isn't null
96 if (IsBackendNull(null_map_array, field_count))
98 // Field is null just keep next field.
100 data.Add(DBNull.Value);
104 // Read the first data of the first row.
106 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, 4);
108 Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
109 field_value_size -= 4;
110 Int32 bytes_left = field_value_size;
112 StringBuilder result = new StringBuilder();
114 while (bytes_left > READ_BUFFER_SIZE)
116 // Now, read just the field value.
117 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
119 // Read the bytes as string.
120 result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
122 bytes_left -= READ_BUFFER_SIZE;
125 // Now, read just the field value.
126 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
128 // Read the bytes as string.
129 result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
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));
138 private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding)
140 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
142 Byte[] input_buffer = new Byte[READ_BUFFER_SIZE];
144 PGUtil.ReadInt32(inputStream, input_buffer);
145 Int16 numCols = PGUtil.ReadInt16(inputStream, input_buffer);
147 for (Int16 field_count = 0; field_count < numCols; field_count++)
149 Int32 field_value_size = PGUtil.ReadInt32(inputStream, input_buffer);
151 if (field_value_size == -1) // Null value
153 // Field is null just keep next field.
155 data.Add(DBNull.Value);
159 Int32 bytes_left = field_value_size;
161 StringBuilder result = new StringBuilder();
163 while (bytes_left > READ_BUFFER_SIZE)
165 // Now, read just the field value.
166 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
168 // Read the bytes as string.
169 result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
171 bytes_left -= READ_BUFFER_SIZE;
174 // Now, read just the field value.
175 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
177 if (row_desc[field_count].format_code == FormatCode.Text)
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));
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));
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)
195 // Get the byte that holds the bit index position.
196 Byte test_byte = null_map_array[index/8];
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);
205 public Boolean IsDBNull(Int32 index)
207 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsDBNull", index);
209 // Check valid index range.
210 if ((index < 0) || (index >= row_desc.NumFields))
211 throw new IndexOutOfRangeException("index");
213 return (this.data[index] == DBNull.Value);
216 public Object this[Int32 index]
221 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, index);
223 if ((index < 0) || (index >= row_desc.NumFields))
224 throw new IndexOutOfRangeException("this[] index value");