using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Core; namespace Sarand.Net.Standard.Base.Dbas { //======================================================================= public static class OdbcUtil { //------------------------------------------------------------------- // O d b c D r i v e r s //------------------------------------------------------------------- [DllImport("odbccp32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SQLGetInstalledDriversW (char[] lpszBuf, ushort cbufMax, out ushort pcbBufOut); /// /// Gets the ODBC driver names from the SQLGetInstalledDrivers function. /// /// a string array containing the ODBC driver names, if the call to SQLGetInstalledDrivers was successfull; null, otherwise. public static string[] GetOdbcDriverNames () { string[] odbcDriverNames = null; char[] driverNamesBuffer = new char[ushort.MaxValue]; ushort size; bool succeeded = SQLGetInstalledDriversW(driverNamesBuffer, ushort.MaxValue, out size); if (succeeded == true) { char[] driverNames = new char[size - 1]; Array.Copy(driverNamesBuffer, driverNames, size - 1); odbcDriverNames = (new string(driverNames)).Split('\0'); } return odbcDriverNames; } /// /// Gets the name of an ODBC driver for Microsoft SQL Server giving preference to the most recent one. /// /// the name of an ODBC driver for Microsoft SQL Server, if one is present; null, otherwise. public static string GetOdbcSqlDriverName () { List driverPrecedence = new List() { "ODBC Driver 17 for SQL Server", "ODBC Driver 13 for SQL Server", "ODBC Driver 11 for SQL Server", "SQL Server Native Client 11.0", "SQL Server Native Client 10.0", "SQL Server Native Client 9.0", "SQL Server" }; string[] availableOdbcDrivers = GetOdbcDriverNames(); string driverName = null; if (availableOdbcDrivers != null) { driverName = driverPrecedence.Intersect(availableOdbcDrivers).FirstOrDefault(); } return driverName; } //------------------------------------------------------------------- // SQL Data Type to Native Data Type //------------------------------------------------------------------- public static short GetSqlNativeDataType (String dataTypeName) { switch (dataTypeName) { case "varchar": return 12; // SQL_VARCHAR case "nvarchar": return -9; // SQL_WVARCHAR case "bit": return -7; // SQL_BIT case "tinyint": return -6; // SQL_TINYINT case "smallint": return 5; // SQL_SMALLINT case "int": return 4; // SQL_INTEGER case "bigint": return -5; // SQL_BIGINT case "real": return 7; // SQL_REAL case "float": return 6; // SQL_FLOAT case "decimal": return 3; // SQL_DECIMAL case "numeric": return 2; // SQL_NUMERIC case "datetime": return 93; // SQL_TYPE_TIMESTAMP case "smalldatetime": return 93; // SQL_TYPE_TIMESTAMP case "varbinary": return 93; // SQL_TYPE_TIMESTAMP default: SaLogger.Warn("OdbcUtil.GetSqlNativeDataType Unrecognised SQL data type: '" + dataTypeName + "'"); break; } return 0; } //------------------------------------------------------------------- // O d b c T o D a t a T y p e //------------------------------------------------------------------- // ANSI SQL data type C++ data type // CHAR CString // DECIMAL CString 1 // SMALLINT int // REAL float // INTEGER long // FLOAT double // DOUBLE double // NUMERIC CString 1 // VARCHAR CString // LONGVARCHAR CLongBinary, CString 2 // BIT BOOL // TINYINT BYTE // BIGINT CString 1 // BINARY CByteArray // VARBINARY CByteArray // LONGVARBINARY CLongBinary, CByteArray 3 // DATE CTime, CString // TIME CTime, CString // TIMESTAMP CTime, CString // Notes: // 1. ANSI DECIMAL and NUMERIC map to CString because SQL_C_CHAR is the default ODBC transfer type. // 2. Character data beyond 255 characters is truncated by default when mapped to CString. // You can extend the truncation length by explicitly setting the nMaxLength argument of RFX_Text. // 3. Binary data beyond 255 characters is truncated by default when mapped to CByteArray. // You can extend the truncation length by explicitly setting the nMaxLength argument of RFX_Binary. //------------------------------------------------------------------- public static DbColumnDataType OdbcToDataType (short dataType) { // Data types: // 2 DECIMAL, CURRENCY // 4 INTEGER, COUNTER // 5 SMALLINT // 7 REAL // 8 DOUBLE // 11 DATETIME (Date & Time) // 12 VARCHAR // -6 BYTE // -7 BIT (YesNo) // -11 GUID (NumberReplId) DbColumnDataType type = DbColumnDataType.Unknown; switch (dataType) { //--------------------------------------------------------------- case 1: // SQL_CHAR: // 1 CHAR(n) Character string of fixed string length n. case 12: // SQL_VARCHAR: // 12 VARCHAR(n) Variable-length character string with a maximum string length n. case -1: // SQL_LONGVARCHAR: // -1 LONG VARCHAR Variable length character data. Maximum length is data source–dependent.[9] case -8: // SQL_WCHAR: // -8 WCHAR(n) Unicode character string of fixed string length n case -9: // SQL_WVARCHAR: // -9 VARWCHAR(n) Unicode variable-length character string with a maximum string length n case -10: // SQL_WLONGVARCHAR: // -10 LONGWVARCHAR Unicode variable-length character data. Maximum length is data source–dependent type = DbColumnDataType.VarChar; break; //--------------------------------------------------------------- case 3: // SQL_DECIMAL: // 3 DECIMAL(p,s) Signed, exact, numeric value with a precision of at least p and scale s. (The maximum precision is driver-defined.) (1 <= p <= 15; s <= p).[4] case 2: // SQL_NUMERIC: // 2 NUMERIC(p,s) Signed, exact, numeric value with a precision p and scale s (1 <= p <= 15; s <= p).[4] type = DbColumnDataType.Decimal; break; //--------------------------------------------------------------- case -6: // SQL_TINYINT: // -6 TINYINT Exact numeric value with precision 3 and scale 0 (signed: –128 <= n <= 127, unsigned:0 <= n <= 255)[3]. type = DbColumnDataType.TinyInt; //eByte break; //--------------------------------------------------------------- case 5: // SQL_SMALLINT: // 5 SMALLINT Exact numeric value with precision 5 and scale 0 (signed: –32,768 <= n <= 32,767, unsigned: 0 <= n <= 65,535)[3]. type = DbColumnDataType.SmallInt; break; //--------------------------------------------------------------- case 4: // SQL_INTEGER: // 4 INTEGER Exact numeric value with precision 10 and scale 0 (signed: –2[31] <= n <= 2[31] – 1, unsigned:0 <= n <= 2[32] – 1)[3]. type = DbColumnDataType.Int; break; //--------------------------------------------------------------- case -5: // SQL_BIGINT: // -5 BIGINT Exact numeric value with precision 19 (if signed) or 20 (if unsigned) and scale 0 (signed: –2[63] <= n <= 2[63] – 1, unsigned:0 <= n <= 2[64] – 1)[3],[9]. type = DbColumnDataType.BigInt; break; //--------------------------------------------------------------- case 7: // SQL_REAL: // 7 REAL Signed, approximate, numeric value with a binary precision 24 (zero or absolute value 10[–38] to 10[38]). type = DbColumnDataType.Single; break; //--------------------------------------------------------------- case 6: // SQL_FLOAT: // 6 FLOAT(p) Signed, approximate, numeric value with a binary precision of at least p. (The maximum precision is driver-defined.)[5] case 8: // SQL_DOUBLE: // 8 DOUBLE PRECISION Signed, approximate, numeric value with a binary precision 53 (zero or absolute value 10[–308] to 10[308]). type = DbColumnDataType.Double; break; //--------------------------------------------------------------- case 9: // SQL_DATETIME: // 9 DateTime ODBC case 11: // 11 DateTime Access DateTime format (SQL_TIMESTAMP?) case 91: // SQL_TYPE_DATE: // 91 DATE Year, month, and day fields, conforming to the rules of the Gregorian calendar. case 92: // SQL_TYPE_TIME: // 92 TIME(p) Hour, minute, and second fields, with valid values for hours of 00 to 23, valid values for minutes of 00 to 59, and valid values for seconds of 00 to 61. Precision p indicates the seconds precision. case 93: // SQL_TYPE_TIMESTAMP: // 93 Time Stamp TIMESTAMP(p) - Year, month, day, hour, minute, and second fields, with valid values as defined for the DATE and TIME data types. type = DbColumnDataType.DateTime; break; //--------------------------------------------------------------- case -7: // SQL_BIT: // -7 BIT Single bit binary data.[8] type = DbColumnDataType.Bit; break; //--------------------------------------------------------------- case -2: // SQL_BINARY: // -2 BINARY(n) Binary data of fixed length n.[9] case -3: // SQL_VARBINARY: // -3 VARBINARY(n) Variable length binary data of maximum length n. The maximum is set by the user.[9] case -4: // SQL_LONGVARBINARY: // -4 LONG VARBINARY Variable length binary data. Maximum length is data source–dependent.[9] type = DbColumnDataType.Binary; break; //--------------------------------------------------------------- case 102: // SQL_INTERVAL_MONTH: // 102 INTERVAL MONTH(p) Number of months between two dates; p is the interval leading precision. case 101: // SQL_INTERVAL_YEAR: // 101 INTERVAL YEAR(p) Number of years between two dates; p is the interval leading precision. case 107: // SQL_INTERVAL_YEAR_TO_MONTH: // 107 INTERVAL YEAR(p) TO MONTH Number of years and months between two dates; p is the interval leading precision. case 103: // SQL_INTERVAL_DAY: // 103 INTERVAL DAY(p) Number of days between two dates; p is the interval leading precision. case 104: // SQL_INTERVAL_HOUR: // 104 INTERVAL HOUR(p) Number of hours between two date/times; p is the interval leading precision. case 105: // SQL_INTERVAL_MINUTE: // 105 INTERVAL MINUTE(p) Number of minutes between two date/times; p is the interval leading precision. case 106: // SQL_INTERVAL_SECOND: // 106 INTERVAL SECOND(p,q) Number of seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case 108: // SQL_INTERVAL_DAY_TO_HOUR: // 108 INTERVAL DAY(p) TO HOUR Number of days/hours between two date/times; p is the interval leading precision. case 109: // SQL_INTERVAL_DAY_TO_MINUTE: // 109 INTERVAL DAY(p) TO MINUTE Number of days/hours/minutes between two date/times; p is the interval leading precision. case 110: // SQL_INTERVAL_DAY_TO_SECOND: // 110 INTERVAL DAY(p) TO SECOND(q) Number of days/hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case 111: // SQL_INTERVAL_HOUR_TO_MINUTE: // 111 INTERVAL HOUR(p) TO MINUTE Number of hours/minutes between two date/times; p is the interval leading precision. case 112: // SQL_INTERVAL_HOUR_TO_SECOND: // 112 INTERVAL HOUR(p) TO SECOND(q) Number of hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case 113: // SQL_INTERVAL_MINUTE_TO_SECOND: // 113 INTERVAL MINUTE(p) TO SECOND(q) Number of minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case -11: // SQL_GUID: // -11 GUID Fixed length Globally Unique Identifier. // break; //--------------------------------------------------------------- default: SaLogger.Debug("OdbcUtil.OdbcToDataType Unrecognised native data type: " + dataType); break; //--------------------------------------------------------------- } return type; } //------------------------------------------------------------------- public static String GetOdbcDataTypeCaption (short dataType) { switch (dataType) { //--------------------------------------------------------------- case 1: return "Char"; // SQL_CHAR case 12: return "VarChar"; // SQL_VARCHAR case -1: return "LongVarChar"; // SQL_LONGVARCHAR case -8: return "WChar"; // SQL_WCHAR case -9: return "VarWChar"; // SQL_WVARCHAR case -10: return "LongWVarChar"; // SQL_WLONGVARCHAR case 3: return "Decimal"; // SQL_DECIMAL case 2: return "Numeric"; // SQL_NUMERIC case -6: return "TinyInt"; // SQL_TINYINT case 5: return "SmallInt"; // SQL_SMALLINT case 4: return "Integer"; // SQL_INTEGER case 7: return "Real"; // SQL_REAL case 6: return "Float"; // SQL_FLOAT case 8: return "Double"; // SQL_DOUBLE case 9: return "DateTime"; // SQL_DATETIME case 11: return "TimeStamp_11"; case 91: return "Date"; // SQL_TYPE_DATE case 92: return "Time"; // SQL_TYPE_TIME case 93: return "TimeStamp"; // SQL_TYPE_TIMESTAMP case -7: return "Bit"; // SQL_BIT case -5: return "BigInt"; // SQL_BIGINT case -2: return "Binary"; // SQL_BINARY case -3: return "VarBinary"; // SQL_VARBINARY /* case SQL_LONGVARBINARY: // -4 LONG VARBINARY Variable length binary data. Maximum length is data source–dependent.[9] case SQL_INTERVAL_MONTH: // 102 INTERVAL MONTH(p) Number of months between two dates; p is the interval leading precision. case SQL_INTERVAL_YEAR: // 101 INTERVAL YEAR(p) Number of years between two dates; p is the interval leading precision. case SQL_INTERVAL_YEAR_TO_MONTH: // 107 INTERVAL YEAR(p) TO MONTH Number of years and months between two dates; p is the interval leading precision. case SQL_INTERVAL_DAY: // 103 INTERVAL DAY(p) Number of days between two dates; p is the interval leading precision. case SQL_INTERVAL_HOUR: // 104 INTERVAL HOUR(p) Number of hours between two date/times; p is the interval leading precision. case SQL_INTERVAL_MINUTE: // 105 INTERVAL MINUTE(p) Number of minutes between two date/times; p is the interval leading precision. case SQL_INTERVAL_SECOND: // 106 INTERVAL SECOND(p,q) Number of seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case SQL_INTERVAL_DAY_TO_HOUR: // 108 INTERVAL DAY(p) TO HOUR Number of days/hours between two date/times; p is the interval leading precision. case SQL_INTERVAL_DAY_TO_MINUTE: // 109 INTERVAL DAY(p) TO MINUTE Number of days/hours/minutes between two date/times; p is the interval leading precision. case SQL_INTERVAL_DAY_TO_SECOND: // 110 INTERVAL DAY(p) TO SECOND(q) Number of days/hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case SQL_INTERVAL_HOUR_TO_MINUTE: // 111 INTERVAL HOUR(p) TO MINUTE Number of hours/minutes between two date/times; p is the interval leading precision. case SQL_INTERVAL_HOUR_TO_SECOND: // 112 INTERVAL HOUR(p) TO SECOND(q) Number of hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case SQL_INTERVAL_MINUTE_TO_SECOND: // 113 INTERVAL MINUTE(p) TO SECOND(q) Number of minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision. case SQL_GUID: // -11 GUID Fixed length Globally Unique Identifier. */ default: break; //--------------------------------------------------------------- } return "Type " + dataType; } //------------------------------------------------------------------- } //======================================================================= }