File: System\Data\Odbc\OdbcConnection.cs
Project: ndp\fx\src\data\System.Data.csproj (System.Data)
//------------------------------------------------------------------------------
// <copyright file="OdbcConnection.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.Data.ProviderBase;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using SysTx = System.Transactions;
 
namespace System.Data.Odbc {
 
    [DefaultEvent("InfoMessage")]
    public sealed partial class OdbcConnection : DbConnection, ICloneable {
        private int connectionTimeout = ADP.DefaultConnectionTimeout;
 
        private OdbcInfoMessageEventHandler infoMessageEventHandler;
        private WeakReference weakTransaction;
 
        private OdbcConnectionHandle _connectionHandle;
        private ConnectionState _extraState;    // extras, like Executing and Fetching, that we add to the State.
 
        public OdbcConnection(string connectionString) : this() {
            ConnectionString = connectionString;
        }
 
        private OdbcConnection(OdbcConnection connection) : this() { // Clone
            CopyFrom(connection);
            connectionTimeout = connection.connectionTimeout;
        }
 
        internal OdbcConnectionHandle ConnectionHandle {
            get {
                return _connectionHandle;
            }
            set {
                Debug.Assert(null == _connectionHandle, "reopening a connection?");
                _connectionHandle = value;
            }
        }
 
        [
        DefaultValue(""),
        Editor("Microsoft.VSDesigner.Data.Odbc.Design.OdbcConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
#pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
        RecommendedAsConfigurable(true),
#pragma warning restore 618
        SettingsBindableAttribute(true),
        RefreshProperties(RefreshProperties.All),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.OdbcConnection_ConnectionString),
        ]
        override public string ConnectionString {
            get {
                return ConnectionString_Get();
            }
            set {
                ConnectionString_Set(value);
            }
        }
 
        [
        DefaultValue(ADP.DefaultConnectionTimeout),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.OdbcConnection_ConnectionTimeout),
        ]
        new public int ConnectionTimeout {
            get {
                return connectionTimeout;
            }
            set {
                if (value < 0)
                    throw ODBC.NegativeArgument();
                if (IsOpen)
                    throw ODBC.CantSetPropertyOnOpenConnection();
                connectionTimeout = value;
            }
        }
 
        [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResDescriptionAttribute(Res.OdbcConnection_Database),
        ]
        override public string Database {
            get {
                if (IsOpen && !ProviderInfo.NoCurrentCatalog) {
                    //Note: CURRENT_CATALOG may not be supported by the current driver.  In which
                    //case we ignore any error (without throwing), and just return string.empty.
                    //As we really don't want people to have to have try/catch around simple properties
                    return GetConnectAttrString(ODBC32.SQL_ATTR.CURRENT_CATALOG);
                }
                //Database is not available before open, and its not worth parsing the
                //connection string over.
                return String.Empty;
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResDescriptionAttribute(Res.OdbcConnection_DataSource),
        ]
        override public string DataSource {
            get {
                if (IsOpen) {
                    // note: This will return an empty string if the driver keyword was used to connect
                    // see ODBC3.0 Programmers Reference, SQLGetInfo
                    //
                    return GetInfoStringUnhandled(ODBC32.SQL_INFO.SERVER_NAME, true);
                }
                return String.Empty;
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResDescriptionAttribute(Res.OdbcConnection_ServerVersion),
        ]
        override public string ServerVersion {
            get {
                return InnerConnection.ServerVersion;
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResDescriptionAttribute(Res.DbConnection_State),
        ]
        override public ConnectionState State {
            get {
                return InnerConnection.State;
            }
        }
 
        internal OdbcConnectionPoolGroupProviderInfo ProviderInfo {
            get {
                Debug.Assert(null != this.PoolGroup, "PoolGroup must never be null when accessing ProviderInfo");
                return (OdbcConnectionPoolGroupProviderInfo)this.PoolGroup.ProviderInfo;
            }
        }
 
        internal ConnectionState InternalState {
            get {
                return (this.State | _extraState);
            }
        }
 
        internal bool IsOpen {
            get {
                return (InnerConnection is OdbcConnectionOpen);
            }
        }
 
        internal OdbcTransaction LocalTransaction {
 
            get {
                OdbcTransaction result = null;
                if (null != weakTransaction) {
                    result = ((OdbcTransaction)weakTransaction.Target);
                }
                return result;
            }
 
            set {
                weakTransaction = null;
 
                if (null != value) {
                    weakTransaction = new WeakReference((OdbcTransaction)value);
                }
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResDescriptionAttribute(Res.OdbcConnection_Driver),
        ]
        public string Driver {
            get {
                if(IsOpen) {
                    if (ProviderInfo.DriverName == null) {
                        ProviderInfo.DriverName = GetInfoStringUnhandled(ODBC32.SQL_INFO.DRIVER_NAME);
                    }
                    return ProviderInfo.DriverName;
                }
                return ADP.StrEmpty;
            }
        }
 
        internal bool IsV3Driver {
            get {
                if (ProviderInfo.DriverVersion == null) {
                    ProviderInfo.DriverVersion = GetInfoStringUnhandled(ODBC32.SQL_INFO.DRIVER_ODBC_VER);
                    // protected against null and index out of range. Number cannot be bigger than 99
                    if (ProviderInfo.DriverVersion != null && ProviderInfo.DriverVersion.Length>=2) {
                        try {   // mdac 89269: driver may return malformatted string
                            ProviderInfo.IsV3Driver = (int.Parse(ProviderInfo.DriverVersion.Substring(0,2), CultureInfo.InvariantCulture) >= 3);
                        }
                        catch (System.FormatException e) {
                            ProviderInfo.IsV3Driver = false;
                            ADP.TraceExceptionWithoutRethrow(e);
                        }
                    }
                    else {
                        ProviderInfo.DriverVersion = "";
                    }
                }
                return ProviderInfo.IsV3Driver;
            }
        }
 
        [
        ResCategoryAttribute(Res.DataCategory_InfoMessage),
        ResDescriptionAttribute(Res.DbConnection_InfoMessage),
        ]
        public event OdbcInfoMessageEventHandler InfoMessage {
            add {
                infoMessageEventHandler += value;
            }
            remove {
                infoMessageEventHandler -= value;
            }
        }
 
        internal char EscapeChar(string method) {
            CheckState(method);
            if (!ProviderInfo.HasEscapeChar) {
                string escapeCharString;
                escapeCharString = GetInfoStringUnhandled(ODBC32.SQL_INFO.SEARCH_PATTERN_ESCAPE);
                Debug.Assert((escapeCharString.Length <= 1), "Can't handle multichar quotes");
                ProviderInfo.EscapeChar = (escapeCharString.Length==1)?escapeCharString[0]:QuoteChar(method)[0];
            }
            return ProviderInfo.EscapeChar;
        }
 
        internal string QuoteChar(string method) {
            CheckState(method);
            if (!ProviderInfo.HasQuoteChar) {
                string quoteCharString;
                quoteCharString = GetInfoStringUnhandled(ODBC32.SQL_INFO.IDENTIFIER_QUOTE_CHAR);
                Debug.Assert((quoteCharString.Length <= 1), "Can't handle multichar quotes");
                ProviderInfo.QuoteChar = (1==quoteCharString.Length)?quoteCharString:"\0";
            }
            return ProviderInfo.QuoteChar;
        }
 
        new public OdbcTransaction BeginTransaction() {
            return BeginTransaction(IsolationLevel.Unspecified);
            }
 
        new public OdbcTransaction BeginTransaction(IsolationLevel isolevel) {
            return (OdbcTransaction)InnerConnection.BeginTransaction(isolevel);
            }
 
        private void RollbackDeadTransaction() {
            WeakReference weak = weakTransaction;
            if ((null != weak) && !weak.IsAlive) {
                weakTransaction = null;
                ConnectionHandle.CompleteTransaction(ODBC32.SQL_ROLLBACK);
            }
        }
 
        override public void ChangeDatabase(string value) {
            InnerConnection.ChangeDatabase(value);
            }
 
        internal void CheckState(string method) {
            ConnectionState state = InternalState;
            if (ConnectionState.Open != state) {
                throw ADP.OpenConnectionRequired(method, state); // MDAC 68323
            }
        }
 
        object ICloneable.Clone() {
            OdbcConnection clone = new OdbcConnection(this);
            Bid.Trace("<odbc.OdbcConnection.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
            return clone;
        }
 
        internal bool ConnectionIsAlive(Exception innerException) {
            if (IsOpen) {
                if (!ProviderInfo.NoConnectionDead) {
                    int isDead = GetConnectAttr(ODBC32.SQL_ATTR.CONNECTION_DEAD, ODBC32.HANDLER.IGNORE);
                    if (ODBC32.SQL_CD_TRUE == isDead) {
                        Close();
                        throw ADP.ConnectionIsDisabled(innerException);
                    }
                }
                // else connection is still alive or attribute not supported
                return true;
            }
            return false;
        }
 
        new public OdbcCommand CreateCommand() {
            return new OdbcCommand(String.Empty, this);
            }
 
        internal OdbcStatementHandle CreateStatementHandle() {
            return new OdbcStatementHandle(ConnectionHandle);
        }
 
        override public void Close() {
            InnerConnection.CloseConnection(this, ConnectionFactory);
 
            OdbcConnectionHandle connectionHandle = _connectionHandle;
 
            if (null != connectionHandle) {
                _connectionHandle = null;
 
                // If there is a pending transaction, automatically rollback.
                WeakReference weak = this.weakTransaction;
                if (null != weak) {
                    this.weakTransaction = null;
                    IDisposable transaction = weak.Target as OdbcTransaction;
                    if ((null != transaction) && weak.IsAlive) {
                        transaction.Dispose();
                    }
                    // else transaction will be rolled back when handle is disposed
                }
                connectionHandle.Dispose();
            }
        }
 
        private void DisposeMe(bool disposing) { // MDAC 65459
        }
 
        public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) {
            EnlistDistributedTransactionHelper(transaction);
        }        
        
        internal string GetConnectAttrString(ODBC32.SQL_ATTR attribute) {
            string value = "";
            Int32 cbActual = 0;
            byte[] buffer = new byte[100];
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            if (null != connectionHandle) {
                ODBC32.RetCode retcode = connectionHandle.GetConnectionAttribute(attribute, buffer, out cbActual);
                if (buffer.Length+2 <= cbActual) {
                    // 2 bytes for unicode null-termination character
                    // retry with cbActual because original buffer was too small
                    buffer = new byte[cbActual + 2];
                    retcode = connectionHandle.GetConnectionAttribute(attribute, buffer, out cbActual);
                }
                if ((ODBC32.RetCode.SUCCESS == retcode) || (ODBC32.RetCode.SUCCESS_WITH_INFO == retcode)) {
                    value = Encoding.Unicode.GetString(buffer, 0, Math.Min(cbActual, buffer.Length));
                }
                else if (retcode == ODBC32.RetCode.ERROR) {
                    string sqlstate = GetDiagSqlState();
                    if (("HYC00" == sqlstate) || ("HY092" == sqlstate) || ("IM001" == sqlstate)) {
                        FlagUnsupportedConnectAttr(attribute);
                    }
                    // not throwing errors if not supported or other failure
                }
            }
            return value;
        }
 
        internal int GetConnectAttr(ODBC32.SQL_ATTR attribute, ODBC32.HANDLER handler) {
            Int32 retval = -1;
            Int32 cbActual = 0;
            byte[] buffer = new byte[4];
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            if (null != connectionHandle) {
                ODBC32.RetCode retcode = connectionHandle.GetConnectionAttribute(attribute, buffer, out cbActual);
                
                if ((ODBC32.RetCode.SUCCESS == retcode) || (ODBC32.RetCode.SUCCESS_WITH_INFO == retcode)) {
                    retval = BitConverter.ToInt32(buffer, 0);
                }
                else {
                    if (retcode == ODBC32.RetCode.ERROR) {
                        string sqlstate = GetDiagSqlState();
                        if (("HYC00" == sqlstate) || ("HY092" == sqlstate) || ("IM001" == sqlstate)) {
                            FlagUnsupportedConnectAttr(attribute);
                        }
                    }
                    if (handler == ODBC32.HANDLER.THROW) {
                        this.HandleError(connectionHandle, retcode);
                    }
                }
            }
            return retval;
        }
 
        private string GetDiagSqlState () {
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            string sqlstate;
            connectionHandle.GetDiagnosticField(out sqlstate);
            return sqlstate;
        }
 
        internal ODBC32.RetCode GetInfoInt16Unhandled(ODBC32.SQL_INFO info, out Int16 resultValue) {
            byte[] buffer = new byte[2];
            ODBC32.RetCode retcode = ConnectionHandle.GetInfo1(info, buffer);
            resultValue = BitConverter.ToInt16(buffer, 0);
            return retcode;
        }
 
        internal ODBC32.RetCode GetInfoInt32Unhandled(ODBC32.SQL_INFO info, out Int32 resultValue) {
            byte[] buffer = new byte[4];
            ODBC32.RetCode retcode = ConnectionHandle.GetInfo1(info, buffer);
            resultValue = BitConverter.ToInt32(buffer, 0);
            return retcode;
        }
 
        private Int32 GetInfoInt32Unhandled(ODBC32.SQL_INFO infotype) {
            byte[] buffer = new byte[4];
            ConnectionHandle.GetInfo1(infotype, buffer);
            return BitConverter.ToInt32(buffer, 0);
        }
        
        internal string GetInfoStringUnhandled(ODBC32.SQL_INFO info) {
            return GetInfoStringUnhandled(info, false);
        }
 
        private string GetInfoStringUnhandled(ODBC32.SQL_INFO info, bool handleError) {
            //SQLGetInfo
            string value = null;
            Int16 cbActual  = 0;
            byte[] buffer = new byte[100];
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            if (null != connectionHandle) {
                ODBC32.RetCode retcode = connectionHandle.GetInfo2(info, buffer, out cbActual);
                if (buffer.Length < cbActual-2) {
                    // 2 bytes for unicode null-termination character
                    // retry with cbActual because original buffer was too small
                    buffer = new byte[cbActual + 2];
                    retcode = connectionHandle.GetInfo2(info, buffer, out cbActual);
                }
                if (retcode == ODBC32.RetCode.SUCCESS || retcode == ODBC32.RetCode.SUCCESS_WITH_INFO) {
                    value = Encoding.Unicode.GetString(buffer, 0, Math.Min(cbActual, buffer.Length));
                }
                else if (handleError) {
                    this.HandleError(ConnectionHandle, retcode);
                }
            }
            else if (handleError) {
                value = "";
            }
            return value;
        }
 
        // non-throwing HandleError
        internal Exception HandleErrorNoThrow(OdbcHandle hrHandle, ODBC32.RetCode retcode) {
 
            Debug.Assert(retcode!=ODBC32.RetCode.INVALID_HANDLE, "retcode must never be ODBC32.RetCode.INVALID_HANDLE");
 
            switch(retcode) {
            case ODBC32.RetCode.SUCCESS:
                break;
            case ODBC32.RetCode.SUCCESS_WITH_INFO: {
                //Optimize to only create the event objects and obtain error info if
                //the user is really interested in retriveing the events...
                if (infoMessageEventHandler != null) {
                    OdbcErrorCollection errors = ODBC32.GetDiagErrors(null, hrHandle, retcode);
                    errors.SetSource(this.Driver);
                    OnInfoMessage(new OdbcInfoMessageEventArgs(errors));
                }
                break;
            }
            default:
                OdbcException e = OdbcException.CreateException(ODBC32.GetDiagErrors(null, hrHandle, retcode), retcode);
                if (e != null) {
                    e.Errors.SetSource(this.Driver);
                }
                ConnectionIsAlive(e);        // this will close and throw if the connection is dead
                return (Exception)e;
            }
            return null;
        }
 
        internal void HandleError(OdbcHandle hrHandle, ODBC32.RetCode retcode) {
            Exception e = HandleErrorNoThrow(hrHandle, retcode);
            switch(retcode) {
            case ODBC32.RetCode.SUCCESS:
            case ODBC32.RetCode.SUCCESS_WITH_INFO:
                Debug.Assert(null == e, "success exception");
                break;
            default:
                Debug.Assert(null != e, "failure without exception");
                throw e;
            }
        }
 
        override public void Open() {
            InnerConnection.OpenConnection(this, ConnectionFactory);
 
            // SQLBUDT #276132 - need to manually enlist in some cases, because
            // native ODBC doesn't know about SysTx transactions.
            if (ADP.NeedManualEnlistment()) {
                EnlistTransaction(SysTx.Transaction.Current);
            }
        }
 
        private void OnInfoMessage(OdbcInfoMessageEventArgs args) {
            if (null != infoMessageEventHandler) {
                try {
                    infoMessageEventHandler(this, args);
                }
                catch (Exception e) {
                    // 
                    if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
                        throw;
                    }
                    ADP.TraceExceptionWithoutRethrow(e);
                }
            }
        }
 
        static public void ReleaseObjectPool() {
            (new OdbcPermission(PermissionState.Unrestricted)).Demand();
            OdbcEnvironment.ReleaseObjectPool();
        }
 
        internal OdbcTransaction SetStateExecuting(string method, OdbcTransaction transaction) { // MDAC 69003
            if (null != weakTransaction) { // transaction may exist
                OdbcTransaction weak = (weakTransaction.Target as OdbcTransaction);
                if (transaction != weak) { // transaction doesn't exist
                    if (null == transaction) { // transaction exists
                        throw ADP.TransactionRequired(method);
                    }
                    if (this!= transaction.Connection) {
                        // transaction can't have come from this connection
                        throw ADP.TransactionConnectionMismatch();
                    }
                    // if transaction is zombied, we don't know the original connection
                    transaction = null; // MDAC 69264
                }
            }
            else if (null != transaction) { // no transaction started
                if (null != transaction.Connection) {
                    // transaction can't have come from this connection
                    throw ADP.TransactionConnectionMismatch();
                }
                // if transaction is zombied, we don't know the original connection
                transaction = null; // MDAC 69264
            }
            ConnectionState state = InternalState;
            if (ConnectionState.Open != state) {
                NotifyWeakReference(OdbcReferenceCollection.Recover); // recover for a potentially finalized reader
 
                state = InternalState;
                if (ConnectionState.Open != state) {
                    if (0 != (ConnectionState.Fetching & state)) {
                        throw ADP.OpenReaderExists();
                    }
                    throw ADP.OpenConnectionRequired(method, state);
                }
            }
            return transaction;
        }
 
        // This adds a type to the list of types that are supported by the driver
        // (don't need to know that for all the types)
        //
 
        internal void SetSupportedType (ODBC32.SQL_TYPE sqltype) {
            ODBC32.SQL_CVT sqlcvt;
 
            switch (sqltype) {
                case ODBC32.SQL_TYPE.NUMERIC: {
                    sqlcvt = ODBC32.SQL_CVT.NUMERIC;
                    break;
                }
                case ODBC32.SQL_TYPE.WCHAR: {
                    sqlcvt = ODBC32.SQL_CVT.WCHAR;
                    break;
                }
                case ODBC32.SQL_TYPE.WVARCHAR: {
                    sqlcvt = ODBC32.SQL_CVT.WVARCHAR;
                    break;
                }
                case ODBC32.SQL_TYPE.WLONGVARCHAR: {
                    sqlcvt = ODBC32.SQL_CVT.WLONGVARCHAR;
                    break;
                }
                default:
                    // other types are irrelevant at this time
                    return;
            }
            ProviderInfo.TestedSQLTypes |= (int)sqlcvt;
            ProviderInfo.SupportedSQLTypes |= (int)sqlcvt;
        }
 
        internal void FlagRestrictedSqlBindType(ODBC32.SQL_TYPE sqltype) {
            ODBC32.SQL_CVT sqlcvt;
 
            switch (sqltype) {
                case ODBC32.SQL_TYPE.NUMERIC: {
                    sqlcvt = ODBC32.SQL_CVT.NUMERIC;
                    break;
                }
                case ODBC32.SQL_TYPE.DECIMAL: {
                    sqlcvt = ODBC32.SQL_CVT.DECIMAL;
                    break;
                }
                default:
                    // other types are irrelevant at this time
                    return;
            }
            ProviderInfo.RestrictedSQLBindTypes |= (int)sqlcvt;
        }
 
        internal void FlagUnsupportedConnectAttr (ODBC32.SQL_ATTR Attribute) {
            switch (Attribute) {
                case ODBC32.SQL_ATTR.CURRENT_CATALOG:
                    ProviderInfo.NoCurrentCatalog = true;
                    break;
                case ODBC32.SQL_ATTR.CONNECTION_DEAD:
                    ProviderInfo.NoConnectionDead = true;
                    break;
                default:
                    Debug.Assert (false, "Can't flag unknown Attribute");
                    break;
            }
        }
 
        internal void FlagUnsupportedStmtAttr (ODBC32.SQL_ATTR Attribute) {
            switch (Attribute) {
                case ODBC32.SQL_ATTR.QUERY_TIMEOUT:
                    ProviderInfo.NoQueryTimeout = true;
                    break;
                case (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE:
                    ProviderInfo.NoSqlSoptSSNoBrowseTable = true;
                    break;
                case (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS:
                    ProviderInfo.NoSqlSoptSSHiddenColumns = true;
                    break;
                default:
                    Debug.Assert (false, "Can't flag unknown Attribute");
                    break;
            }
        }
 
        internal void FlagUnsupportedColAttr (ODBC32.SQL_DESC v3FieldId, ODBC32.SQL_COLUMN v2FieldId) {
            if (IsV3Driver) {
                switch ( v3FieldId){
                    case (ODBC32.SQL_DESC)ODBC32.SQL_CA_SS.COLUMN_KEY:
                        // SSS_WARNINGS_OFF
                        ProviderInfo.NoSqlCASSColumnKey = true;
                        break;
                        // SSS_WARNINGS_ON
                    default:
                        Debug.Assert (false, "Can't flag unknown Attribute");
                        break;
                }
            }
            else {
                switch ( v2FieldId) {
                    default:
                        Debug.Assert (false, "Can't flag unknown Attribute");
                        break;
                }
            }
        }
 
        internal Boolean SQLGetFunctions(ODBC32.SQL_API odbcFunction) {
            //SQLGetFunctions
            ODBC32.RetCode retcode;
            Int16 fExists;
            Debug.Assert ((Int16) odbcFunction != 0,"SQL_API_ALL_FUNCTIONS is not supported");
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            if (null != connectionHandle) {
                retcode = connectionHandle.GetFunctions(odbcFunction, out fExists);
            }
            else {
                Debug.Assert (false, "GetFunctions called and ConnectionHandle is null (connection is disposed?)");
                throw ODBC.ConnectionClosed();
            }
 
            if(retcode != ODBC32.RetCode.SUCCESS)
                this.HandleError(connectionHandle, retcode);
 
            if (fExists == 0){
                return false;
            }
            else {
                return true;
            }
 
        }
 
        internal bool TestTypeSupport (ODBC32.SQL_TYPE sqltype){
            ODBC32.SQL_CONVERT sqlconvert;
            ODBC32.SQL_CVT sqlcvt;
 
            // we need to convert the sqltype to sqlconvert and sqlcvt first
            //
            switch (sqltype) {
                case ODBC32.SQL_TYPE.NUMERIC: {
                    sqlconvert = ODBC32.SQL_CONVERT.NUMERIC;
                    sqlcvt = ODBC32.SQL_CVT.NUMERIC;
                    break;
                }
                case ODBC32.SQL_TYPE.WCHAR: {
                    sqlconvert = ODBC32.SQL_CONVERT.CHAR;
                    sqlcvt = ODBC32.SQL_CVT.WCHAR;
                    break;
                }
                case ODBC32.SQL_TYPE.WVARCHAR: {
                    sqlconvert = ODBC32.SQL_CONVERT.VARCHAR;
                    sqlcvt = ODBC32.SQL_CVT.WVARCHAR;
                    break;
                }
                case ODBC32.SQL_TYPE.WLONGVARCHAR: {
                    sqlconvert = ODBC32.SQL_CONVERT.LONGVARCHAR;
                    sqlcvt = ODBC32.SQL_CVT.WLONGVARCHAR;
                    break;
                }
                default:
                    Debug.Assert(false, "Testing that sqltype is currently not supported");
                    return false;
            }
            // now we can check if we have already tested that type
            // if not we need to do so
            if (0 == (ProviderInfo.TestedSQLTypes & (int)sqlcvt)) {
                int flags;
 
                flags = GetInfoInt32Unhandled((ODBC32.SQL_INFO)sqlconvert);
                flags = flags & (int)sqlcvt;
 
                ProviderInfo.TestedSQLTypes |= (int)sqlcvt;
                ProviderInfo.SupportedSQLTypes |= flags;
            }
 
            // now check if the type is supported and return the result
            //
            return (0 != (ProviderInfo.SupportedSQLTypes & (int)sqlcvt));
        }
 
        internal bool TestRestrictedSqlBindType (ODBC32.SQL_TYPE sqltype){
            ODBC32.SQL_CVT sqlcvt;
            switch (sqltype) {
                case ODBC32.SQL_TYPE.NUMERIC: {
                    sqlcvt = ODBC32.SQL_CVT.NUMERIC;
                    break;
                }
                case ODBC32.SQL_TYPE.DECIMAL: {
                    sqlcvt = ODBC32.SQL_CVT.DECIMAL;
                    break;
                }
                default:
                    Debug.Assert(false, "Testing that sqltype is currently not supported");
                    return false;
            }
            return (0 != (ProviderInfo.RestrictedSQLBindTypes & (int)sqlcvt));
        }
 
        // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355)
        [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
        override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
            IntPtr hscp;
 
            Bid.ScopeEnter(out hscp, "<prov.OdbcConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel);
            try {
 
                DbTransaction transaction = InnerConnection.BeginTransaction(isolationLevel);
 
                // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and 
                //   subsequently leaves open the possibility that the outer connection could be GC'ed before the DbTransaction
                //   is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable
                //   until the completion of BeginTransaction with KeepAlive
                GC.KeepAlive(this);
 
                return transaction;
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        internal OdbcTransaction Open_BeginTransaction(IsolationLevel isolevel) {
            OdbcConnection.ExecutePermission.Demand();
 
            CheckState(ADP.BeginTransaction); // MDAC 68323
 
            RollbackDeadTransaction();
 
            if ((null != this.weakTransaction) && this.weakTransaction.IsAlive) { // regression from Dispose/Finalize work
                throw ADP.ParallelTransactionsNotSupported(this);
            }
 
            //Use the default for unspecified.
            switch(isolevel) {
            case IsolationLevel.Unspecified:
            case IsolationLevel.ReadUncommitted:
            case IsolationLevel.ReadCommitted:
            case IsolationLevel.RepeatableRead:
            case IsolationLevel.Serializable:
            case IsolationLevel.Snapshot:
                break;
            case IsolationLevel.Chaos:
                throw ODBC.NotSupportedIsolationLevel(isolevel);
            default:
                throw ADP.InvalidIsolationLevel(isolevel);
            };
 
            //Start the transaction
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            ODBC32.RetCode retcode = connectionHandle.BeginTransaction(ref isolevel);
            if (retcode == ODBC32.RetCode.ERROR) {
                HandleError(connectionHandle, retcode);
            }
            OdbcTransaction transaction = new OdbcTransaction(this, isolevel, connectionHandle);
            this.weakTransaction = new WeakReference(transaction); // MDAC 69188
            return transaction;
        }
 
        internal void Open_ChangeDatabase(string value) {
            OdbcConnection.ExecutePermission.Demand();
 
            CheckState(ADP.ChangeDatabase);
 
            // Database name must not be null, empty or whitspace
            if ((null == value) || (0 == value.Trim().Length)) { // MDAC 62679
                throw ADP.EmptyDatabaseName();
            }
            if (1024 < value.Length*2+2) {
                throw ADP.DatabaseNameTooLong();
            }
            RollbackDeadTransaction();
 
            //Set the database
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            ODBC32.RetCode retcode = connectionHandle.SetConnectionAttribute3(ODBC32.SQL_ATTR.CURRENT_CATALOG, value, checked((Int32)value.Length*2));
 
            if (retcode != ODBC32.RetCode.SUCCESS) {
                HandleError(connectionHandle, retcode);
            }
        }
 
        internal void Open_EnlistTransaction(SysTx.Transaction transaction) {
            OdbcConnection.VerifyExecutePermission();
 
            if ((null != this.weakTransaction) && this.weakTransaction.IsAlive) {
                throw ADP.LocalTransactionPresent();
            }
 
            SysTx.IDtcTransaction oleTxTransaction = ADP.GetOletxTransaction(transaction);
 
            OdbcConnectionHandle connectionHandle = ConnectionHandle;
            ODBC32.RetCode retcode;
            if (null == oleTxTransaction) {
                retcode = connectionHandle.SetConnectionAttribute2(ODBC32.SQL_ATTR.SQL_COPT_SS_ENLIST_IN_DTC, (IntPtr) ODBC32.SQL_DTC_DONE, ODBC32.SQL_IS_PTR);
            }
            else {
                retcode = connectionHandle.SetConnectionAttribute4(ODBC32.SQL_ATTR.SQL_COPT_SS_ENLIST_IN_DTC,  oleTxTransaction, ODBC32.SQL_IS_PTR);
            }
 
            if (retcode != ODBC32.RetCode.SUCCESS) {
                HandleError(connectionHandle, retcode);
            }
 
            // Tell the base class about our enlistment
            ((OdbcConnectionOpen)InnerConnection).EnlistedTransaction = transaction;
        }
 
        internal string Open_GetServerVersion() {
            //SQLGetInfo - SQL_DBMS_VER
            return GetInfoStringUnhandled(ODBC32.SQL_INFO.DBMS_VER, true);
        }
    }
}