forked from npgsql/npgsql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTypeHandler.cs
More file actions
executable file
·329 lines (279 loc) · 12.2 KB
/
TypeHandler.cs
File metadata and controls
executable file
·329 lines (279 loc) · 12.2 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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using Npgsql.BackendMessages;
using NpgsqlTypes;
using System.Data;
using System.Diagnostics.CodeAnalysis;
namespace Npgsql
{
interface ITypeReader<T> {}
#region Simple type handler
interface ISimpleTypeWriter
{
int ValidateAndGetLength(object value);
void Write(object value, NpgsqlBuffer buf);
}
/// <summary>
/// A handler which can read small, usually fixed-length values.
/// </summary>
/// <typeparam name="T">the type of the value returned by this type handler</typeparam>
//[ContractClass(typeof(ITypeHandlerContract<>))]
// ReSharper disable once TypeParameterCanBeVariant
interface ISimpleTypeReader<T> : ITypeReader<T>
{
/// <summary>
/// The entire data required to read the value is expected to be in the buffer.
/// </summary>
/// <param name="buf"></param>
/// <param name="fieldDescription"></param>
/// <param name="len"></param>
/// <returns></returns>
T Read(NpgsqlBuffer buf, FieldDescription fieldDescription, int len);
}
#endregion
#region Chunking type handler
[ContractClass(typeof(IChunkingTypeWriterContracts))]
interface IChunkingTypeWriter
{
/// <param name="value">the value to be examined</param>
/// <param name="parameter">
/// the <see cref="NpgsqlParameter"/> containing <paramref name="value"/>. Consulted for the
/// length cache and for settings which impact how to send the parameter, e.g.
/// <see cref="NpgsqlParameter.Size"/>.
/// </param>
int ValidateAndGetLength(object value, NpgsqlParameter parameter);
/// <param name="value">the value to be written</param>
/// <param name="buf"></param>
/// <param name="parameter">
/// the <see cref="NpgsqlParameter"/> containing <paramref name="value"/>. Consulted for the
/// length cache and for settings which impact how to send the parameter, e.g.
/// <see cref="NpgsqlParameter.Size"/>.
/// </param>
void PrepareWrite(object value, NpgsqlBuffer buf, NpgsqlParameter parameter);
bool Write(ref DirectBuffer directBuf);
}
[ContractClassFor(typeof(IChunkingTypeWriter))]
// ReSharper disable once InconsistentNaming
class IChunkingTypeWriterContracts : IChunkingTypeWriter
{
public int ValidateAndGetLength(object value, NpgsqlParameter parameter)
{
Contract.Requires(value != null);
return default(int);
}
public void PrepareWrite(object value, NpgsqlBuffer buf, NpgsqlParameter parameter)
{
Contract.Requires(buf != null);
Contract.Requires(value != null);
}
public bool Write(ref DirectBuffer directBuf)
{
Contract.Ensures(Contract.Result<bool>() == false || directBuf.Buffer == null);
return default(bool);
}
}
/// <summary>
/// A type handler which handles values of totally arbitrary length, and therefore supports chunking them.
/// </summary>
[ContractClass(typeof(IChunkingTypeReaderContracts<>))]
// ReSharper disable once TypeParameterCanBeVariant
interface IChunkingTypeReader<T> : ITypeReader<T>
{
void PrepareRead(NpgsqlBuffer buf, FieldDescription fieldDescription, int len);
bool Read(out T result);
}
[ContractClassFor(typeof(IChunkingTypeReader<>))]
// ReSharper disable once InconsistentNaming
class IChunkingTypeReaderContracts<T> : IChunkingTypeReader<T>
{
public void PrepareRead(NpgsqlBuffer buf, FieldDescription fieldDescription, int len)
{
Contract.Requires(buf != null);
Contract.Requires(fieldDescription != null);
}
public bool Read(out T result)
{
//Contract.Ensures(!completed || Contract.ValueAtReturn(out result) == default(T));
result = default(T);
return default(bool);
}
}
#endregion
internal abstract class TypeHandler
{
internal string PgName { get; set; }
internal uint OID { get; set; }
internal NpgsqlDbType NpgsqlDbType { get; set; }
internal abstract Type GetFieldType(FieldDescription fieldDescription=null);
internal abstract Type GetProviderSpecificFieldType(FieldDescription fieldDescription=null);
internal abstract object ReadValueAsObject(DataRowMessage row, FieldDescription fieldDescription);
internal virtual object ReadPsvAsObject(DataRowMessage row, FieldDescription fieldDescription)
{
return ReadValueAsObject(row, fieldDescription);
}
public virtual bool PreferTextWrite { get { return false; } }
internal T Read<T>(DataRowMessage row, FieldDescription fieldDescription, int len)
{
Contract.Requires(row.PosInColumn == 0);
Contract.Ensures(row.PosInColumn == row.ColumnLen);
T result;
var asSimpleReader = this as ISimpleTypeReader<T>;
if (asSimpleReader != null)
{
row.Buffer.Ensure(len);
result = asSimpleReader.Read(row.Buffer, fieldDescription, len);
}
else
{
var asChunkingReader = this as IChunkingTypeReader<T>;
if (asChunkingReader == null) {
throw new InvalidCastException(String.Format("Can't cast database type {0} to {1}", fieldDescription.Handler.PgName, typeof(T).Name));
}
asChunkingReader.PrepareRead(row.Buffer, fieldDescription, len);
while (!asChunkingReader.Read(out result)) {
row.Buffer.ReadMore();
}
}
row.PosInColumn += row.ColumnLen;
return result;
}
protected static T GetIConvertibleValue<T>(object value) where T : IConvertible
{
return value is T ? (T)value : (T)Convert.ChangeType(value, typeof(T), null);
}
[ContractInvariantMethod]
void ObjectInvariants()
{
Contract.Invariant(!(this is IChunkingTypeWriter && this is ISimpleTypeWriter));
}
}
internal abstract class TypeHandler<T> : TypeHandler
{
internal override Type GetFieldType(FieldDescription fieldDescription)
{
return typeof(T);
}
internal override Type GetProviderSpecificFieldType(FieldDescription fieldDescription)
{
return typeof(T);
}
internal override object ReadValueAsObject(DataRowMessage row, FieldDescription fieldDescription)
{
return Read<T>(row, fieldDescription, row.ColumnLen);
}
internal override object ReadPsvAsObject(DataRowMessage row, FieldDescription fieldDescription)
{
return Read<T>(row, fieldDescription, row.ColumnLen);
}
[ContractInvariantMethod]
void ObjectInvariants()
{
Contract.Invariant(this is ISimpleTypeReader<T> || this is IChunkingTypeReader<T>);
}
}
/// <summary>
/// A marking interface to allow us to know whether a given type handler has a provider-specific type
/// distinct from its regular type
/// </summary>
internal interface ITypeHandlerWithPsv {}
/// <summary>
/// A type handler that supports a provider-specific value which is different from the regular value (e.g.
/// NpgsqlDate and DateTime)
/// </summary>
/// <typeparam name="T">the regular value type returned by this type handler</typeparam>
/// <typeparam name="TPsv">the type of the provider-specific value returned by this type handler</typeparam>
internal abstract class TypeHandlerWithPsv<T, TPsv> : TypeHandler<T>, ITypeHandlerWithPsv
{
internal override Type GetProviderSpecificFieldType(FieldDescription fieldDescription)
{
return typeof (TPsv);
}
internal override object ReadPsvAsObject(DataRowMessage row, FieldDescription fieldDescription)
{
return Read<TPsv>(row, fieldDescription, row.ColumnLen);
}
}
struct DirectBuffer
{
public byte[] Buffer;
public int Size;
}
/// <summary>
/// Can be thrown by readers to indicate that interpreting the value failed, but the value was read wholly
/// and it is safe to continue reading. Any other exception is assumed to leave the row in an unknown state
/// and the connector is therefore set to Broken.
/// Note that an inner exception is mandatory, and will get thrown to the user instead of the SafeReadException.
/// </summary>
internal class SafeReadException : Exception
{
public SafeReadException(Exception innerException) : base("", innerException)
{
Contract.Requires(innerException != null);
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
[SuppressMessage("ReSharper", "LocalizableElement")]
class TypeMappingAttribute : Attribute
{
internal TypeMappingAttribute(string pgName, NpgsqlDbType? npgsqlDbType, DbType[] dbTypes, Type[] types)
{
if (String.IsNullOrWhiteSpace(pgName))
throw new ArgumentException("pgName can't be empty", "pgName");
Contract.EndContractBlock();
PgName = pgName;
NpgsqlDbType = npgsqlDbType;
DbTypes = dbTypes ?? new DbType[0];
Types = types ?? new Type[0];
}
internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType, DbType[] dbTypes, Type[] types)
: this(pgName, (NpgsqlDbType?)npgsqlDbType, dbTypes, types) {}
//internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType, DbType[] dbTypes=null, Type type=null)
// : this(pgName, npgsqlDbType, dbTypes, type == null ? null : new[] { type }) {}
internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType)
: this(pgName, npgsqlDbType, new DbType[0], new Type[0]) { }
internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType, DbType[] dbTypes, Type type)
: this(pgName, npgsqlDbType, dbTypes, new[] {type}) { }
internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType, DbType dbType, Type[] types)
: this(pgName, npgsqlDbType, new[] { dbType }, types) {}
internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType, DbType dbType, Type type=null)
: this(pgName, npgsqlDbType, new[] { dbType }, type == null ? null : new[] { type }) {}
internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType, Type[] types)
: this(pgName, npgsqlDbType, new DbType[0], types) { }
internal TypeMappingAttribute(string pgName, NpgsqlDbType npgsqlDbType, Type type)
: this(pgName, npgsqlDbType, new DbType[0], new[] { type }) {}
/// <summary>
/// Read-only parameter, only used by "unknown"
/// </summary>
internal TypeMappingAttribute(string pgName)
: this(pgName, null, null, null) {}
internal string PgName { get; private set; }
internal NpgsqlDbType? NpgsqlDbType { get; private set; }
internal DbType[] DbTypes { get; private set; }
internal Type[] Types { get; private set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendFormat("[{0} NpgsqlDbType={1}", PgName, NpgsqlDbType);
if (DbTypes.Length > 0) {
sb.Append(" DbTypes=");
sb.Append(String.Join(",", DbTypes.Select(t => t.ToString())));
}
if (Types.Length > 0) {
sb.Append(" Types=");
sb.Append(String.Join(",", Types.Select(t => t.Name)));
}
sb.AppendFormat("]");
return sb.ToString();
}
[ContractInvariantMethod]
void ObjectInvariants()
{
Contract.Invariant(!String.IsNullOrWhiteSpace(PgName));
Contract.Invariant(Types != null);
Contract.Invariant(DbTypes != null);
}
}
}