1 // created on 4/3/2003 at 19:45
3 // Npgsql.NpgsqlBinaryRow.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 // 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.
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.
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.
30 using System
.Collections
.Generic
;
32 using System
.Reflection
;
33 using System
.Resources
;
40 /// This is the abstract base class for NpgsqlAsciiRow and NpgsqlBinaryRow.
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
)
63 public override object this[Int32 index
]
67 if ((index
< 0) || (index
>= NumFields
))
69 throw new IndexOutOfRangeException("this[] index value");
71 while (_data
.Count
<= index
)
73 _data
.Add(_inner
[_data
.Count
]);
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
];
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
);
101 public override long GetChars(int i
, long fieldoffset
, char[] buffer
, int bufferoffset
, int length
)
103 string source
= (string) this[i
];
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
);
113 public override void Dispose()
119 internal sealed class ForwardsOnlyRow
: NpgsqlRow
121 private int _lastIndex
= -1;
122 private readonly RowReader _reader
;
124 public ForwardsOnlyRow(RowReader 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);
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
)
157 throw new NotSupportedException();
159 if (!_reader
.CanGetByteStream(i
))
161 throw new InvalidCastException();
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
)
172 throw new NotSupportedException();
174 if (!_reader
.CanGetCharStream(i
))
176 throw new InvalidCastException();
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
)
192 SetIndex(index
- 1, true);
194 return _reader
.IsNextDBNull
;
197 public override void Dispose()
204 /// Reads a row, field by field, allowing a DataRow to be built appropriately.
206 internal abstract class RowReader
: IStreamOwner
209 /// Reads part of a field, as needed (for <see cref="System.Data.IDataRecord.GetChars()"/>
210 /// and <see cref="System.Data.IDataRecord.GetBytes()"/>
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
)
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
);
237 /// Adds further functionality to stream that is dependant upon the type of data read.
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
);
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
);
272 /// Completes the implementation of Streamer for char data.
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
);
293 /// Completes the implementation of Streamer for byte data.
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
)
325 protected Streamer CurrentStreamer
327 get { return _streamer; }
330 if (_streamer
!= null)
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();
366 return (CurrentStreamer
= new ByteStreamer(Stream
, GetNextFieldCount())) as ByteStreamer
;
368 else if (!(CurrentStreamer
is Streamer
<byte>))
370 throw new InvalidOperationException();
374 return CurrentStreamer
as ByteStreamer
;
379 protected Streamer
<char> CurrentCharStreamer
383 if (CurrentStreamer
== null)
385 if (!CanGetCharStream(_currentField
+ 1))
387 throw new InvalidCastException();
390 return (CurrentStreamer
= new CharStreamer(Stream
, GetNextFieldCount())) as CharStreamer
;
392 else if (!(CurrentStreamer
is Streamer
<char>))
394 throw new InvalidOperationException();
398 return CurrentStreamer
as CharStreamer
;
403 protected Stream Stream
405 get { return _stream; }
408 protected NpgsqlRowDescription
.FieldData FieldData
410 get { return _rowDesc[_currentField]; }
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();
434 public abstract bool IsNextDBNull { get; }
435 protected abstract void SkipOne();
437 public void Skip(int count
)
441 if (_currentField
+ count
>= _rowDesc
.NumFields
)
443 throw new IndexOutOfRangeException();
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);