SQLGetInfo - как использовать эту функцию

Я разработал приложение С#, которое подключается ко многим типам серверов баз данных, таких как Sql, Oracle, Mysql и т.д. Соединение было установлено с помощью ODBC.

Мне нужно найти тип сервера (тип СУБД), для которого было установлено соединение, так как пользователь вводит только имя DSN.

После нескольких часов серфинга я обнаружил, что мое требование возможно только через эту функцию SQLGetInfo. Но я не знаю, как справиться с этим в С#.

даже проверили этот Link1.

из приведенной выше ссылки, я обнаружил, что API ODBC только дает имя базы данных и Имя источника данных. Но мне нужно найти тип Datatbase, например, это соединение SQL или соединение Oracle или mySqlConnection.

Можно ли получить тип СУБД с именем DSN..?

Примечание. Я не хочу читать его из настройки реестра. Я пробовал это, и он работает, но у меня возникают проблемы с правами.!

Ответ 1

Это будет абсолютно трюк. Вот моя реализация, чтобы подключить функцию OdbcConnection GetInfoStringUnhandled. Бог мы любим размышления, я знаю, что я легенда;)

public enum SQL_INFO
{
    DATA_SOURCE_NAME,
    DRIVER_NAME,
    DRIVER_VER,
    ODBC_VER,
    SERVER_NAME,
    SEARCH_PATTERN_ESCAPE,
    DBMS_NAME,
    DBMS_VER,
    IDENTIFIER_CASE,
    IDENTIFIER_QUOTE_CHAR,
    CATALOG_NAME_SEPARATOR,
    DRIVER_ODBC_VER,
    GROUP_BY,
    KEYWORDS,
    ORDER_BY_COLUMNS_IN_SELECT,
    QUOTED_IDENTIFIER_CASE,
    SQL_OJ_CAPABILITIES_30,
    SQL_SQL92_RELATIONAL_JOIN_OPERATORS,
    SQL_OJ_CAPABILITIES_20

}

public static string GetInfoStringUnhandled(OdbcConnection ocn, SQL_INFO info)
{
    MethodInfo GetInfoStringUnhandled = ocn.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).First(c => c.Name == "GetInfoStringUnhandled");
    ParameterInfo SQL_INFO =
        GetInfoStringUnhandled.GetParameters()
            .First(c => (c.ParameterType.ToString() == "System.Data.Odbc.ODBC32+SQL_INFO"));
    Array EnumValues = SQL_INFO.ParameterType.GetEnumValues();
    foreach (var enumval in EnumValues) {
        if (enumval.ToString() == info.ToString()) {
            return Convert.ToString(GetInfoStringUnhandled.Invoke(ocn, new object[] { enumval }));
        }
    }
    return string.Empty;
}

private static void Main(string[] args)
{
    OdbcConnection ocn = new OdbcConnection("DSN=GENESIS");
    ocn.Open();
    Console.WriteLine(GetInfoStringUnhandled(ocn, SQL_INFO.DBMS_VER) + " " +
                      GetInfoStringUnhandled(ocn, SQL_INFO.DBMS_NAME));
}

Лучшая документация, которую я нашел, объясняя 47 возможностей использования SQLGetInfo, здесь https://mariadb.com/kb/en/sql-99/sqlgetinfo/

Тем не менее, OdbcConnection только что интегрировал перечисление с 19 возможностями. Ниже приведено дизассемблированное перечисление SQL_INFO System.Data.Odbc.ODBC32:

public enum SQL_INFO : ushort
{
    DATA_SOURCE_NAME = (ushort)2,
    DRIVER_NAME = (ushort)6,
    DRIVER_VER = (ushort)7,
    ODBC_VER = (ushort)10,
    SERVER_NAME = (ushort)13,
    SEARCH_PATTERN_ESCAPE = (ushort)14,
    DBMS_NAME = (ushort)17,
    DBMS_VER = (ushort)18,
    IDENTIFIER_CASE = (ushort)28,
    IDENTIFIER_QUOTE_CHAR = (ushort)29,
    CATALOG_NAME_SEPARATOR = (ushort)41,
    DRIVER_ODBC_VER = (ushort)77,
    GROUP_BY = (ushort)88,
    KEYWORDS = (ushort)89,
    ORDER_BY_COLUMNS_IN_SELECT = (ushort)90,
    QUOTED_IDENTIFIER_CASE = (ushort)93,
    SQL_OJ_CAPABILITIES_30 = (ushort)115,
    SQL_SQL92_RELATIONAL_JOIN_OPERATORS = (ushort)161,
    SQL_OJ_CAPABILITIES_20 = (ushort)65003
}

Как вы видите, вы можете просто вызвать метод GetInfoStringUnhandled с целым числом (ushort), которое вы хотите использовать. Это пример:

public static string GetInfoStringUnhandled(OdbcConnection ocn, ushort info)
{
    MethodInfo GetInfoStringUnhandled = ocn.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).First(c => c.Name == "GetInfoStringUnhandled");
    return Convert.ToString(GetInfoStringUnhandled.Invoke(ocn, new object[] { (ushort)info }));            
}

public static void Main(string[] args)
{
    OdbcConnection ocn = new OdbcConnection("DSN=GENESIS");
    ocn.Open();
    Console.WriteLine(GetInfoStringUnhandled(ocn, (ushort)10003)); //SQL_CATALOG_NAME returns Y
}

Ответ 2

Короткий ответ: Не надо. Попробуйте очень трудно найти управляемые эквиваленты. Нет документального способа получения этого дескриптора.

Длинный ответ: Параметр InfoType функции SQLGetInfo имеет 47 возможных значений. Ссылка. Вы можете получить шаблон регулярного выражения для цитируемых идентификаторов следующим образом:

DataTable dt = connection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation); 
string quotedIdentifierPattern = (string)dt.Rows[0][DbMetaDataColumnNames.QuotedIdentifierPattern];

Это позволит вам распознавать, но не создавать цитируемые идентификаторы. Можно с уверенностью предположить, что символ цитаты действительно является одним персонажем, поэтому вы можете получить его, просто сделав:

Regex.Unescape(quotedIdentifierPattern)[0];

(Требуется использовать .Unescape(), так как символ кавычки может быть специальным для regexen и, следовательно, экранированный.)

Большинство других применений для SQLInfo() можно аналогичным образом решить с помощью .GetSchema(). Если вы абсолютно, положительно должны использовать SQLGetInfo() для чего-то, я рекомендую используя частные методы .GetInfoInt16Unhandled(), .GetInfoInt32Unhandled() и ..GetInfoStringUnhandled() на OdbcConnection через отражение. Это при условии, что он будет нарушен без предупреждения.

Вы можете получить внутреннюю ручку через частный .ConnectionHandle член, но это в равной степени подвержено разрушению и гораздо менее удобно (потому что вам тоже нужно написать весь неуправляемый код взаимодействия).

Используйте ILSpy или Reflector чтобы получить больше подробности об осуществлении. Обратное проектирование внутренностей может во многих случаи указывают на полностью управляемое решение. Ссылка.


ИЛИ постройте этот пример кода MSDN для обнаружения версии с помощью разных команд, например

MySQL:     "SELECT version()";
Oracle:     "SELECT @@version, @@version_comment FROM dual",
SQLServer: "SELECT @@version";

Пример кода MSDN:

using System;
using System.Data;

namespace IDbConnectionSample {
   class Program {
      static void Main(string[] args) {
         IDbConnection connection;

         // First use a SqlClient connection
         connection = new System.Data.SqlClient.SqlConnection(@"Server=(localdb)\V11.0");
         Console.WriteLine("SqlClient\r\n{0}", GetServerVersion(connection));
         connection = new System.Data.SqlClient.SqlConnection(@"Server=(local);Integrated Security=true");
         Console.WriteLine("SqlClient\r\n{0}", GetServerVersion(connection));

         // Call the same method using ODBC 
         // NOTE: LocalDB requires the SQL Server 2012 Native Client ODBC driver
         connection = new System.Data.Odbc.OdbcConnection(@"Driver={SQL Server Native Client 11.0};Server=(localdb)\v11.0");
         Console.WriteLine("ODBC\r\n{0}", GetServerVersion(connection));
         connection = new System.Data.Odbc.OdbcConnection(@"Driver={SQL Server Native Client 11.0};Server=(local);Trusted_Connection=yes");
         Console.WriteLine("ODBC\r\n{0}", GetServerVersion(connection));

         // Call the same method using OLE DB
         connection = new System.Data.OleDb.OleDbConnection(@"Provider=SQLNCLI11;Server=(localdb)\v11.0;Trusted_Connection=yes;");
         Console.WriteLine("OLE DB\r\n{0}", GetServerVersion(connection));
         connection = new System.Data.OleDb.OleDbConnection(@"Provider=SQLNCLI11;Server=(local);Trusted_Connection=yes;");
         Console.WriteLine("OLE DB\r\n{0}", GetServerVersion(connection));
         }

      public static string GetServerVersion(IDbConnection connection) {
         // Ensure that the connection is opened (otherwise executing the command will fail)
         ConnectionState originalState = connection.State;
         if (originalState != ConnectionState.Open)
            connection.Open();
         try {
            // Create a command to get the server version 
            IDbCommand command = connection.CreateCommand();
            command.CommandText = "SELECT @@version"; //<- HERE  
            //try out the different commands by passing the CommandText as a parameter
            return (string)command.ExecuteScalar();
         }
         finally {
            // Close the connection if that how we got it 
            if (originalState == ConnectionState.Closed)
               connection.Close();
         }
      }
   }
}

ИЛИ вы могли бы сделать что-то вроде других, предлагая немного более элегантно.

Примечание: это копирование/вставка задания в ответ @FabianStern - кредит автору. Я просто делал это менее процедурным и более ортодоксальным, поскольку я не мог выдержать каскадное Try-Catch):

protected static DBType GetDBType(string odbcConnStr)
{
 var dbType = DBType.UNSUPPORTED;
 try
 {
  using (var cn = new OdbcConnection(odbcConnStr))
  {
    if (cn.State != ConnectionState.Open) cn.Open();
    dbType = GetDbType(cn, dbType)
    if (dbType > 0) return dbType;

    var sqlVersionQuery = "SELECT version()"; 
    dbType = GetDbType(cn, sqlVersionQuery, DBType.MYSQL)
    if (dbType > 0) return dbType;

    sqlVersionQuery = "SELECT @@version, @@version_comment FROM dual"; 
    dbType = GetDbType(cn, sqlVersionQuery, DBType.Oracle)
    if (dbType > 0) return dbType;

    sqlVersionQuery = "SELECT @@version"; 
    dbType = GetDbType(cn, sqlVersionQuery, DBType.MSSQL)
    if (dbType > 0) return dbType;
  }
 }
 catch(Exception connEx) { }
 return dbType;
}

public enum DBType
{
    UNSUPPORTED = 0,
    MYSQL = 1,
    ORACLE = 2,
    MSSQL = 3,
    JET = 4
}

private static DBType GetDBType(OdbcConnection cn, DBType dbType)
{
  try
  {
    if (cn.Driver == "odbcjt32.dll") dbType = DBType.JET;
  }
  catch(Exception ex) { }
  return dbType;
}

private static DBType GetDbType(OdbcConnection cn, string sqlVersionQuery, DBType dbType)
{
  try
  {
  using (var cmd = cn.CreateCommand()) {
  cmd.CommandText = sqlVersionQuery;
    try
    {
       using (var reader = cmd.ExecuteReader()) 
       {
           if (reader.HasRows) return dbType;
       }
    }
    catch (Exception ex) { }
  }}
  catch (Exception cmdEx) { }           
  }
 return dbType;
}

Ответ 3

Вы пытались проанализировать свойство .Driver для OdbcConnection? Он покажет вам используемый драйвер оболочки базы данных для подключения. Эти сопоставления можно найти также в реестре по адресу HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ODBC

Иногда это просто имя .dll(например, для Microsoft Excel Driver), но оно может дать вам подсказку.

Вы также можете выполнять команды, специфичные для базы данных, для получения версии базы данных с сервера:

   static void Main(string[] args)
    {
        var cn = new OdbcConnection("DSN=mysql1");
        Console.WriteLine("DBType: {0}", GetDbType(cn));
        Console.Read();
    }

    public enum DbType
    {
        UNSUPPORTED = 0,
        MYSQL = 1,
        ORACLE = 2,
        MSSQL = 3,
        POSTGRESQL = 4,
        JET = 5
    }

    public static DbType GetDbType(OdbcConnection cn)
    {
        DbType t = DbType.UNSUPPORTED;
            try
            {
                if (cn.State != ConnectionState.Open) cn.Open();

                if (cn.Driver == "odbcjt32.dll")
                {
                    return DbType.JET;
                }

                var cmd = cn.CreateCommand();
                string outstring = "";
                cmd.CommandText = "SELECT * FROM v$version";
                try
                {
                    var reader = cmd.ExecuteReader();
                    if (reader.HasRows)
                    {
                        reader.Read();
                        outstring = String.Format("{0}", reader.GetString(0));

                    }
                }
                catch (Exception)
                {
                    cmd = cn.CreateCommand();
                    cmd.CommandText = "SELECT @@version, @@version_comment FROM dual";
                    try
                    {
                        var reader = cmd.ExecuteReader();
                        if (reader.HasRows)
                        {
                            reader.Read();
                            outstring = String.Format("{0} {1}", reader.GetString(0), reader.GetString(1));

                        }
                    }
                    catch (Exception)
                    {
                        cmd = cn.CreateCommand();
                        cmd.CommandText = "SELECT @@version";
                        try
                        {
                            var reader = cmd.ExecuteReader();
                            if (reader.HasRows)
                            {
                                reader.Read();
                                outstring = String.Format("{0}", reader.GetString(0));

                            }
                        }
                        catch (Exception)
                        {
                            cmd = cn.CreateCommand();
                            cmd.CommandText = "SELECT version()";
                            try
                            {
                                var reader = cmd.ExecuteReader();
                                if (reader.HasRows)
                                {
                                    reader.Read();
                                    outstring = String.Format("{0}", reader.GetString(0));

                                }
                            }
                            catch (Exception)
                            {
                            }
                        }
                    }
                }

                outstring = outstring.ToUpper();

                if (outstring.Contains("MYSQL"))
                {
                    t = DbType.MYSQL;
                }
                else if (outstring.Contains("ORACLE"))
                {
                    t = DbType.ORACLE;
                }
                else if (outstring.Contains("SQL SERVER"))
                {
                    t = DbType.MSSQL;
                }
                else if (outstring.Contains("POSTGRESQL"))
                {
                    t = DbType.POSTGRESQL;
                }


            }
            catch (Exception E)
            {

            }
        return t;

    }