|
//------------------------------------------------------------------------------
// <copyright file="RowBinding.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
namespace System.Data.OleDb {
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Data;
using System.Data.Common;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Runtime.ConstrainedExecution;
sealed internal class RowBinding : System.Data.ProviderBase.DbBuffer {
private readonly int _bindingCount;
private readonly int _headerLength;
private readonly int _dataLength;
private readonly int _emptyStringOffset;
private UnsafeNativeMethods.IAccessor _iaccessor;
private IntPtr _accessorHandle;
private readonly bool _needToReset;
private bool _haveData;
// tagDBBINDING[] starting 64bit aligned
// all DBSTATUS values (32bit per value), starting 64bit aligned
// all DBLENGTH values (32/64bit per value), starting 64bit alignedsa
// all data values listed after that (variable length), each individual starting 64bit aligned
// Int64 - zero for pointers to emptystring
internal static RowBinding CreateBuffer(int bindingCount, int databuffersize, bool needToReset) {
int headerLength = RowBinding.AlignDataSize(bindingCount * ODB.SizeOf_tagDBBINDING);
int length = RowBinding.AlignDataSize(headerLength + databuffersize) + 8; // 8 bytes for a null terminated string
return new RowBinding(bindingCount, headerLength, databuffersize, length, needToReset);
}
private RowBinding(int bindingCount, int headerLength, int dataLength, int length, bool needToReset) : base(length) {
_bindingCount = bindingCount;
_headerLength = headerLength;
_dataLength = dataLength;
_emptyStringOffset = length - 8; // 8 bytes for a null terminated string
_needToReset = needToReset;
Debug.Assert(0 < _bindingCount, "bad _bindingCount");
Debug.Assert(0 < _headerLength, "bad _headerLength");
Debug.Assert(0 < _dataLength, "bad _dataLength");
Debug.Assert(_bindingCount * 3 * IntPtr.Size <= _dataLength, "_dataLength too small");
Debug.Assert(_headerLength + _dataLength <= _emptyStringOffset, "bad string offset");
Debug.Assert(_headerLength + _dataLength + 8 <= length, "bad length");
}
internal void StartDataBlock() {
if (_haveData) {
Debug.Assert(false, "previous row not yet cleared");
ResetValues();
}
_haveData = true;
}
internal int BindingCount() {
return _bindingCount;
}
internal IntPtr DangerousGetAccessorHandle() {
return _accessorHandle;
}
internal IntPtr DangerousGetDataPtr() {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: You must have called DangerousAddRef before calling this
// method, or you run the risk of allowing Handle Recycling
// to occur!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return ADP.IntPtrOffset(DangerousGetHandle(), _headerLength);
}
internal IntPtr DangerousGetDataPtr(int valueOffset) {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: You must have called DangerousAddRef before calling this
// method, or you run the risk of allowing Handle Recycling
// to occur!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return ADP.IntPtrOffset(DangerousGetHandle(), valueOffset);
}
internal OleDbHResult CreateAccessor(UnsafeNativeMethods.IAccessor iaccessor, int flags, ColumnBinding[] bindings) {
OleDbHResult hr = 0;
int[] rowBindStatus = new int[BindingCount()];
_iaccessor = iaccessor;
Bid.Trace("<oledb.IAccessor.CreateAccessor|API|OLEDB>\n");
hr = iaccessor.CreateAccessor(flags, (IntPtr)rowBindStatus.Length, this, (IntPtr)_dataLength, out _accessorHandle, rowBindStatus); // MDAC 69530
Bid.Trace("<oledb.IAccessor.CreateAccessor|API|OLEDB|RET> %08X{HRESULT}\n", hr);
for (int k = 0; k < rowBindStatus.Length; ++k) {
if (DBBindStatus.OK != (DBBindStatus)rowBindStatus[k]) {
if (ODB.DBACCESSOR_PARAMETERDATA == flags) {
throw ODB.BadStatus_ParamAcc(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
}
else if (ODB.DBACCESSOR_ROWDATA == flags) {
throw ODB.BadStatusRowAccessor(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
}
else Debug.Assert(false, "unknown accessor buffer");
}
}
return hr;
}
internal ColumnBinding[] SetBindings(OleDbDataReader dataReader, Bindings bindings,
int indexStart, int indexForAccessor,
OleDbParameter[] parameters, tagDBBINDING[] dbbindings, bool ifIRowsetElseIRow) {
Debug.Assert(null != bindings, "null bindings");
Debug.Assert(dbbindings.Length == BindingCount(), "count mismatch");
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
DangerousAddRef(ref mustRelease);
IntPtr buffer = DangerousGetHandle();
for(int i = 0; i < dbbindings.Length; ++i) {
IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING));
Marshal.StructureToPtr(dbbindings[i], ptr, false/*deleteold*/);
}
}
finally {
if (mustRelease) {
DangerousRelease();
}
}
ColumnBinding[] columns = new ColumnBinding[dbbindings.Length];
for(int indexWithinAccessor = 0; indexWithinAccessor < columns.Length; ++indexWithinAccessor) {
int index = indexStart + indexWithinAccessor;
OleDbParameter parameter = ((null != parameters) ? parameters[index] : null);
columns[indexWithinAccessor] = new ColumnBinding(
dataReader, index, indexForAccessor, indexWithinAccessor,
parameter, this, bindings, dbbindings[indexWithinAccessor], _headerLength,
ifIRowsetElseIRow);
}
return columns;
}
static internal int AlignDataSize(int value) {
// buffer data to start on 8-byte boundary
return Math.Max(8, (value + 7) & ~0x7); // MDAC 70350
}
internal object GetVariantValue(int offset) {
Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
Debug.Assert(0 == offset%8, "invalid alignment");
ValidateCheck(offset, 2*ODB.SizeOf_Variant);
object value = null;
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
DangerousAddRef(ref mustRelease);
IntPtr buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);
value = Marshal.GetObjectForNativeVariant(buffer);
}
finally {
if (mustRelease) {
DangerousRelease();
}
}
return ((null != value) ? value : DBNull.Value);
}
// translate to native
internal void SetVariantValue(int offset, object value) {
// two contigous VARIANT structures, second should be a binary copy of the first
Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
Debug.Assert(0 == offset%8, "invalid alignment");
ValidateCheck(offset, 2*ODB.SizeOf_Variant);
IntPtr buffer = ADP.PtrZero;
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
DangerousAddRef(ref mustRelease);
buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);
RuntimeHelpers.PrepareConstrainedRegions();
try {
// GetNativeVariantForObject must be in try block since it has no reliability contract
Marshal.GetNativeVariantForObject(value, buffer);
}
finally {
// safe to copy memory(dst,src,count), even if GetNativeVariantForObject failed
NativeOledbWrapper.MemoryCopy(ADP.IntPtrOffset(buffer,ODB.SizeOf_Variant), buffer, ODB.SizeOf_Variant);
}
}
finally {
if (mustRelease) {
DangerousRelease();
}
}
}
// value
// cached value
// cached zero value
// translate to native
internal void SetBstrValue(int offset, string value) {
// two contigous BSTR ptr, second should be a binary copy of the first
Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
ValidateCheck(offset, 2*IntPtr.Size);
IntPtr ptr;
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
DangerousAddRef(ref mustRelease);
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
ptr = SafeNativeMethods.SysAllocStringLen(value, value.Length);
// safe to copy ptr, even if SysAllocStringLen failed
Marshal.WriteIntPtr(base.handle, offset, ptr);
Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, ptr);
}
}
finally {
if (mustRelease) {
DangerousRelease();
}
}
if (IntPtr.Zero == ptr) {
throw new OutOfMemoryException();
}
}
// translate to native
internal void SetByRefValue(int offset, IntPtr pinnedValue) {
Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
ValidateCheck(offset, 2*IntPtr.Size);
if (ADP.PtrZero == pinnedValue) { // empty array scenario
pinnedValue = ADP.IntPtrOffset(base.handle, _emptyStringOffset);
}
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
DangerousAddRef(ref mustRelease);
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
Marshal.WriteIntPtr(base.handle, offset, pinnedValue); // parameter input value
Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, pinnedValue); // original parameter value
}
}
finally {
if (mustRelease) {
DangerousRelease();
}
}
}
internal void CloseFromConnection() {
_iaccessor = null;
_accessorHandle = ODB.DB_INVALID_HACCESSOR;
}
internal new void Dispose() {
ResetValues();
UnsafeNativeMethods.IAccessor iaccessor = _iaccessor;
IntPtr accessorHandle = _accessorHandle;
_iaccessor = null;
_accessorHandle = ODB.DB_INVALID_HACCESSOR;
if ((ODB.DB_INVALID_HACCESSOR != accessorHandle) && (null != iaccessor)) {
OleDbHResult hr;
int refcount;
hr = iaccessor.ReleaseAccessor(accessorHandle, out refcount);
if (hr < 0) { // ignore any error msgs
SafeNativeMethods.Wrapper.ClearErrorInfo();
}
}
base.Dispose();
}
internal void ResetValues() {
if (_needToReset && _haveData) {
lock(this) { // prevent Dispose/ResetValues race condition
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
DangerousAddRef(ref mustRelease);
ResetValues(DangerousGetHandle(), _iaccessor);
}
finally {
if (mustRelease) {
DangerousRelease();
}
}
}
}
else {
_haveData = false;
}
#if DEBUG
// verify types that need reseting are not forgotton, since the code
// that sets this up is in dbbinding.cs, MaxLen { set; }
if (!_needToReset) {
Debug.Assert(0 <= _bindingCount && (_bindingCount * ODB.SizeOf_tagDBBINDING) < Length, "bad _bindingCount");
for (int i = 0; i < _bindingCount; ++i) {
short wtype = ReadInt16((i * ODB.SizeOf_tagDBBINDING) + ODB.OffsetOf_tagDBBINDING_wType);
switch(wtype) {
case (NativeDBType.BYREF | NativeDBType.BYTES):
case (NativeDBType.BYREF | NativeDBType.WSTR):
case NativeDBType.PROPVARIANT:
case NativeDBType.VARIANT:
case NativeDBType.BSTR:
case NativeDBType.HCHAPTER:
Debug.Assert(false, "expected _needToReset");
break;
}
}
}
#endif
}
// requires ReliabilityContract to be called by ReleaseHandle
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
private void ResetValues(IntPtr buffer, object iaccessor) {
Debug.Assert(ADP.PtrZero != buffer && _needToReset && _haveData, "shouldn't be calling ResetValues");
for (int i = 0; i < _bindingCount; ++i) {
IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING));
int valueOffset = _headerLength + Marshal.ReadIntPtr(ptr, ODB.OffsetOf_tagDBBINDING_obValue).ToInt32();
short wtype = Marshal.ReadInt16(ptr, ODB.OffsetOf_tagDBBINDING_wType);
switch(wtype) {
case (NativeDBType.BYREF | NativeDBType.BYTES):
case (NativeDBType.BYREF | NativeDBType.WSTR):
ValidateCheck(valueOffset, 2*IntPtr.Size);
FreeCoTaskMem(buffer, valueOffset);
break;
case NativeDBType.PROPVARIANT:
ValidateCheck(valueOffset, 2*NativeOledbWrapper.SizeOfPROPVARIANT);
FreePropVariant(buffer, valueOffset);
break;
case NativeDBType.VARIANT:
ValidateCheck(valueOffset, 2*ODB.SizeOf_Variant);
FreeVariant(buffer, valueOffset);
break;
case NativeDBType.BSTR:
ValidateCheck(valueOffset, 2*IntPtr.Size);
FreeBstr(buffer, valueOffset);
break;
case NativeDBType.HCHAPTER:
if (null != iaccessor) {
// iaccessor will always be null when from ReleaseHandle
FreeChapter(buffer, valueOffset, iaccessor);
}
break;
#if DEBUG
case NativeDBType.EMPTY:
case NativeDBType.NULL:
case NativeDBType.I2:
case NativeDBType.I4:
case NativeDBType.R4:
case NativeDBType.R8:
case NativeDBType.CY:
case NativeDBType.DATE:
case NativeDBType.ERROR:
case NativeDBType.BOOL:
case NativeDBType.DECIMAL:
case NativeDBType.I1:
case NativeDBType.UI1:
case NativeDBType.UI2:
case NativeDBType.UI4:
case NativeDBType.I8:
case NativeDBType.UI8:
case NativeDBType.FILETIME:
case NativeDBType.GUID:
case NativeDBType.BYTES:
case NativeDBType.WSTR:
case NativeDBType.NUMERIC:
case NativeDBType.DBDATE:
case NativeDBType.DBTIME:
case NativeDBType.DBTIMESTAMP:
break; // known, do nothing
case NativeDBType.IDISPATCH:
case NativeDBType.IUNKNOWN:
break; // known, releasing RowHandle will handle lifetimes correctly
default:
Debug.Assert(false, "investigate");
break;
#endif
}
}
_haveData = false;
}
// this correctly does not have a ReliabilityContract, will not be called via ReleaseHandle
static private void FreeChapter(IntPtr buffer, int valueOffset, object iaccessor) {
Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
UnsafeNativeMethods.IChapteredRowset chapteredRowset = (iaccessor as UnsafeNativeMethods.IChapteredRowset);
IntPtr chapter = SafeNativeMethods.InterlockedExchangePointer(ADP.IntPtrOffset(buffer, valueOffset), ADP.PtrZero);
if (ODB.DB_NULL_HCHAPTER != chapter) {
int refCount;
Bid.Trace("<oledb.IChapteredRowset.ReleaseChapter|API|OLEDB> Chapter=%Id\n", chapter);
OleDbHResult hr = chapteredRowset.ReleaseChapter(chapter, out refCount);
Bid.Trace("<oledb.IChapteredRowset.ReleaseChapter|API|OLEDB|RET> %08X{HRESULT}, RefCount=%d\n", hr, refCount);
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
static private void FreeBstr(IntPtr buffer, int valueOffset) {
Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
// two contigous BSTR ptrs that need to be freed
// the second should only be freed if different from the first
RuntimeHelpers.PrepareConstrainedRegions();
try {} finally {
IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize);
if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) {
SafeNativeMethods.SysFreeString(currentValue);
}
if (ADP.PtrZero != originalValue) {
SafeNativeMethods.SysFreeString(originalValue);
}
// for debugability - delay clearing memory until after FreeBSTR
Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero);
Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
static private void FreeCoTaskMem(IntPtr buffer, int valueOffset) {
Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
// two contigous CoTaskMemAlloc ptrs that need to be freed
// the first should only be freed if different from the first
RuntimeHelpers.PrepareConstrainedRegions();
try {} finally {
IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize);
// originalValue is pinned managed memory or pointer to emptyStringOffset
if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) {
SafeNativeMethods.CoTaskMemFree(currentValue);
}
// for debugability - delay clearing memory until after CoTaskMemFree
Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero);
Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
static private void FreeVariant(IntPtr buffer, int valueOffset) {
// two contigous VARIANT structures that need to be freed
// the second should only be freed if different from the first
Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset);
IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+ODB.SizeOf_Variant);
bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, ODB.SizeOf_Variant);
RuntimeHelpers.PrepareConstrainedRegions();
try {} finally {
// always clear the first structure
SafeNativeMethods.VariantClear(currentHandle);
if (different) {
// second structure different from the first
SafeNativeMethods.VariantClear(originalHandle);
}
else {
// second structure same as the first, just clear the field
SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)ODB.SizeOf_Variant);
}
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
static private void FreePropVariant(IntPtr buffer, int valueOffset) {
// two contigous PROPVARIANT structures that need to be freed
// the second should only be freed if different from the first
Debug.Assert(0 == (NativeOledbWrapper.SizeOfPROPVARIANT % 8), "unexpected PROPVARIANT size mutiplier");
Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset);
IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+NativeOledbWrapper.SizeOfPROPVARIANT);
bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, NativeOledbWrapper.SizeOfPROPVARIANT);
RuntimeHelpers.PrepareConstrainedRegions();
try {} finally {
// always clear the first structure
SafeNativeMethods.PropVariantClear(currentHandle);
if (different) {
// second structure different from the first
SafeNativeMethods.PropVariantClear(originalHandle);
}
else {
// second structure same as the first, just clear the field
SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)NativeOledbWrapper.SizeOfPROPVARIANT);
}
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
internal IntPtr InterlockedExchangePointer(int offset) {
ValidateCheck(offset, IntPtr.Size);
Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
IntPtr value;
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
DangerousAddRef(ref mustRelease);
IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
value = SafeNativeMethods.InterlockedExchangePointer(ptr, IntPtr.Zero);
}
finally {
if (mustRelease) {
DangerousRelease();
}
}
return value;
}
override protected bool ReleaseHandle() {
// NOTE: The SafeHandle class guarantees this will be called exactly once.
_iaccessor = null;
if (_needToReset && _haveData) {
IntPtr buffer = base.handle;
if (IntPtr.Zero != buffer) {
ResetValues(buffer, null);
}
}
return base.ReleaseHandle();
}
}
}
|