1 //------------------------------------------------------------------------------
2 // <copyright file="RowBinding.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data.OleDb {
12 using System.ComponentModel;
13 using System.Diagnostics;
15 using System.Data.Common;
16 using System.Globalization;
17 using System.Runtime.CompilerServices;
18 using System.Runtime.InteropServices;
19 using System.Security;
20 using System.Security.Permissions;
22 using System.Threading;
23 using System.Runtime.ConstrainedExecution;
25 sealed internal class RowBinding : System.Data.ProviderBase.DbBuffer {
26 private readonly int _bindingCount;
27 private readonly int _headerLength;
28 private readonly int _dataLength;
29 private readonly int _emptyStringOffset;
31 private UnsafeNativeMethods.IAccessor _iaccessor;
32 private IntPtr _accessorHandle;
34 private readonly bool _needToReset;
35 private bool _haveData;
37 // tagDBBINDING[] starting 64bit aligned
38 // all DBSTATUS values (32bit per value), starting 64bit aligned
39 // all DBLENGTH values (32/64bit per value), starting 64bit alignedsa
40 // all data values listed after that (variable length), each individual starting 64bit aligned
41 // Int64 - zero for pointers to emptystring
43 internal static RowBinding CreateBuffer(int bindingCount, int databuffersize, bool needToReset) {
44 int headerLength = RowBinding.AlignDataSize(bindingCount * ODB.SizeOf_tagDBBINDING);
45 int length = RowBinding.AlignDataSize(headerLength + databuffersize) + 8; // 8 bytes for a null terminated string
46 return new RowBinding(bindingCount, headerLength, databuffersize, length, needToReset);
49 private RowBinding(int bindingCount, int headerLength, int dataLength, int length, bool needToReset) : base(length) {
50 _bindingCount = bindingCount;
51 _headerLength = headerLength;
52 _dataLength = dataLength;
53 _emptyStringOffset = length - 8; // 8 bytes for a null terminated string
54 _needToReset = needToReset;
56 Debug.Assert(0 < _bindingCount, "bad _bindingCount");
57 Debug.Assert(0 < _headerLength, "bad _headerLength");
58 Debug.Assert(0 < _dataLength, "bad _dataLength");
59 Debug.Assert(_bindingCount * 3 * IntPtr.Size <= _dataLength, "_dataLength too small");
60 Debug.Assert(_headerLength + _dataLength <= _emptyStringOffset, "bad string offset");
61 Debug.Assert(_headerLength + _dataLength + 8 <= length, "bad length");
64 internal void StartDataBlock() {
66 Debug.Assert(false, "previous row not yet cleared");
72 internal int BindingCount() {
76 internal IntPtr DangerousGetAccessorHandle() {
77 return _accessorHandle;
80 internal IntPtr DangerousGetDataPtr() {
81 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
82 // NOTE: You must have called DangerousAddRef before calling this
83 // method, or you run the risk of allowing Handle Recycling
85 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
86 return ADP.IntPtrOffset(DangerousGetHandle(), _headerLength);
89 internal IntPtr DangerousGetDataPtr(int valueOffset) {
90 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
91 // NOTE: You must have called DangerousAddRef before calling this
92 // method, or you run the risk of allowing Handle Recycling
94 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
95 return ADP.IntPtrOffset(DangerousGetHandle(), valueOffset);
98 internal OleDbHResult CreateAccessor(UnsafeNativeMethods.IAccessor iaccessor, int flags, ColumnBinding[] bindings) {
100 int[] rowBindStatus = new int[BindingCount()];
102 _iaccessor = iaccessor;
104 Bid.Trace("<oledb.IAccessor.CreateAccessor|API|OLEDB>\n");
105 hr = iaccessor.CreateAccessor(flags, (IntPtr)rowBindStatus.Length, this, (IntPtr)_dataLength, out _accessorHandle, rowBindStatus); // MDAC 69530
106 Bid.Trace("<oledb.IAccessor.CreateAccessor|API|OLEDB|RET> %08X{HRESULT}\n", hr);
108 for (int k = 0; k < rowBindStatus.Length; ++k) {
109 if (DBBindStatus.OK != (DBBindStatus)rowBindStatus[k]) {
110 if (ODB.DBACCESSOR_PARAMETERDATA == flags) {
111 throw ODB.BadStatus_ParamAcc(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
113 else if (ODB.DBACCESSOR_ROWDATA == flags) {
114 throw ODB.BadStatusRowAccessor(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
116 else Debug.Assert(false, "unknown accessor buffer");
122 internal ColumnBinding[] SetBindings(OleDbDataReader dataReader, Bindings bindings,
123 int indexStart, int indexForAccessor,
124 OleDbParameter[] parameters, tagDBBINDING[] dbbindings, bool ifIRowsetElseIRow) {
125 Debug.Assert(null != bindings, "null bindings");
126 Debug.Assert(dbbindings.Length == BindingCount(), "count mismatch");
128 bool mustRelease = false;
130 RuntimeHelpers.PrepareConstrainedRegions();
132 DangerousAddRef(ref mustRelease);
134 IntPtr buffer = DangerousGetHandle();
135 for(int i = 0; i < dbbindings.Length; ++i) {
136 IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING));
137 Marshal.StructureToPtr(dbbindings[i], ptr, false/*deleteold*/);
146 ColumnBinding[] columns = new ColumnBinding[dbbindings.Length];
147 for(int indexWithinAccessor = 0; indexWithinAccessor < columns.Length; ++indexWithinAccessor) {
148 int index = indexStart + indexWithinAccessor;
149 OleDbParameter parameter = ((null != parameters) ? parameters[index] : null);
150 columns[indexWithinAccessor] = new ColumnBinding(
151 dataReader, index, indexForAccessor, indexWithinAccessor,
152 parameter, this, bindings, dbbindings[indexWithinAccessor], _headerLength,
158 static internal int AlignDataSize(int value) {
159 // buffer data to start on 8-byte boundary
160 return Math.Max(8, (value + 7) & ~0x7); // MDAC 70350
163 internal object GetVariantValue(int offset) {
164 Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
165 Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
166 Debug.Assert(0 == offset%8, "invalid alignment");
167 ValidateCheck(offset, 2*ODB.SizeOf_Variant);
170 bool mustRelease = false;
171 RuntimeHelpers.PrepareConstrainedRegions();
173 DangerousAddRef(ref mustRelease);
175 IntPtr buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);
176 value = Marshal.GetObjectForNativeVariant(buffer);
184 return ((null != value) ? value : DBNull.Value);
187 // translate to native
188 internal void SetVariantValue(int offset, object value) {
189 // two contigous VARIANT structures, second should be a binary copy of the first
190 Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
191 Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
192 Debug.Assert(0 == offset%8, "invalid alignment");
193 ValidateCheck(offset, 2*ODB.SizeOf_Variant);
195 IntPtr buffer = ADP.PtrZero;
196 bool mustRelease = false;
197 RuntimeHelpers.PrepareConstrainedRegions();
199 DangerousAddRef(ref mustRelease);
201 buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);
203 RuntimeHelpers.PrepareConstrainedRegions();
205 // GetNativeVariantForObject must be in try block since it has no reliability contract
206 Marshal.GetNativeVariantForObject(value, buffer);
209 // safe to copy memory(dst,src,count), even if GetNativeVariantForObject failed
210 NativeOledbWrapper.MemoryCopy(ADP.IntPtrOffset(buffer,ODB.SizeOf_Variant), buffer, ODB.SizeOf_Variant);
223 // translate to native
224 internal void SetBstrValue(int offset, string value) {
225 // two contigous BSTR ptr, second should be a binary copy of the first
226 Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
227 Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
228 ValidateCheck(offset, 2*IntPtr.Size);
231 bool mustRelease = false;
232 RuntimeHelpers.PrepareConstrainedRegions();
234 DangerousAddRef(ref mustRelease);
236 RuntimeHelpers.PrepareConstrainedRegions();
238 ptr = SafeNativeMethods.SysAllocStringLen(value, value.Length);
240 // safe to copy ptr, even if SysAllocStringLen failed
241 Marshal.WriteIntPtr(base.handle, offset, ptr);
242 Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, ptr);
250 if (IntPtr.Zero == ptr) {
251 throw new OutOfMemoryException();
255 // translate to native
256 internal void SetByRefValue(int offset, IntPtr pinnedValue) {
257 Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
258 Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
259 ValidateCheck(offset, 2*IntPtr.Size);
261 if (ADP.PtrZero == pinnedValue) { // empty array scenario
262 pinnedValue = ADP.IntPtrOffset(base.handle, _emptyStringOffset);
264 bool mustRelease = false;
265 RuntimeHelpers.PrepareConstrainedRegions();
267 DangerousAddRef(ref mustRelease);
269 RuntimeHelpers.PrepareConstrainedRegions();
271 Marshal.WriteIntPtr(base.handle, offset, pinnedValue); // parameter input value
272 Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, pinnedValue); // original parameter value
282 internal void CloseFromConnection() {
284 _accessorHandle = ODB.DB_INVALID_HACCESSOR;
287 internal new void Dispose() {
290 UnsafeNativeMethods.IAccessor iaccessor = _iaccessor;
291 IntPtr accessorHandle = _accessorHandle;
294 _accessorHandle = ODB.DB_INVALID_HACCESSOR;
296 if ((ODB.DB_INVALID_HACCESSOR != accessorHandle) && (null != iaccessor)) {
299 hr = iaccessor.ReleaseAccessor(accessorHandle, out refcount);
300 if (hr < 0) { // ignore any error msgs
301 SafeNativeMethods.Wrapper.ClearErrorInfo();
308 internal void ResetValues() {
309 if (_needToReset && _haveData) {
310 lock(this) { // prevent Dispose/ResetValues race condition
312 bool mustRelease = false;
313 RuntimeHelpers.PrepareConstrainedRegions();
315 DangerousAddRef(ref mustRelease);
317 ResetValues(DangerousGetHandle(), _iaccessor);
330 // verify types that need reseting are not forgotton, since the code
331 // that sets this up is in dbbinding.cs, MaxLen { set; }
333 Debug.Assert(0 <= _bindingCount && (_bindingCount * ODB.SizeOf_tagDBBINDING) < Length, "bad _bindingCount");
334 for (int i = 0; i < _bindingCount; ++i) {
335 short wtype = ReadInt16((i * ODB.SizeOf_tagDBBINDING) + ODB.OffsetOf_tagDBBINDING_wType);
337 case (NativeDBType.BYREF | NativeDBType.BYTES):
338 case (NativeDBType.BYREF | NativeDBType.WSTR):
339 case NativeDBType.PROPVARIANT:
340 case NativeDBType.VARIANT:
341 case NativeDBType.BSTR:
342 case NativeDBType.HCHAPTER:
343 Debug.Assert(false, "expected _needToReset");
351 // requires ReliabilityContract to be called by ReleaseHandle
352 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
353 private void ResetValues(IntPtr buffer, object iaccessor) {
354 Debug.Assert(ADP.PtrZero != buffer && _needToReset && _haveData, "shouldn't be calling ResetValues");
355 for (int i = 0; i < _bindingCount; ++i) {
356 IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING));
358 int valueOffset = _headerLength + Marshal.ReadIntPtr(ptr, ODB.OffsetOf_tagDBBINDING_obValue).ToInt32();
359 short wtype = Marshal.ReadInt16(ptr, ODB.OffsetOf_tagDBBINDING_wType);
362 case (NativeDBType.BYREF | NativeDBType.BYTES):
363 case (NativeDBType.BYREF | NativeDBType.WSTR):
364 ValidateCheck(valueOffset, 2*IntPtr.Size);
365 FreeCoTaskMem(buffer, valueOffset);
367 case NativeDBType.PROPVARIANT:
368 ValidateCheck(valueOffset, 2*NativeOledbWrapper.SizeOfPROPVARIANT);
369 FreePropVariant(buffer, valueOffset);
371 case NativeDBType.VARIANT:
372 ValidateCheck(valueOffset, 2*ODB.SizeOf_Variant);
373 FreeVariant(buffer, valueOffset);
375 case NativeDBType.BSTR:
376 ValidateCheck(valueOffset, 2*IntPtr.Size);
377 FreeBstr(buffer, valueOffset);
379 case NativeDBType.HCHAPTER:
380 if (null != iaccessor) {
381 // iaccessor will always be null when from ReleaseHandle
382 FreeChapter(buffer, valueOffset, iaccessor);
387 case NativeDBType.EMPTY:
388 case NativeDBType.NULL:
389 case NativeDBType.I2:
390 case NativeDBType.I4:
391 case NativeDBType.R4:
392 case NativeDBType.R8:
393 case NativeDBType.CY:
394 case NativeDBType.DATE:
395 case NativeDBType.ERROR:
396 case NativeDBType.BOOL:
397 case NativeDBType.DECIMAL:
398 case NativeDBType.I1:
399 case NativeDBType.UI1:
400 case NativeDBType.UI2:
401 case NativeDBType.UI4:
402 case NativeDBType.I8:
403 case NativeDBType.UI8:
404 case NativeDBType.FILETIME:
405 case NativeDBType.GUID:
406 case NativeDBType.BYTES:
407 case NativeDBType.WSTR:
408 case NativeDBType.NUMERIC:
409 case NativeDBType.DBDATE:
410 case NativeDBType.DBTIME:
411 case NativeDBType.DBTIMESTAMP:
412 break; // known, do nothing
413 case NativeDBType.IDISPATCH:
414 case NativeDBType.IUNKNOWN:
415 break; // known, releasing RowHandle will handle lifetimes correctly
417 Debug.Assert(false, "investigate");
425 // this correctly does not have a ReliabilityContract, will not be called via ReleaseHandle
426 static private void FreeChapter(IntPtr buffer, int valueOffset, object iaccessor) {
427 Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
429 UnsafeNativeMethods.IChapteredRowset chapteredRowset = (iaccessor as UnsafeNativeMethods.IChapteredRowset);
430 IntPtr chapter = SafeNativeMethods.InterlockedExchangePointer(ADP.IntPtrOffset(buffer, valueOffset), ADP.PtrZero);
431 if (ODB.DB_NULL_HCHAPTER != chapter) {
433 Bid.Trace("<oledb.IChapteredRowset.ReleaseChapter|API|OLEDB> Chapter=%Id\n", chapter);
434 OleDbHResult hr = chapteredRowset.ReleaseChapter(chapter, out refCount);
435 Bid.Trace("<oledb.IChapteredRowset.ReleaseChapter|API|OLEDB|RET> %08X{HRESULT}, RefCount=%d\n", hr, refCount);
439 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
440 static private void FreeBstr(IntPtr buffer, int valueOffset) {
441 Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
443 // two contigous BSTR ptrs that need to be freed
444 // the second should only be freed if different from the first
445 RuntimeHelpers.PrepareConstrainedRegions();
447 IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
448 IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize);
450 if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) {
451 SafeNativeMethods.SysFreeString(currentValue);
453 if (ADP.PtrZero != originalValue) {
454 SafeNativeMethods.SysFreeString(originalValue);
457 // for debugability - delay clearing memory until after FreeBSTR
458 Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero);
459 Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
463 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
464 static private void FreeCoTaskMem(IntPtr buffer, int valueOffset) {
465 Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
467 // two contigous CoTaskMemAlloc ptrs that need to be freed
468 // the first should only be freed if different from the first
469 RuntimeHelpers.PrepareConstrainedRegions();
471 IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
472 IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize);
474 // originalValue is pinned managed memory or pointer to emptyStringOffset
475 if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) {
476 SafeNativeMethods.CoTaskMemFree(currentValue);
479 // for debugability - delay clearing memory until after CoTaskMemFree
480 Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero);
481 Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
485 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
486 static private void FreeVariant(IntPtr buffer, int valueOffset) {
487 // two contigous VARIANT structures that need to be freed
488 // the second should only be freed if different from the first
490 Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
491 Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
493 IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset);
494 IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+ODB.SizeOf_Variant);
495 bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, ODB.SizeOf_Variant);
497 RuntimeHelpers.PrepareConstrainedRegions();
499 // always clear the first structure
500 SafeNativeMethods.VariantClear(currentHandle);
502 // second structure different from the first
503 SafeNativeMethods.VariantClear(originalHandle);
506 // second structure same as the first, just clear the field
507 SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)ODB.SizeOf_Variant);
512 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
513 static private void FreePropVariant(IntPtr buffer, int valueOffset) {
514 // two contigous PROPVARIANT structures that need to be freed
515 // the second should only be freed if different from the first
516 Debug.Assert(0 == (NativeOledbWrapper.SizeOfPROPVARIANT % 8), "unexpected PROPVARIANT size mutiplier");
517 Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
519 IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset);
520 IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+NativeOledbWrapper.SizeOfPROPVARIANT);
521 bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, NativeOledbWrapper.SizeOfPROPVARIANT);
523 RuntimeHelpers.PrepareConstrainedRegions();
525 // always clear the first structure
526 SafeNativeMethods.PropVariantClear(currentHandle);
528 // second structure different from the first
529 SafeNativeMethods.PropVariantClear(originalHandle);
532 // second structure same as the first, just clear the field
533 SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)NativeOledbWrapper.SizeOfPROPVARIANT);
538 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
539 internal IntPtr InterlockedExchangePointer(int offset) {
540 ValidateCheck(offset, IntPtr.Size);
541 Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
544 bool mustRelease = false;
545 RuntimeHelpers.PrepareConstrainedRegions();
547 DangerousAddRef(ref mustRelease);
549 IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
550 value = SafeNativeMethods.InterlockedExchangePointer(ptr, IntPtr.Zero);
560 override protected bool ReleaseHandle() {
561 // NOTE: The SafeHandle class guarantees this will be called exactly once.
563 if (_needToReset && _haveData) {
564 IntPtr buffer = base.handle;
565 if (IntPtr.Zero != buffer) {
566 ResetValues(buffer, null);
569 return base.ReleaseHandle();