forked from npgsql/npgsql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathByteaHandler.cs
More file actions
147 lines (129 loc) · 6.34 KB
/
ByteaHandler.cs
File metadata and controls
147 lines (129 loc) · 6.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using System;
using System.Data;
using System.Threading.Tasks;
using Npgsql.BackendMessages;
using Npgsql.PostgresTypes;
using Npgsql.TypeHandling;
using Npgsql.TypeMapping;
using NpgsqlTypes;
namespace Npgsql.TypeHandlers
{
/// <summary>
/// A type handler for the PostgreSQL bytea data type.
/// </summary>
/// <remarks>
/// See http://www.postgresql.org/docs/current/static/datatype-binary.html.
///
/// The type handler API allows customizing Npgsql's behavior in powerful ways. However, although it is public, it
/// should be considered somewhat unstable, and may change in breaking ways, including in non-major releases.
/// Use it at your own risk.
/// </remarks>
[TypeMapping(
"bytea",
NpgsqlDbType.Bytea,
DbType.Binary,
new[] {
typeof(byte[]),
typeof(ArraySegment<byte>),
#if !NETSTANDARD2_0 && !NET461
typeof(ReadOnlyMemory<byte>),
typeof(Memory<byte>)
#endif
})]
public class ByteaHandler : NpgsqlTypeHandler<byte[]>, INpgsqlTypeHandler<ArraySegment<byte>>
#if !NETSTANDARD2_0 && !NET461
, INpgsqlTypeHandler<ReadOnlyMemory<byte>>, INpgsqlTypeHandler<Memory<byte>>
#endif
{
/// <summary>
/// Constructs a <see cref="ByteaHandler"/>.
/// </summary>
public ByteaHandler(PostgresType postgresType) : base(postgresType) {}
/// <inheritdoc />
public override async ValueTask<byte[]> Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
{
var bytes = new byte[len];
var pos = 0;
while (true)
{
var toRead = Math.Min(len - pos, buf.ReadBytesLeft);
buf.ReadBytes(bytes, pos, toRead);
pos += toRead;
if (pos == len)
break;
await buf.ReadMore(async);
}
return bytes;
}
ValueTask<ArraySegment<byte>> INpgsqlTypeHandler<ArraySegment<byte>>.Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription)
{
buf.Skip(len);
throw new NpgsqlSafeReadException(new NotSupportedException("Only writing ArraySegment<byte> to PostgreSQL bytea is supported, no reading."));
}
int ValidateAndGetLength(int bufferLen, NpgsqlParameter? parameter)
=> parameter == null || parameter.Size <= 0 || parameter.Size >= bufferLen
? bufferLen
: parameter.Size;
/// <inheritdoc />
public override int ValidateAndGetLength(byte[] value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(value.Length, parameter);
/// <inheritdoc />
public int ValidateAndGetLength(ArraySegment<byte> value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(value.Count, parameter);
/// <inheritdoc />
public override Task Write(byte[] value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
=> Write(value, buf, 0, ValidateAndGetLength(value.Length, parameter), async);
/// <inheritdoc />
public Task Write(ArraySegment<byte> value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
=> value.Array is null ? Task.CompletedTask : Write(value.Array, buf, value.Offset, ValidateAndGetLength(value.Count, parameter), async);
async Task Write(byte[] value, NpgsqlWriteBuffer buf, int offset, int count, bool async)
{
// The entire segment fits in our buffer, copy it as usual.
if (count <= buf.WriteSpaceLeft)
{
buf.WriteBytes(value, offset, count);
return;
}
// The segment is larger than our buffer. Flush whatever is currently in the buffer and
// write the array directly to the socket.
await buf.Flush(async);
await buf.DirectWrite(value, offset, count, async);
}
#if !NETSTANDARD2_0 && !NET461
/// <inheritdoc />
public int ValidateAndGetLength(Memory<byte> value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(value.Length, parameter);
/// <inheritdoc />
public int ValidateAndGetLength(ReadOnlyMemory<byte> value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(value.Length, parameter);
/// <inheritdoc />
public async Task Write(ReadOnlyMemory<byte> value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
{
if (parameter != null && parameter.Size > 0 && parameter.Size < value.Length)
value = value.Slice(0, parameter.Size);
// The entire segment fits in our buffer, copy it into the buffer as usual.
if (value.Length <= buf.WriteSpaceLeft)
{
buf.WriteBytes(value.Span);
return;
}
// The segment is larger than our buffer. Perform a direct write, flushing whatever is currently in the buffer
// and then writing the array directly to the socket.
await buf.DirectWrite(value, async);
}
/// <inheritdoc />
public Task Write(Memory<byte> value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
=> Write((ReadOnlyMemory<byte>)value, buf, lengthCache, parameter, async);
ValueTask<ReadOnlyMemory<byte>> INpgsqlTypeHandler<ReadOnlyMemory<byte>>.Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription)
{
buf.Skip(len);
throw new NpgsqlSafeReadException(new NotSupportedException("Only writing ReadOnlyMemory<byte> to PostgreSQL bytea is supported, no reading."));
}
ValueTask<Memory<byte>> INpgsqlTypeHandler<Memory<byte>>.Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription)
{
buf.Skip(len);
throw new NpgsqlSafeReadException(new NotSupportedException("Only writing Memory<byte> to PostgreSQL bytea is supported, no reading."));
}
#endif
}
}