425 lines
19 KiB
C#
425 lines
19 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="oledbconnectionstring.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
// <owner current="true" primary="false">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Data.OleDb {
|
||
|
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Collections.Specialized;
|
||
|
using System.Data;
|
||
|
using System.Data.Common;
|
||
|
using System.Diagnostics;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Security;
|
||
|
using System.Security.Permissions;
|
||
|
using System.Text;
|
||
|
using Microsoft.Win32;
|
||
|
using System.Runtime.Versioning;
|
||
|
|
||
|
internal struct SchemaSupport {
|
||
|
internal Guid _schemaRowset;
|
||
|
internal int _restrictions;
|
||
|
}
|
||
|
|
||
|
internal sealed class OleDbConnectionString : DbConnectionOptions {
|
||
|
// instances of this class are intended to be immutable, i.e readonly
|
||
|
// used by pooling classes so it is much easier to verify correctness
|
||
|
// when not worried about the class being modified during execution
|
||
|
|
||
|
internal static class KEY {
|
||
|
internal const string Asynchronous_Processing = "asynchronous processing";
|
||
|
internal const string Connect_Timeout = "connect timeout";
|
||
|
internal const string Data_Provider = "data provider";
|
||
|
internal const string Data_Source = "data source";
|
||
|
internal const string Extended_Properties = "extended properties";
|
||
|
internal const string File_Name = "file name";
|
||
|
internal const string Initial_Catalog = "initial catalog";
|
||
|
internal const string Ole_DB_Services = "ole db services";
|
||
|
internal const string Persist_Security_Info = "persist security info";
|
||
|
internal const string Prompt = "prompt";
|
||
|
internal const string Provider = "provider";
|
||
|
internal const string RemoteProvider = "remote provider";
|
||
|
internal const string WindowHandle = "window handle";
|
||
|
}
|
||
|
|
||
|
// registry key and dword value entry for udl pooling
|
||
|
private static class UDL {
|
||
|
internal const string Header = "\xfeff[oledb]\r\n; Everything after this line is an OLE DB initstring\r\n";
|
||
|
internal const string Location = "SOFTWARE\\Microsoft\\DataAccess\\Udl Pooling";
|
||
|
internal const string Pooling = "Cache Size";
|
||
|
|
||
|
static internal volatile bool _PoolSizeInit;
|
||
|
static internal int _PoolSize;
|
||
|
|
||
|
static internal volatile Dictionary<string,string> _Pool;
|
||
|
static internal object _PoolLock = new object();
|
||
|
}
|
||
|
|
||
|
private static class VALUES {
|
||
|
internal const string NoPrompt = "noprompt";
|
||
|
}
|
||
|
|
||
|
// set during ctor
|
||
|
internal readonly bool PossiblePrompt;
|
||
|
internal readonly string ActualConnectionString; // cached value passed to GetDataSource
|
||
|
|
||
|
private readonly string _expandedConnectionString;
|
||
|
|
||
|
internal SchemaSupport[] _schemaSupport;
|
||
|
|
||
|
internal int _sqlSupport;
|
||
|
internal bool _supportMultipleResults;
|
||
|
internal bool _supportIRow;
|
||
|
internal bool _hasSqlSupport;
|
||
|
internal bool _hasSupportMultipleResults, _hasSupportIRow;
|
||
|
|
||
|
private int _oledbServices;
|
||
|
|
||
|
// these are cached delegates (per unique connectionstring)
|
||
|
internal UnsafeNativeMethods.IUnknownQueryInterface DangerousDataSourceIUnknownQueryInterface;
|
||
|
internal UnsafeNativeMethods.IDBInitializeInitialize DangerousIDBInitializeInitialize;
|
||
|
internal UnsafeNativeMethods.IDBCreateSessionCreateSession DangerousIDBCreateSessionCreateSession;
|
||
|
internal UnsafeNativeMethods.IDBCreateCommandCreateCommand DangerousIDBCreateCommandCreateCommand;
|
||
|
|
||
|
// since IDBCreateCommand interface may not be supported for a particular provider (only IOpenRowset)
|
||
|
// we cache that fact rather than call QueryInterface on every call to Open
|
||
|
internal bool HaveQueriedForCreateCommand;
|
||
|
|
||
|
// SxS: if user specifies a value for "File Name=" (UDL) in connection string, OleDbConnectionString will load the connection string
|
||
|
// from the UDL file. The UDL file is opened as FileMode.Open, FileAccess.Read, FileShare.Read, allowing concurrent access to it.
|
||
|
[ResourceExposure(ResourceScope.None)]
|
||
|
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
||
|
internal OleDbConnectionString(string connectionString, bool validate) : base(connectionString) {
|
||
|
string prompt = this[KEY.Prompt];
|
||
|
PossiblePrompt = ((!ADP.IsEmpty(prompt) && (0 != String.Compare(prompt, VALUES.NoPrompt, StringComparison.OrdinalIgnoreCase)))
|
||
|
|| !ADP.IsEmpty(this[KEY.WindowHandle]));
|
||
|
|
||
|
if (!IsEmpty) {
|
||
|
string udlConnectionString = null;
|
||
|
if (!validate) {
|
||
|
int position = 0;
|
||
|
string udlFileName = null;
|
||
|
_expandedConnectionString = ExpandDataDirectories(ref udlFileName, ref position);
|
||
|
|
||
|
if (!ADP.IsEmpty(udlFileName)) { // fail via new FileStream vs. GetFullPath
|
||
|
udlFileName = ADP.GetFullPath(udlFileName); // MDAC 82833
|
||
|
}
|
||
|
if (null != udlFileName) {
|
||
|
udlConnectionString = LoadStringFromStorage(udlFileName);
|
||
|
|
||
|
if (!ADP.IsEmpty(udlConnectionString)) {
|
||
|
_expandedConnectionString = _expandedConnectionString.Substring(0, position) + udlConnectionString + ';' + _expandedConnectionString.Substring(position);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (validate || ADP.IsEmpty(udlConnectionString)) {
|
||
|
ActualConnectionString = ValidateConnectionString(connectionString);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal int ConnectTimeout {
|
||
|
get { return base.ConvertValueToInt32(KEY.Connect_Timeout, ADP.DefaultConnectionTimeout); }
|
||
|
}
|
||
|
|
||
|
internal string DataSource {
|
||
|
get { return base.ConvertValueToString(KEY.Data_Source, ADP.StrEmpty); }
|
||
|
}
|
||
|
|
||
|
internal string InitialCatalog {
|
||
|
get { return base.ConvertValueToString(KEY.Initial_Catalog, ADP.StrEmpty); }
|
||
|
}
|
||
|
|
||
|
internal string Provider {
|
||
|
get {
|
||
|
Debug.Assert(!ADP.IsEmpty(this[KEY.Provider]), "no Provider");
|
||
|
return this[KEY.Provider];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal int OleDbServices {
|
||
|
get {
|
||
|
return _oledbServices;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal SchemaSupport[] SchemaSupport { // OleDbConnection.GetSchemaRowsetInformation
|
||
|
get { return _schemaSupport; }
|
||
|
set { _schemaSupport = value; }
|
||
|
}
|
||
|
|
||
|
protected internal override System.Security.PermissionSet CreatePermissionSet() {
|
||
|
System.Security.PermissionSet permissionSet;
|
||
|
if (PossiblePrompt) {
|
||
|
permissionSet = new NamedPermissionSet("FullTrust");
|
||
|
}
|
||
|
else {
|
||
|
permissionSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None);
|
||
|
permissionSet.AddPermission(new OleDbPermission(this));
|
||
|
}
|
||
|
return permissionSet;
|
||
|
}
|
||
|
|
||
|
protected internal override string Expand() {
|
||
|
if (null != _expandedConnectionString) {
|
||
|
return _expandedConnectionString;
|
||
|
}
|
||
|
else {
|
||
|
return base.Expand();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal int GetSqlSupport(OleDbConnection connection) {
|
||
|
int sqlSupport = _sqlSupport;
|
||
|
if (!_hasSqlSupport) {
|
||
|
object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_SQLSUPPORT);
|
||
|
if (value is Int32) { // not OleDbPropertyStatus
|
||
|
sqlSupport = (int) value;
|
||
|
}
|
||
|
_sqlSupport = sqlSupport;
|
||
|
_hasSqlSupport = true;
|
||
|
}
|
||
|
return sqlSupport;
|
||
|
}
|
||
|
|
||
|
internal bool GetSupportIRow(OleDbConnection connection, OleDbCommand command) {
|
||
|
bool supportIRow = _supportIRow;
|
||
|
if (!_hasSupportIRow) {
|
||
|
object value = command.GetPropertyValue(OleDbPropertySetGuid.Rowset, ODB.DBPROP_IRow);
|
||
|
|
||
|
// SQLOLEDB always returns VARIANT_FALSE for DBPROP_IROW, so base the answer on existance
|
||
|
supportIRow = !(value is OleDbPropertyStatus);
|
||
|
_supportIRow = supportIRow;
|
||
|
_hasSupportIRow = true;
|
||
|
}
|
||
|
return supportIRow;
|
||
|
}
|
||
|
|
||
|
internal bool GetSupportMultipleResults(OleDbConnection connection) {
|
||
|
bool supportMultipleResults = _supportMultipleResults;
|
||
|
if (!_hasSupportMultipleResults) {
|
||
|
object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_MULTIPLERESULTS);
|
||
|
if (value is Int32) {// not OleDbPropertyStatus
|
||
|
supportMultipleResults = (ODB.DBPROPVAL_MR_NOTSUPPORTED != (int) value);
|
||
|
}
|
||
|
_supportMultipleResults = supportMultipleResults;
|
||
|
_hasSupportMultipleResults = true;
|
||
|
}
|
||
|
return supportMultipleResults;
|
||
|
}
|
||
|
|
||
|
static private int UdlPoolSize { // MDAC 69925
|
||
|
// SxS: UdpPoolSize reads registry value to get the pool size
|
||
|
[ResourceExposure(ResourceScope.None)]
|
||
|
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
||
|
get {
|
||
|
int poolsize = UDL._PoolSize;
|
||
|
if (!UDL._PoolSizeInit) {
|
||
|
object value = ADP.LocalMachineRegistryValue(UDL.Location, UDL.Pooling);
|
||
|
if (value is Int32) {
|
||
|
poolsize = (int) value;
|
||
|
poolsize = ((0 < poolsize) ? poolsize : 0);
|
||
|
UDL._PoolSize = poolsize;
|
||
|
}
|
||
|
UDL._PoolSizeInit = true;
|
||
|
}
|
||
|
return poolsize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[ResourceExposure(ResourceScope.Machine)]
|
||
|
[ResourceConsumption(ResourceScope.Machine)]
|
||
|
static private string LoadStringFromStorage(string udlfilename) {
|
||
|
string udlConnectionString = null;
|
||
|
Dictionary<string,string> udlcache = UDL._Pool;
|
||
|
|
||
|
if ((null == udlcache) || !udlcache.TryGetValue(udlfilename, out udlConnectionString)) {
|
||
|
udlConnectionString = LoadStringFromFileStorage(udlfilename);
|
||
|
if (null != udlConnectionString) {
|
||
|
Debug.Assert(!ADP.IsEmpty(udlfilename), "empty filename didn't fail");
|
||
|
|
||
|
if (0 < UdlPoolSize) {
|
||
|
Debug.Assert(udlfilename == ADP.GetFullPath(udlfilename), "only cache full path filenames"); // MDAC 82833
|
||
|
|
||
|
if (null == udlcache) {
|
||
|
udlcache = new Dictionary<string,string>();
|
||
|
udlcache[udlfilename] = udlConnectionString;
|
||
|
|
||
|
lock(UDL._PoolLock) {
|
||
|
if (null != UDL._Pool) {
|
||
|
udlcache = UDL._Pool;
|
||
|
}
|
||
|
else {
|
||
|
UDL._Pool = udlcache;
|
||
|
udlcache = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (null != udlcache) {
|
||
|
lock(udlcache) {
|
||
|
udlcache[udlfilename] = udlConnectionString;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return udlConnectionString;
|
||
|
}
|
||
|
|
||
|
[ResourceExposure(ResourceScope.Machine)]
|
||
|
[ResourceConsumption(ResourceScope.Machine)]
|
||
|
static private string LoadStringFromFileStorage(string udlfilename) {
|
||
|
// Microsoft Data Link File Format
|
||
|
// The first two lines of a .udl file must have exactly the following contents in order to work properly:
|
||
|
// [oledb]
|
||
|
// ; Everything after this line is an OLE DB initstring
|
||
|
//
|
||
|
string connectionString = null;
|
||
|
Exception failure = null;
|
||
|
try {
|
||
|
int hdrlength = ADP.CharSize*UDL.Header.Length;
|
||
|
using(FileStream fstream = new FileStream(udlfilename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
|
||
|
long length = fstream.Length;
|
||
|
if (length < hdrlength || (0 != length%ADP.CharSize)) {
|
||
|
failure = ADP.InvalidUDL();
|
||
|
}
|
||
|
else {
|
||
|
byte[] bytes = new Byte[hdrlength];
|
||
|
int count = fstream.Read(bytes, 0, bytes.Length);
|
||
|
if (count < hdrlength) {
|
||
|
failure = ADP.InvalidUDL();
|
||
|
}
|
||
|
else if (System.Text.Encoding.Unicode.GetString(bytes, 0, hdrlength) != UDL.Header) {
|
||
|
failure = ADP.InvalidUDL();
|
||
|
}
|
||
|
else { // please verify header before allocating memory block for connection string
|
||
|
bytes = new Byte[length - hdrlength];
|
||
|
count = fstream.Read(bytes, 0, bytes.Length);
|
||
|
connectionString = System.Text.Encoding.Unicode.GetString(bytes, 0, count);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
catch(Exception e) {
|
||
|
//
|
||
|
if (!ADP.IsCatchableExceptionType(e)) {
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
throw ADP.UdlFileError(e);
|
||
|
}
|
||
|
if (null != failure) {
|
||
|
throw failure;
|
||
|
}
|
||
|
return connectionString.Trim();
|
||
|
}
|
||
|
|
||
|
[ResourceExposure(ResourceScope.None)] // reads OleDbServices value for the provider
|
||
|
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
||
|
private string ValidateConnectionString(string connectionString) {
|
||
|
if (ConvertValueToBoolean(KEY.Asynchronous_Processing, false)) {
|
||
|
throw ODB.AsynchronousNotSupported();
|
||
|
}
|
||
|
|
||
|
int connectTimeout = ConvertValueToInt32(KEY.Connect_Timeout, 0);
|
||
|
if (connectTimeout < 0) {
|
||
|
throw ADP.InvalidConnectTimeoutValue();
|
||
|
}
|
||
|
|
||
|
string progid = ConvertValueToString(KEY.Data_Provider, null); // MDAC 71923
|
||
|
if (null != progid) {
|
||
|
progid = progid.Trim();
|
||
|
if (0 < progid.Length) { // don't fail on empty 'Data Provider' value
|
||
|
ValidateProvider(progid);
|
||
|
}
|
||
|
}
|
||
|
progid = ConvertValueToString(KEY.RemoteProvider, null); // MDAC 71923
|
||
|
if (null != progid) {
|
||
|
progid = progid.Trim();
|
||
|
if (0 < progid.Length) { // don't fail on empty 'Data Provider' value
|
||
|
ValidateProvider(progid);
|
||
|
}
|
||
|
}
|
||
|
progid = ConvertValueToString(KEY.Provider, ADP.StrEmpty).Trim();
|
||
|
ValidateProvider(progid); // will fail on empty 'Provider' value
|
||
|
|
||
|
// SQLBU VSTS 59322: initialize to default
|
||
|
// If the value is not provided in connection string and OleDbServices registry key has not been set by the provider,
|
||
|
// the default for the provider is -1 (all services are ON).
|
||
|
// our default is -13, we turn off ODB.DBPROPVAL_OS_AGR_AFTERSESSION and ODB.DBPROPVAL_OS_CLIENTCURSOR flags
|
||
|
_oledbServices = DbConnectionStringDefaults.OleDbServices;
|
||
|
|
||
|
bool hasOleDBServices = (base.ContainsKey(KEY.Ole_DB_Services) && !ADP.IsEmpty((string)base[KEY.Ole_DB_Services]));
|
||
|
if (!hasOleDBServices) { // don't touch registry if they have OLE DB Services
|
||
|
string classid = (string) ADP.ClassesRootRegistryValue(progid + "\\CLSID", String.Empty);
|
||
|
if ((null != classid) && (0 < classid.Length)) {
|
||
|
// CLSID detection of 'Microsoft OLE DB Provider for ODBC Drivers'
|
||
|
Guid classidProvider = new Guid(classid);
|
||
|
if (ODB.CLSID_MSDASQL == classidProvider) {
|
||
|
throw ODB.MSDASQLNotSupported();
|
||
|
}
|
||
|
object tmp = ADP.ClassesRootRegistryValue("CLSID\\{" + classidProvider.ToString("D", CultureInfo.InvariantCulture) + "}", ODB.OLEDB_SERVICES);
|
||
|
if (null != tmp) {
|
||
|
|
||
|
// @devnote: some providers like MSDataShape don't have the OLEDB_SERVICES value
|
||
|
// the MSDataShape provider doesn't support the 'Ole Db Services' keyword
|
||
|
// hence, if the value doesn't exist - don't prepend to string
|
||
|
try {
|
||
|
_oledbServices = (int)tmp;
|
||
|
}
|
||
|
catch(InvalidCastException e) {
|
||
|
ADP.TraceExceptionWithoutRethrow(e);
|
||
|
}
|
||
|
_oledbServices &= ~(ODB.DBPROPVAL_OS_AGR_AFTERSESSION | ODB.DBPROPVAL_OS_CLIENTCURSOR); // NT 347436, MDAC 58606
|
||
|
|
||
|
StringBuilder builder = new StringBuilder();
|
||
|
builder.Append(KEY.Ole_DB_Services);
|
||
|
builder.Append("=");
|
||
|
builder.Append(_oledbServices.ToString(CultureInfo.InvariantCulture));
|
||
|
builder.Append(";");
|
||
|
builder.Append(connectionString);
|
||
|
connectionString = builder.ToString();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// SQLBU VSTS 59322: parse the Ole Db Services value from connection string
|
||
|
_oledbServices = ConvertValueToInt32(KEY.Ole_DB_Services, DbConnectionStringDefaults.OleDbServices);
|
||
|
}
|
||
|
|
||
|
return connectionString;
|
||
|
}
|
||
|
|
||
|
internal static bool IsMSDASQL(string progid) {
|
||
|
return (("msdasql" == progid) || progid.StartsWith("msdasql.", StringComparison.Ordinal) || ("microsoft ole db provider for odbc drivers" == progid));
|
||
|
}
|
||
|
|
||
|
static private void ValidateProvider(string progid) {
|
||
|
if (ADP.IsEmpty(progid)) {
|
||
|
throw ODB.NoProviderSpecified();
|
||
|
}
|
||
|
if (ODB.MaxProgIdLength <= progid.Length) { // MDAC 63151
|
||
|
throw ODB.InvalidProviderSpecified();
|
||
|
}
|
||
|
progid = progid.ToLower(CultureInfo.InvariantCulture);
|
||
|
if (IsMSDASQL(progid)) {
|
||
|
// fail msdasql even if not on the machine.
|
||
|
throw ODB.MSDASQLNotSupported();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static internal void ReleaseObjectPool() {
|
||
|
UDL._PoolSizeInit = false;
|
||
|
UDL._Pool = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|