Revert the revert
[mcs.git] / class / Npgsql / Npgsql / NpgsqlRow.cs
blob79c2da8dc4b7614981e562652fa45cadca23048b
1 // created on 4/3/2003 at 19:45
3 // Npgsql.NpgsqlBinaryRow.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
12 // Permission to use, copy, modify, and distribute this software and its
13 // documentation for any purpose, without fee, and without a written
14 // agreement is hereby granted, provided that the above copyright notice
15 // and this paragraph and the following two paragraphs appear in all copies.
16 //
17 // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
18 // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
19 // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
20 // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
21 // THE POSSIBILITY OF SUCH DAMAGE.
22 //
23 // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
24 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25 // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
26 // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
27 // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
29 using System;
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Reflection;
33 using System.Resources;
34 using System.Text;
35 using NpgsqlTypes;
37 namespace Npgsql
39 /// <summary>
40 /// This is the abstract base class for NpgsqlAsciiRow and NpgsqlBinaryRow.
41 /// </summary>
42 internal abstract class NpgsqlRow : IStreamOwner
44 protected static readonly ResourceManager resman = new ResourceManager(MethodBase.GetCurrentMethod().DeclaringType);
45 public abstract object this[int index] { get; }
46 public abstract int NumFields { get; }
47 public abstract bool IsDBNull(int index);
48 public abstract void Dispose();
49 public abstract long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length);
50 public abstract long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length);
53 internal sealed class CachingRow : NpgsqlRow
55 private readonly List<object> _data = new List<object>();
56 private readonly ForwardsOnlyRow _inner;
58 public CachingRow(ForwardsOnlyRow fo)
60 _inner = fo;
63 public override object this[Int32 index]
65 get
67 if ((index < 0) || (index >= NumFields))
69 throw new IndexOutOfRangeException("this[] index value");
71 while (_data.Count <= index)
73 _data.Add(_inner[_data.Count]);
75 return _data[index];
79 public override int NumFields
81 get { return _inner.NumFields; }
84 public override bool IsDBNull(int index)
86 return this[index] == DBNull.Value;
89 public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
91 byte[] source = (byte[]) this[i];
92 if (buffer == null)
94 return source.Length - fieldOffset;
96 long finalLength = Math.Max(0, Math.Min(length, source.Length - fieldOffset));
97 Array.Copy(source, fieldOffset, buffer, bufferoffset, finalLength);
98 return finalLength;
101 public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
103 string source = (string) this[i];
104 if (buffer == null)
106 return source.Length - fieldoffset;
108 long finalLength = Math.Max(0, Math.Min(length, source.Length - fieldoffset));
109 Array.Copy(source.ToCharArray(), fieldoffset, buffer, bufferoffset, finalLength);
110 return finalLength;
113 public override void Dispose()
115 _inner.Dispose();
119 internal sealed class ForwardsOnlyRow : NpgsqlRow
121 private int _lastIndex = -1;
122 private readonly RowReader _reader;
124 public ForwardsOnlyRow(RowReader reader)
126 _reader = reader;
129 private void SetIndex(int index, bool allowCurrent)
131 if (index < 0 || index >= NumFields)
133 throw new IndexOutOfRangeException();
135 if (allowCurrent && _reader.CurrentlyStreaming ? index < _lastIndex : index <= _lastIndex)
137 throw new InvalidOperationException(
138 string.Format(resman.GetString("Row_Sequential_Field_Error"), index, _lastIndex + 1));
140 _reader.Skip(index - _lastIndex - 1);
141 _lastIndex = index;
144 public override object this[int index]
148 SetIndex(index, false);
149 return _reader.GetNext();
153 public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
155 if (buffer == null)
157 throw new NotSupportedException();
159 if (!_reader.CanGetByteStream(i))
161 throw new InvalidCastException();
163 SetIndex(i, true);
164 _reader.SkipBytesTo(fieldOffset);
165 return _reader.Read(buffer, bufferoffset, length);
168 public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
170 if (buffer == null)
172 throw new NotSupportedException();
174 if (!_reader.CanGetCharStream(i))
176 throw new InvalidCastException();
178 SetIndex(i, true);
179 _reader.SkipCharsTo(fieldoffset);
180 return _reader.Read(buffer, bufferoffset, length);
183 public override int NumFields
185 get { return _reader.NumFields; }
188 public override bool IsDBNull(int index)
190 if (_lastIndex > -1)
192 SetIndex(index - 1, true);
194 return _reader.IsNextDBNull;
197 public override void Dispose()
199 _reader.Dispose();
203 /// <summary>
204 /// Reads a row, field by field, allowing a DataRow to be built appropriately.
205 /// </summary>
206 internal abstract class RowReader : IStreamOwner
208 /// <summary>
209 /// Reads part of a field, as needed (for <see cref="System.Data.IDataRecord.GetChars()"/>
210 /// and <see cref="System.Data.IDataRecord.GetBytes()"/>
211 /// </summary>
212 protected abstract class Streamer : IStreamOwner
214 protected readonly Stream _stream;
215 protected int _remainingBytes;
216 private int _alreadyRead = 0;
218 protected Streamer(Stream stream, int remainingBytes)
220 _stream = stream;
221 _remainingBytes = remainingBytes;
224 public int AlreadyRead
226 get { return _alreadyRead; }
227 protected set { _alreadyRead = value; }
230 public void Dispose()
232 PGUtil.EatStreamBytes(_stream, _remainingBytes);
236 /// <summary>
237 /// Adds further functionality to stream that is dependant upon the type of data read.
238 /// </summary>
239 protected abstract class Streamer<T> : Streamer
241 protected Streamer(Stream stream, int remainingBytes)
242 : base(stream, remainingBytes)
246 public abstract int DoRead(T[] output, int outputIdx, int length);
247 public abstract int DoSkip(int length);
249 public int Read(T[] output, int outputIdx, int length)
251 int ret = DoRead(output, outputIdx, length);
252 AlreadyRead += ret;
253 return ret;
256 private void Skip(int length)
258 AlreadyRead += DoSkip(length);
261 public void SkipTo(long position)
263 if (position < AlreadyRead)
265 throw new InvalidOperationException();
267 Skip((int) position - AlreadyRead);
271 /// <summary>
272 /// Completes the implementation of Streamer for char data.
273 /// </summary>
274 protected sealed class CharStreamer : Streamer<char>
276 public CharStreamer(Stream stream, int remainingBytes)
277 : base(stream, remainingBytes)
281 public override int DoRead(char[] output, int outputIdx, int length)
283 return PGUtil.ReadChars(_stream, output, length, ref _remainingBytes, outputIdx);
286 public override int DoSkip(int length)
288 return PGUtil.SkipChars(_stream, length, ref _remainingBytes);
292 /// <summary>
293 /// Completes the implementation of Streamer for byte data.
294 /// </summary>
295 protected sealed class ByteStreamer : Streamer<byte>
297 public ByteStreamer(Stream stream, int remainingBytes)
298 : base(stream, remainingBytes)
302 public override int DoRead(byte[] output, int outputIdx, int length)
304 return PGUtil.ReadEscapedBytes(_stream, output, length, ref _remainingBytes, outputIdx);
307 public override int DoSkip(int length)
309 return PGUtil.SkipEscapedBytes(_stream, length, ref _remainingBytes);
313 protected static readonly Encoding UTF8Encoding = Encoding.UTF8;
314 private readonly NpgsqlRowDescription _rowDesc;
315 private readonly Stream _stream;
316 private Streamer _streamer;
317 private int _currentField = -1;
319 public RowReader(NpgsqlRowDescription rowDesc, Stream stream)
321 _rowDesc = rowDesc;
322 _stream = stream;
325 protected Streamer CurrentStreamer
327 get { return _streamer; }
330 if (_streamer != null)
332 _streamer.Dispose();
334 _streamer = value;
338 public bool CurrentlyStreaming
340 get { return _streamer != null; }
343 public bool CanGetByteStream(int index)
345 //TODO: Add support for byte[] being read as a stream of bytes.
346 return _rowDesc[index].TypeInfo.NpgsqlDbType == NpgsqlDbType.Bytea;
349 public bool CanGetCharStream(int index)
351 //TODO: Add support for arrays of string types?
352 return _rowDesc[index].TypeInfo.Type.Equals(typeof (string));
355 protected Streamer<byte> CurrentByteStreamer
359 if (CurrentStreamer == null)
361 if (!CanGetByteStream(_currentField + 1))
363 throw new InvalidCastException();
365 ++_currentField;
366 return (CurrentStreamer = new ByteStreamer(Stream, GetNextFieldCount())) as ByteStreamer;
368 else if (!(CurrentStreamer is Streamer<byte>))
370 throw new InvalidOperationException();
372 else
374 return CurrentStreamer as ByteStreamer;
379 protected Streamer<char> CurrentCharStreamer
383 if (CurrentStreamer == null)
385 if (!CanGetCharStream(_currentField + 1))
387 throw new InvalidCastException();
389 ++_currentField;
390 return (CurrentStreamer = new CharStreamer(Stream, GetNextFieldCount())) as CharStreamer;
392 else if (!(CurrentStreamer is Streamer<char>))
394 throw new InvalidOperationException();
396 else
398 return CurrentStreamer as CharStreamer;
403 protected Stream Stream
405 get { return _stream; }
408 protected NpgsqlRowDescription.FieldData FieldData
410 get { return _rowDesc[_currentField]; }
413 public int NumFields
415 get { return _rowDesc.NumFields; }
418 protected int CurrentField
420 get { return _currentField; }
423 protected abstract object ReadNext();
425 public object GetNext()
427 if (++_currentField == _rowDesc.NumFields)
429 throw new IndexOutOfRangeException();
431 return ReadNext();
434 public abstract bool IsNextDBNull { get; }
435 protected abstract void SkipOne();
437 public void Skip(int count)
439 if (count > 0)
441 if (_currentField + count >= _rowDesc.NumFields)
443 throw new IndexOutOfRangeException();
445 while (count-- > 0)
447 ++_currentField;
448 SkipOne();
453 protected abstract int GetNextFieldCount();
455 public int Read(byte[] output, int outputIdx, int length)
457 return CurrentByteStreamer.Read(output, outputIdx, length);
460 public void SkipBytesTo(long position)
462 CurrentByteStreamer.SkipTo(position);
465 public int Read(char[] output, int outputIdx, int length)
467 return CurrentCharStreamer.Read(output, outputIdx, length);
470 public void SkipCharsTo(long position)
472 CurrentCharStreamer.SkipTo(position);
475 public void Dispose()
477 CurrentStreamer = null;
478 Skip(_rowDesc.NumFields - _currentField - 1);