536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
236 lines
9.8 KiB
C#
236 lines
9.8 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="OdbcHandle.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>
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Diagnostics;
|
|
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;
|
|
|
|
namespace System.Data.Odbc {
|
|
|
|
internal abstract class OdbcHandle : SafeHandle {
|
|
|
|
private ODBC32.SQL_HANDLE _handleType;
|
|
private OdbcHandle _parentHandle;
|
|
|
|
protected OdbcHandle(ODBC32.SQL_HANDLE handleType, OdbcHandle parentHandle) : base(IntPtr.Zero, true) {
|
|
|
|
_handleType = handleType;
|
|
|
|
bool mustRelease = false;
|
|
ODBC32.RetCode retcode = ODBC32.RetCode.SUCCESS;
|
|
|
|
// using ConstrainedRegions to make the native ODBC call and AddRef the parent
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try {
|
|
// validate handleType
|
|
switch(handleType) {
|
|
case ODBC32.SQL_HANDLE.ENV:
|
|
Debug.Assert(null == parentHandle, "did not expect a parent handle");
|
|
retcode = UnsafeNativeMethods.SQLAllocHandle(handleType, IntPtr.Zero, out base.handle);
|
|
break;
|
|
case ODBC32.SQL_HANDLE.DBC:
|
|
case ODBC32.SQL_HANDLE.STMT:
|
|
// must addref before calling native so it won't be released just after
|
|
Debug.Assert(null != parentHandle, "expected a parent handle"); // safehandle can't be null
|
|
parentHandle.DangerousAddRef(ref mustRelease);
|
|
|
|
retcode = UnsafeNativeMethods.SQLAllocHandle(handleType, parentHandle, out base.handle);
|
|
break;
|
|
// case ODBC32.SQL_HANDLE.DESC:
|
|
default:
|
|
Debug.Assert(false, "unexpected handleType");
|
|
break;
|
|
}
|
|
}
|
|
finally {
|
|
if (mustRelease) {
|
|
switch(handleType) {
|
|
case ODBC32.SQL_HANDLE.DBC:
|
|
case ODBC32.SQL_HANDLE.STMT:
|
|
if (IntPtr.Zero != base.handle) {
|
|
// must assign _parentHandle after a handle is actually created
|
|
// since ReleaseHandle will only call DangerousRelease if a handle exists
|
|
_parentHandle = parentHandle;
|
|
}
|
|
else {
|
|
// without a handle, ReleaseHandle may not be called
|
|
parentHandle.DangerousRelease();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Bid.TraceSqlReturn("<odbc.SQLAllocHandle|API|ODBC|RET> %08X{SQLRETURN}\n", retcode);
|
|
|
|
if((ADP.PtrZero == base.handle) || (ODBC32.RetCode.SUCCESS != retcode)) {
|
|
//
|
|
throw ODBC.CantAllocateEnvironmentHandle(retcode);
|
|
}
|
|
}
|
|
|
|
internal OdbcHandle(OdbcStatementHandle parentHandle, ODBC32.SQL_ATTR attribute) : base(IntPtr.Zero, true) {
|
|
Debug.Assert((ODBC32.SQL_ATTR.APP_PARAM_DESC == attribute) || (ODBC32.SQL_ATTR.APP_ROW_DESC == attribute), "invalid attribute");
|
|
_handleType = ODBC32.SQL_HANDLE.DESC;
|
|
|
|
int cbActual;
|
|
ODBC32.RetCode retcode;
|
|
bool mustRelease = false;
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try {
|
|
// must addref before calling native so it won't be released just after
|
|
parentHandle.DangerousAddRef(ref mustRelease);
|
|
|
|
retcode = parentHandle.GetStatementAttribute(attribute, out base.handle, out cbActual);
|
|
}
|
|
finally {
|
|
if (mustRelease) {
|
|
if (IntPtr.Zero != base.handle) {
|
|
// must call DangerousAddRef after a handle is actually created
|
|
// since ReleaseHandle will only call DangerousRelease if a handle exists
|
|
_parentHandle = parentHandle;
|
|
}
|
|
else {
|
|
// without a handle, ReleaseHandle may not be called
|
|
parentHandle.DangerousRelease();
|
|
}
|
|
}
|
|
}
|
|
if (ADP.PtrZero == base.handle) {
|
|
throw ODBC.FailedToGetDescriptorHandle(retcode);
|
|
}
|
|
// no info-message handle on getting a descriptor handle
|
|
}
|
|
|
|
internal ODBC32.SQL_HANDLE HandleType {
|
|
get {
|
|
return _handleType;
|
|
}
|
|
}
|
|
|
|
public override bool IsInvalid {
|
|
get {
|
|
// we should not have a parent if we do not have a handle
|
|
return (IntPtr.Zero == base.handle);
|
|
}
|
|
}
|
|
|
|
override protected bool ReleaseHandle() {
|
|
// NOTE: The SafeHandle class guarantees this will be called exactly once and is non-interrutible.
|
|
IntPtr handle = base.handle;
|
|
base.handle = IntPtr.Zero;
|
|
|
|
if (IntPtr.Zero != handle) {
|
|
ODBC32.SQL_HANDLE handleType = HandleType;
|
|
|
|
switch (handleType) {
|
|
case ODBC32.SQL_HANDLE.DBC:
|
|
// Disconnect happens in OdbcConnectionHandle.ReleaseHandle
|
|
case ODBC32.SQL_HANDLE.ENV:
|
|
case ODBC32.SQL_HANDLE.STMT:
|
|
ODBC32.RetCode retcode = UnsafeNativeMethods.SQLFreeHandle(handleType, handle);
|
|
Bid.TraceSqlReturn("<odbc.SQLFreeHandle|API|ODBC|RET> %08X{SQLRETURN}\n", retcode);
|
|
break;
|
|
|
|
case ODBC32.SQL_HANDLE.DESC:
|
|
// nothing to free on the handle
|
|
break;
|
|
|
|
// case 0: ThreadAbortException setting handle before HandleType
|
|
default:
|
|
Debug.Assert(ADP.PtrZero == handle, "unknown handle type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we ended up getting released, then we have to release
|
|
// our reference on our parent.
|
|
OdbcHandle parentHandle = _parentHandle;
|
|
_parentHandle = null;
|
|
if (null != parentHandle) {
|
|
parentHandle.DangerousRelease();
|
|
parentHandle = null;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal ODBC32.RetCode GetDiagnosticField (out string sqlState) {
|
|
short cbActual;
|
|
// ODBC (MSDN) documents it expects a buffer large enough to hold 5(+L'\0') unicode characters
|
|
StringBuilder sb = new StringBuilder(6);
|
|
ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetDiagFieldW(
|
|
HandleType,
|
|
this,
|
|
(short)1,
|
|
ODBC32.SQL_DIAG_SQLSTATE,
|
|
sb,
|
|
checked((short)(2*sb.Capacity)), // expects number of bytes, see \\kbinternal\kb\articles\294\1\69.HTM
|
|
out cbActual);
|
|
ODBC.TraceODBC(3, "SQLGetDiagFieldW", retcode);
|
|
if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)) {
|
|
sqlState = sb.ToString();
|
|
}
|
|
else {
|
|
sqlState = ADP.StrEmpty;
|
|
}
|
|
return retcode;
|
|
}
|
|
|
|
internal ODBC32.RetCode GetDiagnosticRecord(short record, out string sqlState, StringBuilder message, out int nativeError, out short cchActual) {
|
|
// ODBC (MSDN) documents it expects a buffer large enough to hold 4(+L'\0') unicode characters
|
|
StringBuilder sb = new StringBuilder(5);
|
|
ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetDiagRecW(HandleType, this, record, sb, out nativeError, message, checked((short)message.Capacity), out cchActual);
|
|
ODBC.TraceODBC(3, "SQLGetDiagRecW", retcode);
|
|
|
|
if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)) {
|
|
sqlState = sb.ToString();
|
|
}
|
|
else {
|
|
sqlState = ADP.StrEmpty;
|
|
}
|
|
return retcode;
|
|
}
|
|
}
|
|
|
|
sealed internal class OdbcDescriptorHandle : OdbcHandle {
|
|
|
|
internal OdbcDescriptorHandle(OdbcStatementHandle statementHandle, ODBC32.SQL_ATTR attribute) : base(statementHandle, attribute) {
|
|
}
|
|
|
|
internal ODBC32.RetCode GetDescriptionField(int i, ODBC32.SQL_DESC attribute, CNativeBuffer buffer, out int numericAttribute) {
|
|
ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetDescFieldW(this, checked((short)i), attribute, buffer, buffer.ShortLength, out numericAttribute);
|
|
ODBC.TraceODBC(3, "SQLGetDescFieldW", retcode);
|
|
return retcode;
|
|
}
|
|
|
|
internal ODBC32.RetCode SetDescriptionField1(short ordinal, ODBC32.SQL_DESC type, IntPtr value) {
|
|
ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetDescFieldW(this, ordinal, type, value, 0);
|
|
ODBC.TraceODBC(3, "SQLSetDescFieldW", retcode);
|
|
return retcode;
|
|
}
|
|
|
|
internal ODBC32.RetCode SetDescriptionField2(short ordinal, ODBC32.SQL_DESC type, HandleRef value) {
|
|
ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetDescFieldW(this, ordinal, type, value, 0);
|
|
ODBC.TraceODBC(3, "SQLSetDescFieldW", retcode);
|
|
return retcode;
|
|
}
|
|
}
|
|
|
|
}
|
|
|