diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingContext.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingContext.cs
index 0add6ec4..edb7d312 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingContext.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingContext.cs
@@ -6,11 +6,14 @@
using System;
using System.Threading;
using Microsoft.SqlServer.Management.Common;
+using SMO = Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.Common;
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Parser;
+using Microsoft.SqlTools.Utility;
+using System.Linq;
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
@@ -25,6 +28,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
private ServerConnection serverConnection;
+ private SMO.Server server;
+
///
/// Connected binding context constructor
///
@@ -55,7 +60,19 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// reset the parse options so the get recreated for the current connection
this.parseOptions = null;
- }
+ // Set up a SMO Server to query when determing parse options and we don't have a metadataprovider
+ this.server = new SMO.Server(this.serverConnection);
+ }
+ }
+
+ public SMO.Server Server
+ {
+ get
+ {
+ // Use the Server from the SmoMetadataProvider if we have it to avoid
+ // unnecessary overhead of querying a new object
+ return this.SmoMetadataProvider?.SmoServer ?? this.server;
+ }
}
///
@@ -92,26 +109,30 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
/// Gets the Language Service ServerVersion
///
- public ServerVersion ServerVersion
- {
- get
- {
- return this.ServerConnection != null
- ? this.ServerConnection.ServerVersion
- : null;
+ public ServerVersion ServerVersion
+ {
+ get
+ {
+ return this.ServerConnection?.ServerVersion;
}
}
///
/// Gets the current DataEngineType
///
- public DatabaseEngineType DatabaseEngineType
- {
- get
- {
- return this.ServerConnection != null
- ? this.ServerConnection.DatabaseEngineType
- : DatabaseEngineType.Standalone;
+ public DatabaseEngineType DatabaseEngineType
+ {
+ get
+ {
+ return this.ServerConnection?.DatabaseEngineType ?? DatabaseEngineType.Standalone;
+ }
+ }
+
+ public DatabaseEngineEdition DatabaseEngineEdition
+ {
+ get
+ {
+ return this.ServerConnection?.DatabaseEngineEdition ?? DatabaseEngineEdition.Standard;
}
}
@@ -122,12 +143,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
get
{
- return this.IsConnected
- ? GetTransactSqlVersion(this.ServerVersion)
+ return this.IsConnected
+ ? GetTransactSqlVersion(this.Server)
: TransactSqlVersion.Current;
}
}
-
+
///
/// Gets the current DatabaseCompatibilityLevel
///
@@ -136,7 +157,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
get
{
return this.IsConnected
- ? GetDatabaseCompatibilityLevel(this.ServerVersion)
+ ? GetDatabaseCompatibilityLevel(this.Server)
: DatabaseCompatibilityLevel.Current;
}
}
@@ -162,55 +183,111 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
- /// Gets the database compatibility level from a server version
+ /// Gets the database compatibility level for a given server connection
///
- ///
- private static DatabaseCompatibilityLevel GetDatabaseCompatibilityLevel(ServerVersion serverVersion)
+ ///
+ private static DatabaseCompatibilityLevel GetDatabaseCompatibilityLevel(SMO.Server server)
{
- int versionMajor = Math.Max(serverVersion.Major, 8);
-
- switch (versionMajor)
+ if (server.DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase)
{
- case 8:
+ return DatabaseCompatibilityLevel.Azure;
+ }
+
+ // Get the actual compat level of the database we're connected to
+ switch (GetServerCompatibilityLevel(server))
+ {
+ case SMO.CompatibilityLevel.Version80:
return DatabaseCompatibilityLevel.Version80;
- case 9:
+ case SMO.CompatibilityLevel.Version90:
return DatabaseCompatibilityLevel.Version90;
- case 10:
+ case SMO.CompatibilityLevel.Version100:
return DatabaseCompatibilityLevel.Version100;
- case 11:
+ case SMO.CompatibilityLevel.Version110:
return DatabaseCompatibilityLevel.Version110;
- case 12:
+ case SMO.CompatibilityLevel.Version120:
return DatabaseCompatibilityLevel.Version120;
- case 13:
+ case SMO.CompatibilityLevel.Version130:
return DatabaseCompatibilityLevel.Version130;
+ case SMO.CompatibilityLevel.Version140:
+ return DatabaseCompatibilityLevel.Version140;
+ case SMO.CompatibilityLevel.Version150:
+ return DatabaseCompatibilityLevel.Version150;
default:
return DatabaseCompatibilityLevel.Current;
}
}
///
- /// Gets the transaction sql version from a server version
+ /// Gets the transaction sql version for a given server connection
///
- ///
- private static TransactSqlVersion GetTransactSqlVersion(ServerVersion serverVersion)
+ ///
+ private static TransactSqlVersion GetTransactSqlVersion(SMO.Server server)
{
- int versionMajor = Math.Max(serverVersion.Major, 9);
-
- switch (versionMajor)
+ if (server.DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase)
{
- case 9:
- case 10:
+ return TransactSqlVersion.Azure;
+ }
+
+ // Determine the language version to use - we can't just use VersionMajor directly because there are engine versions (such as MI)
+ // whose language version they support is higher than the actual server version. So we choose the highest compat level from
+ // between the server version and compat level
+ var compatLevel = Math.Max(server.VersionMajor * 10, (int)GetServerCompatibilityLevel(server));
+ switch (compatLevel)
+ {
+ case 90:
+ case 100:
// In case of 10.0 we still use Version 10.5 as it is the closest available.
return TransactSqlVersion.Version105;
- case 11:
+ case 110:
return TransactSqlVersion.Version110;
- case 12:
+ case 120:
return TransactSqlVersion.Version120;
- case 13:
+ case 130:
return TransactSqlVersion.Version130;
+ case 140:
+ return TransactSqlVersion.Version140;
+ case 150:
+ return TransactSqlVersion.Version150;
default:
return TransactSqlVersion.Current;
}
}
+
+ ///
+ /// Gets the SMO compatibility level for the given server, defaulting to the highest available level if an
+ /// error occurs while querying.
+ ///
+ /// The server object to get the compat level of
+ ///
+ private static SMO.CompatibilityLevel GetServerCompatibilityLevel(SMO.Server server)
+ {
+ // Set the default fields so that we avoid the overhead of querying for properties we don't need right now
+ server.SetDefaultInitFields(typeof(SMO.Database), nameof(SMO.Database.CompatibilityLevel));
+
+ SMO.CompatibilityLevel compatLevel;
+ try
+ {
+ // First try the master DB since it will have the highest compat level for that instance
+ compatLevel = server.Databases["master"].CompatibilityLevel;
+ Logger.Write(System.Diagnostics.TraceEventType.Information, $"Got compat level for binding context {compatLevel} after querying master");
+ }
+ catch
+ {
+ // If we can't get it from master then fall back to the current database
+ try
+ {
+ compatLevel = server.Databases[server.ConnectionContext.DatabaseName].CompatibilityLevel;
+ Logger.Write(System.Diagnostics.TraceEventType.Information, $"Got compat level for binding context {compatLevel} after querying connection DB");
+ }
+ catch
+ {
+ // There's nothing else we can do so just default to the highest available version
+ compatLevel = Enum.GetValues(typeof(SMO.CompatibilityLevel)).Cast().Max();
+ Logger.Write(System.Diagnostics.TraceEventType.Information, $"Failed to get compat level for binding context from querying server - using default of {compatLevel}");
+ }
+
+ }
+ return compatLevel;
+ }
}
}