diff --git a/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationFormatException.cs b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationFormatException.cs
new file mode 100644
index 00000000..1e4e0eea
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationFormatException.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+using Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Helpers;
+
+namespace Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Exceptions
+{
+ ///
+ /// ParameterizationFormatException is used to surface format exceptions encountered in the TSQL batch to perform
+ /// auto-parameterization of literals for Always Encrypted.
+ ///
+ public class ParameterizationFormatException : FormatException
+ {
+ public readonly int LineNumber;
+ public readonly string LiteralValue;
+ public readonly string CodeSenseMessage;
+ public readonly string LogMessage;
+ public readonly string SqlDatatype;
+ public readonly string CSharpDataType;
+
+ public ParameterizationFormatException(MessageHelper.MessageType type, string variableName, string sqlDataType, string cSharpDataType, string literalValue, int lineNumber)
+ : this(type, variableName, sqlDataType, cSharpDataType, literalValue, lineNumber, exception: null) { }
+
+ public ParameterizationFormatException(MessageHelper.MessageType type, string variableName, string sqlDataType, string cSharpDataType, string literalValue, int lineNumber, Exception exception)
+ : base(MessageHelper.GetLocalizedMessage(type, variableName, sqlDataType, literalValue), exception)
+ {
+ LineNumber = lineNumber;
+ LiteralValue = literalValue;
+ SqlDatatype = sqlDataType;
+ CSharpDataType = cSharpDataType;
+ CodeSenseMessage = Message;
+ LogMessage = MessageHelper.GetLocaleInvariantMessage(type, variableName, sqlDataType) + ", Literal Value: " + literalValue + ", On line: " + lineNumber + ", CSharp DataType: " + cSharpDataType;
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationParsingException.cs b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationParsingException.cs
new file mode 100644
index 00000000..bf07e566
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationParsingException.cs
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Exceptions
+{
+ ///
+ /// ParameterizationParsingException is used to surface parse errors encountered in the TSQL batch while creating a parse tree
+ ///
+ public class ParameterizationParsingException : Exception
+ {
+ public readonly int ColumnNumber;
+ public readonly int LineNumber;
+
+ public ParameterizationParsingException(int lineNumber, int columnNumber, string errorMessage) : base(errorMessage)
+ {
+ LineNumber = lineNumber;
+ ColumnNumber = columnNumber;
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationScriptTooLargeException.cs b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationScriptTooLargeException.cs
new file mode 100644
index 00000000..4e5fd276
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/Exceptions/ParameterizationScriptTooLargeException.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+namespace Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Exceptions
+{
+ internal class ParameterizationScriptTooLargeException : ParameterizationParsingException
+ {
+ public readonly int ScriptLength;
+
+ // LineNumber and ColumnNumber are defaulted to 1 because this exception is thrown if the script is very large and lineNumber and columnNumber dont make much sense
+ public ParameterizationScriptTooLargeException(int scriptLength, string errorMessage) : base(lineNumber: 1, columnNumber: 1, errorMessage: errorMessage)
+ {
+ ScriptLength = scriptLength;
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/MessageHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/MessageHelper.cs
new file mode 100644
index 00000000..78fe4cab
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/MessageHelper.cs
@@ -0,0 +1,53 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Globalization;
+
+namespace Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Helpers
+{
+ public class MessageHelper
+ {
+ private static readonly string ERROR_MESSAGE_TEMPLATE = "Unable to Convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(System.Data.SqlDbType).";
+ private static readonly string DATE_TIME_ERROR_MESSAGE_TEMPLATE = "Unable to Convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(System.Data.SqlDbType), as it used an unsupported date/time format. Use one of the supported Date/time formats.";
+ private static readonly string BINARY_LITERAL_PREFIX_MISSING_ERROR_TEMPLATE = "Unable to Convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(System.Data.SqlDbType), as prefix 0x is expected for a binary literals.";
+
+ internal static string GetLocalizedMessage(MessageType type, string variableName, string sqlDataType, string literalValue)
+ {
+ switch (type)
+ {
+ case MessageType.ERROR_MESSAGE:
+ return SR.ErrorMessage(variableName, sqlDataType, literalValue);
+ case MessageType.DATE_TIME_ERROR_MESSAGE:
+ return SR.DateTimeErrorMessage(variableName, sqlDataType, literalValue);
+ case MessageType.BINARY_LITERAL_PREFIX_MISSING_ERROR:
+ return SR.BinaryLiteralPrefixMissingError(variableName, sqlDataType, literalValue);
+ default:
+ return "";
+ }
+ }
+
+ internal static string GetLocaleInvariantMessage(MessageType type, string variableName, string sqlDataType)
+ {
+ switch (type)
+ {
+ case MessageType.ERROR_MESSAGE:
+ return string.Format(CultureInfo.InvariantCulture, ERROR_MESSAGE_TEMPLATE, variableName, sqlDataType);
+ case MessageType.DATE_TIME_ERROR_MESSAGE:
+ return string.Format(CultureInfo.InvariantCulture, DATE_TIME_ERROR_MESSAGE_TEMPLATE, variableName, sqlDataType);
+ case MessageType.BINARY_LITERAL_PREFIX_MISSING_ERROR:
+ return string.Format(CultureInfo.InvariantCulture, BINARY_LITERAL_PREFIX_MISSING_ERROR_TEMPLATE, variableName, sqlDataType);
+ default:
+ return "";
+ }
+ }
+
+ public enum MessageType
+ {
+ ERROR_MESSAGE,
+ DATE_TIME_ERROR_MESSAGE,
+ BINARY_LITERAL_PREFIX_MISSING_ERROR,
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/ScalarExpressionTransformer.cs b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/ScalarExpressionTransformer.cs
new file mode 100644
index 00000000..4f2b2462
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/ScalarExpressionTransformer.cs
@@ -0,0 +1,718 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.SqlTypes;
+using System.Globalization;
+using System.Linq;
+using Microsoft.Data.SqlClient;
+using Microsoft.SqlServer.TransactSql.ScriptDom;
+using Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Exceptions;
+using Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Helpers;
+using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.AutoParameterizaition
+{
+ internal class ScalarExpressionTransformer : TSqlFragmentVisitor
+ {
+ #region datetimeFormats
+
+ private static readonly string[] SUPPORTED_ISO_DATE_TIME_FORMATS = {
+ "yyyyMMdd HH:mm:ss.fffffff",
+ "yyyyMMdd HH:mm:ss.ffffff",
+ "yyyyMMdd HH:mm:ss.fffff",
+ "yyyyMMdd HH:mm:ss.ffff",
+ "yyyyMMdd HH:mm:ss.fff",
+ "yyyyMMdd HH:mm:ss.ff",
+ "yyyyMMdd HH:mm:ss.f",
+ "yyyyMMdd HH:mm:ss",
+ "yyyyMMdd HH:mm",
+ "yyyyMMdd",
+
+ "yyyy-MM-ddTHH:mm:ss.fffffff",
+ "yyyy-MM-ddTHH:mm:ss.ffffff",
+ "yyyy-MM-ddTHH:mm:ss.fffff",
+ "yyyy-MM-ddTHH:mm:ss.ffff",
+ "yyyy-MM-ddTHH:mm:ss.fff",
+ "yyyy-MM-ddTHH:mm:ss.ff",
+ "yyyy-MM-ddTHH:mm:ss.f",
+ "yyyy-MM-ddTHH:mm:ss",
+ "yyyy-MM-ddTHH:mm",
+ "yyyy-MM-dd",
+ };
+
+ private static readonly string[] SUPPORTED_ISO_DATE_FORMATS = {
+ "yyyyMMdd",
+ "yyyy-MM-dd",
+ };
+
+ private static readonly string[] SUPPORTED_ISO_DATE_TIME_OFFSET_FORMATS = {
+ // 121025 12:32:10.1234567 +01:00 – zzz in the below format represents +01:00
+ "yyyyMMdd HH:mm:ss.fffffff zzz",
+ "yyyyMMdd HH:mm:ss.ffffff zzz",
+ "yyyyMMdd HH:mm:ss.fffff zzz",
+ "yyyyMMdd HH:mm:ss.ffff zzz",
+ "yyyyMMdd HH:mm:ss.fff zzz",
+ "yyyyMMdd HH:mm:ss.ff zzz",
+ "yyyyMMdd HH:mm:ss.f zzz",
+ "yyyyMMdd HH:mm:ss zzz",
+ "yyyyMMdd HH:mm zzz",
+ "yyyyMMdd zzz",
+
+ "yyyy-MM-ddTHH:mm:ss.fffffff zzz",
+ "yyyy-MM-ddTHH:mm:ss.ffffff zzz",
+ "yyyy-MM-ddTHH:mm:ss.fffff zzz",
+ "yyyy-MM-ddTHH:mm:ss.ffff zzz",
+ "yyyy-MM-ddTHH:mm:ss.fff zzz",
+ "yyyy-MM-ddTHH:mm:ss.ff zzz",
+ "yyyy-MM-ddTHH:mm:ss.f zzz",
+ "yyyy-MM-ddTHH:mm:ss zzz",
+ "yyyy-MM-ddTHH:mm zzz",
+ "yyyy-MM-dd zzz",
+
+ //19991212 19:30:30.1234567Z – K in the below format represents Z
+ "yyyyMMdd HH:mm:ss.fffffffK",
+ "yyyyMMdd HH:mm:ss.ffffffK",
+ "yyyyMMdd HH:mm:ss.fffffK",
+ "yyyyMMdd HH:mm:ss.ffffK",
+ "yyyyMMdd HH:mm:ss.fffK",
+ "yyyyMMdd HH:mm:ss.ffK",
+ "yyyyMMdd HH:mm:ss.fK",
+ "yyyyMMdd HH:mm:ssK",
+ "yyyyMMdd HH:mmK",
+ "yyyyMMddK",
+
+ "yyyy-MM-ddTHH:mm:ss.fffffffK",
+ "yyyy-MM-ddTHH:mm:ss.ffffffK",
+ "yyyy-MM-ddTHH:mm:ss.fffffK",
+ "yyyy-MM-ddTHH:mm:ss.ffffK",
+ "yyyy-MM-ddTHH:mm:ss.fffK",
+ "yyyy-MM-ddTHH:mm:ss.ffK",
+ "yyyy-MM-ddTHH:mm:ss.fK",
+ "yyyy-MM-ddTHH:mm:ssK",
+ "yyyy-MM-ddTHH:mmK",
+ "yyyy-MM-ddK",
+ };
+
+ #endregion
+
+ private const string C_SHARP_BYTE_ARRAY = "byte[]";
+ private readonly bool IsCodeSenseRequest;
+ private bool IsNegative = false;
+ private ScalarExpression CurrentScalarExpression;
+ public SqlDataTypeOption SqlDataTypeOption;
+ public IList SqlDataTypeParameters;
+ public string VariableName;
+
+ public IList Parameters { get; private set; }
+
+ private readonly IList CodeSenseErrors;
+
+ public ScalarExpressionTransformer(bool isCodeSenseRequest, IList codeSenseErrors)
+ {
+ Parameters = new List();
+ IsCodeSenseRequest = isCodeSenseRequest;
+ CodeSenseErrors = codeSenseErrors;
+ }
+
+ public override void ExplicitVisit(ScalarExpression node)
+ {
+ if (node == null)
+ {
+ return;
+ }
+
+ CurrentScalarExpression = node;
+
+ if (node is Literal literal)
+ {
+ if (ShouldParameterize(literal))
+ {
+ var variableReference = new VariableReference();
+ string parameterName = GetParameterName();
+ variableReference.Name = parameterName;
+ AddToParameterCollection(literal, parameterName, SqlDataTypeOption, SqlDataTypeParameters);
+ CurrentScalarExpression = variableReference;
+ }
+
+ return;
+ }
+
+ if (node is UnaryExpression unaryExpression)
+ {
+ ScalarExpression expression = unaryExpression.Expression;
+
+ if (expression != null)
+ {
+ if (unaryExpression.UnaryExpressionType.Equals(UnaryExpressionType.Negative))
+ {
+ IsNegative = !IsNegative;
+ }
+
+ ExplicitVisit(expression);
+ }
+
+ base.ExplicitVisit(node); //let the base class finish up
+ return;
+ }
+
+ if (node is ParenthesisExpression parenthesisExpression)
+ {
+ ScalarExpression expression = parenthesisExpression.Expression;
+
+ if (expression != null)
+ {
+ ScalarExpression tempScalarExpression = CurrentScalarExpression;
+ ExplicitVisit(expression);
+ parenthesisExpression.Expression = GetTransformedExpression();
+ CurrentScalarExpression = tempScalarExpression;
+ }
+
+ base.ExplicitVisit(node); // let the base class finish up
+ }
+ }
+
+ public ScalarExpression GetTransformedExpression()
+ {
+ return CurrentScalarExpression;
+ }
+
+ public void Reset()
+ {
+ SqlDataTypeOption = SqlDataTypeOption.VarChar;
+ SqlDataTypeParameters = null;
+ VariableName = null;
+ IsNegative = false;
+ Parameters.Clear();
+ }
+
+ ///
+ /// Converts a hex string to a byte[]
+ /// Note: this method expects "0x" prefix to be stripped off from the input string
+ /// For example, to convert the string "0xFFFF" to byte[] the input to this method should be "FFFF"
+ ///
+ ///
+ ///
+ private byte[] StringToByteArray(string hex)
+ {
+ return Enumerable.Range(0, hex.Length)
+ .Where(x => x % 2 == 0)
+ .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
+ .ToArray();
+ }
+
+ private void AddToParameterCollection(Literal literal, string parameterName, SqlDataTypeOption sqlDataTypeOption, IList sqlDataTypeParameters)
+ {
+ SqlParameter sqlParameter = new SqlParameter();
+ string literalValue = literal.Value;
+ object parsedValue = null;
+ SqlDbType paramType = SqlDbType.VarChar;
+ bool parseSuccessful = true;
+
+ switch (sqlDataTypeOption)
+ {
+ case SqlDataTypeOption.Binary:
+ paramType = SqlDbType.Binary;
+ try
+ {
+ parsedValue = TryParseBinaryLiteral(literalValue, VariableName, SqlDbType.Binary, literal.StartLine);
+ }
+ catch (ParameterizationFormatException)
+ {
+ if (IsCodeSenseRequest)
+ {
+ parseSuccessful = false;
+ AddCodeSenseErrorItem(MessageHelper.MessageType.BINARY_LITERAL_PREFIX_MISSING_ERROR, literal, literal.Value, VariableName, SqlDbType.Binary.ToString());
+ }
+ else
+ {
+ throw;
+ }
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.Binary.ToString(), C_SHARP_BYTE_ARRAY, literalValue, literal.StartLine, e);
+ }
+ break;
+
+ case SqlDataTypeOption.VarBinary:
+ paramType = SqlDbType.VarBinary;
+ try
+ {
+ parsedValue = TryParseBinaryLiteral(literalValue, VariableName, SqlDbType.VarBinary, literal.StartLine);
+ ExtractSize(sqlDataTypeParameters, sqlParameter);
+ }
+ catch (ParameterizationFormatException)
+ {
+ if (IsCodeSenseRequest)
+ {
+ parseSuccessful = false;
+ string sqlDataTypeString = GetSqlDataTypeStringOneParameter(SqlDbType.VarBinary, sqlDataTypeParameters);
+ AddCodeSenseErrorItem(MessageHelper.MessageType.BINARY_LITERAL_PREFIX_MISSING_ERROR, literal, literalValue, VariableName, sqlDataTypeString);
+ }
+ else
+ {
+ throw;
+ }
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ string sqlDataTypeString = GetSqlDataTypeStringOneParameter(SqlDbType.VarBinary, sqlDataTypeParameters);
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, sqlDataTypeString, C_SHARP_BYTE_ARRAY, literalValue, literal.StartLine, e);
+ }
+ break;
+
+
+ //Integer literals of form 24.0 will not be supported
+ case SqlDataTypeOption.BigInt:
+ paramType = SqlDbType.BigInt;
+ long parsedLong;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ if (long.TryParse(literalValue, out parsedLong))
+ {
+ parsedValue = parsedLong;
+ }
+ else
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.BigInt.ToString(), "Int64", literalValue, literal.StartLine, null);
+ }
+
+ break;
+
+ case SqlDataTypeOption.Int:
+ paramType = SqlDbType.Int;
+ int parsedInt;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ if (int.TryParse(literalValue, out parsedInt))
+ {
+ parsedValue = parsedInt;
+ }
+ else
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.Int.ToString(), "Int32", literalValue, literal.StartLine, null);
+ }
+
+ break;
+
+ case SqlDataTypeOption.SmallInt:
+ paramType = SqlDbType.SmallInt;
+ short parsedShort;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ if (short.TryParse(literalValue, out parsedShort))
+ {
+ parsedValue = parsedShort;
+ }
+ else
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.SmallInt.ToString(), "Int16", literalValue, literal.StartLine, null);
+ }
+
+ break;
+
+ case SqlDataTypeOption.TinyInt:
+ paramType = SqlDbType.TinyInt;
+ byte parsedByte;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ if (byte.TryParse(literalValue, out parsedByte))
+ {
+ parsedValue = parsedByte;
+ }
+ else
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.TinyInt.ToString(), "Byte", literalValue, literal.StartLine, null);
+ }
+
+ break;
+
+
+ case SqlDataTypeOption.Real:
+ paramType = SqlDbType.Real;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ try
+ {
+ parsedValue = SqlSingle.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.Real.ToString(), "SqlSingle", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.Float:
+ paramType = SqlDbType.Float;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ try
+ {
+ parsedValue = SqlDouble.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.Float.ToString(), "SqlDouble", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+
+ case SqlDataTypeOption.Decimal:
+ case SqlDataTypeOption.Numeric:
+ paramType = SqlDbType.Decimal;
+ ExtractPrecisionAndScale(sqlDataTypeParameters, sqlParameter);
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ try
+ {
+ parsedValue = SqlDecimal.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ string sqlDecimalDataType = sqlDataTypeParameters != null ? (SqlDbType.Decimal + "(" + sqlDataTypeParameters[0] + ", " + sqlDataTypeParameters[1] + ")") : "";
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, sqlDecimalDataType, "SqlDecimal", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.Money:
+ paramType = SqlDbType.Money;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ try
+ {
+ parsedValue = SqlMoney.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.Money.ToString(), "SqlMoney", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.SmallMoney:
+ paramType = SqlDbType.SmallMoney;
+ literalValue = IsNegative ? "-" + literalValue : literalValue;
+
+ try
+ {
+ parsedValue = SqlMoney.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.SmallMoney.ToString(), "SqlMoney", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.DateTime:
+ paramType = SqlDbType.DateTime;
+
+ try
+ {
+ parsedValue = ParseDateTime(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.DATE_TIME_ERROR_MESSAGE, literal, VariableName, SqlDbType.DateTime.ToString(), "DateTime", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.SmallDateTime:
+ paramType = SqlDbType.SmallDateTime;
+
+ try
+ {
+ parsedValue = ParseDateTime(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.DATE_TIME_ERROR_MESSAGE, literal, VariableName, SqlDbType.SmallDateTime.ToString(), "DateTime", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.DateTime2:
+ paramType = SqlDbType.DateTime2;
+
+ try
+ {
+ parsedValue = ParseDateTime(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.DATE_TIME_ERROR_MESSAGE, literal, VariableName, SqlDbType.DateTime2.ToString(), "DateTime", literalValue, literal.StartLine, e);
+ }
+
+ ExtractPrecision(sqlDataTypeParameters, sqlParameter);
+ break;
+
+ case SqlDataTypeOption.Date:
+ paramType = SqlDbType.Date;
+
+ try
+ {
+ parsedValue = ParseDate(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.DATE_TIME_ERROR_MESSAGE, literal, VariableName, SqlDbType.Date.ToString(), "DateTime", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.DateTimeOffset:
+ paramType = SqlDbType.DateTimeOffset;
+
+ try
+ {
+ parsedValue = ParseDateTimeOffset(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.DATE_TIME_ERROR_MESSAGE, literal, VariableName, SqlDbType.DateTimeOffset.ToString(), "DateTimeOffset", literalValue, literal.StartLine, e);
+ }
+
+ ExtractPrecision(sqlDataTypeParameters, sqlParameter);
+ break;
+
+
+ case SqlDataTypeOption.Time:
+ paramType = SqlDbType.Time;
+
+ try
+ {
+ parsedValue = TimeSpan.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.Time.ToString(), "TimeSpan", literalValue, literal.StartLine, e);
+ }
+
+ ExtractPrecision(sqlDataTypeParameters, sqlParameter);
+ break;
+
+ case SqlDataTypeOption.Char:
+ paramType = SqlDbType.Char;
+ ExtractSize(sqlDataTypeParameters, sqlParameter);
+ break;
+
+ case SqlDataTypeOption.VarChar:
+ paramType = SqlDbType.VarChar;
+ ExtractSize(sqlDataTypeParameters, sqlParameter);
+ break;
+
+ case SqlDataTypeOption.NChar:
+ paramType = SqlDbType.NChar;
+ ExtractSize(sqlDataTypeParameters, sqlParameter);
+ break;
+
+ case SqlDataTypeOption.NVarChar:
+ paramType = SqlDbType.NVarChar;
+ ExtractSize(sqlDataTypeParameters, sqlParameter);
+ break;
+
+ case SqlDataTypeOption.UniqueIdentifier:
+ paramType = SqlDbType.UniqueIdentifier;
+
+ try
+ {
+ parsedValue = SqlGuid.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.UniqueIdentifier.ToString(), "SqlGuid", literalValue, literal.StartLine, e);
+ }
+
+ break;
+
+ case SqlDataTypeOption.Bit:
+ paramType = SqlDbType.Bit;
+
+ try
+ {
+ parsedValue = Byte.Parse(literalValue);
+ }
+ catch (Exception e)
+ {
+ parseSuccessful = false;
+ HandleError(MessageHelper.MessageType.ERROR_MESSAGE, literal, VariableName, SqlDbType.Bit.ToString(), "Byte", literalValue, literal.StartLine, e);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (parseSuccessful)
+ {
+ sqlParameter.ParameterName = parameterName;
+ sqlParameter.SqlDbType = paramType;
+ sqlParameter.Value = parsedValue ?? literalValue;
+ sqlParameter.Direction = ParameterDirection.Input;
+ Parameters.Add(sqlParameter);
+ }
+ }
+
+ private string GetSqlDataTypeStringOneParameter(SqlDbType sqlDataType, IList sqlDataTypeParameters)
+ {
+ string parameters = sqlDataTypeParameters != null ? ("(" + sqlDataTypeParameters[0] + ")") : "";
+ return sqlDataType + parameters;
+ }
+
+ private void HandleError(MessageHelper.MessageType errorMessage, Literal literal, string variableName, string sqlDbType, string cSharpType, string literalValue, int startLine, Exception exception)
+ {
+ if (IsCodeSenseRequest)
+ {
+ AddCodeSenseErrorItem(errorMessage, literal, literalValue, variableName, sqlDbType);
+ }
+ else
+ {
+ if (exception != null)
+ {
+ throw new ParameterizationFormatException(errorMessage, variableName, sqlDbType, cSharpType, literalValue, startLine, exception);
+ }
+ else
+ {
+ throw new ParameterizationFormatException(errorMessage, variableName, sqlDbType, cSharpType, literalValue, startLine);
+ }
+ }
+ }
+
+ private void AddCodeSenseErrorItem(MessageHelper.MessageType messageType, Literal literal, string literalValue, string variableName, string sqlDbType)
+ {
+ CodeSenseErrors.Add(new ScriptFileMarker
+ {
+ Level = ScriptFileMarkerLevel.Error,
+ Message = MessageHelper.GetLocalizedMessage(messageType, variableName, sqlDbType, literalValue),
+ ScriptRegion = new ScriptRegion
+ {
+ StartLineNumber = literal.StartLine,
+ StartColumnNumber = literal.StartColumn,
+ EndLineNumber = literal.StartLine,
+ EndColumnNumber = literal.StartColumn + literalValue.Length
+ }
+ });
+ }
+
+ private object ParseDateTime(string literalValue)
+ {
+ return DateTime.ParseExact(literalValue, SUPPORTED_ISO_DATE_TIME_FORMATS, CultureInfo.InvariantCulture, DateTimeStyles.None);
+ }
+
+ private object ParseDate(string literalValue)
+ {
+ return DateTime.ParseExact(literalValue, SUPPORTED_ISO_DATE_FORMATS, CultureInfo.InvariantCulture, DateTimeStyles.None);
+ }
+
+ private object ParseDateTimeOffset(string literalValue)
+ {
+ return DateTimeOffset.ParseExact(literalValue, SUPPORTED_ISO_DATE_TIME_OFFSET_FORMATS, CultureInfo.InvariantCulture, DateTimeStyles.None);
+ }
+
+ private bool ShouldParameterize(Literal literal)
+ {
+ switch (literal.LiteralType)
+ {
+ case LiteralType.Integer:
+ case LiteralType.Real:
+ case LiteralType.Money:
+ case LiteralType.Binary:
+ case LiteralType.String:
+ case LiteralType.Numeric:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private void ExtractPrecisionAndScale(IList dataTypeParameters, SqlParameter sqlParameter)
+ {
+ if (dataTypeParameters != null && dataTypeParameters.Count == 2)
+ {
+ Literal precisionLiteral = dataTypeParameters[0];
+
+ if (byte.TryParse(precisionLiteral.Value, out byte precision))
+ {
+ sqlParameter.Precision = precision;
+ }
+
+ Literal scaleLiteral = dataTypeParameters[1];
+
+ if (byte.TryParse(scaleLiteral.Value, out byte scale))
+ {
+ sqlParameter.Scale = scale;
+ }
+ }
+ }
+
+ private void ExtractPrecision(IList dataTypeParameters, SqlParameter sqlParameter)
+ {
+ if (dataTypeParameters != null && dataTypeParameters.Count == 1)
+ {
+ Literal precisionLiteral = dataTypeParameters[0];
+
+ if (byte.TryParse(precisionLiteral.Value, out byte precision))
+ {
+ sqlParameter.Precision = precision;
+ }
+ }
+ }
+
+ private void ExtractSize(IList dataTypeParameters, SqlParameter sqlParameter)
+ {
+ if (dataTypeParameters != null && dataTypeParameters.Count == 1)
+ {
+ Literal sizeLiteral = dataTypeParameters[0];
+
+ if (int.TryParse(sizeLiteral.Value, out int size))
+ {
+ sqlParameter.Size = size;
+ }
+ }
+ }
+
+ private object TryParseBinaryLiteral(string literalValue, string variableName, SqlDbType sqlDbType, int lineNumber)
+ {
+ if (literalValue.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ {
+ string hexString = literalValue.Substring(2);
+ return StringToByteArray(hexString);
+ }
+
+ throw new ParameterizationFormatException(MessageHelper.MessageType.BINARY_LITERAL_PREFIX_MISSING_ERROR, variableName, sqlDbType.ToString(), C_SHARP_BYTE_ARRAY, literalValue, lineNumber);
+ }
+
+ private string GetParameterName()
+ {
+ return "@p" + Guid.NewGuid().ToString("N"); //option N will give a guid without dashes
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/SqlParameterizer.cs b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/SqlParameterizer.cs
new file mode 100644
index 00000000..fdbc2373
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/SqlParameterizer.cs
@@ -0,0 +1,127 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Collections.Generic;
+using System.Data.Common;
+using System.IO;
+using System.Linq;
+using Microsoft.SqlServer.TransactSql.ScriptDom;
+using Microsoft.SqlTools.ServiceLayer.AutoParameterizaition.Exceptions;
+using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.AutoParameterizaition
+{
+ public static class SqlParameterizer
+ {
+ private const int maxStringLength = 300000; // Approximately 600 Kb
+ private static readonly IList EmptyCodeSenseItemList = Enumerable.Empty().ToList();
+
+ public static SqlScriptGenerator GetScriptGenerator() => new Sql150ScriptGenerator();
+
+ public static TSqlParser GetTSqlParser(bool initialQuotedIdentifiers) => new TSql150Parser(initialQuotedIdentifiers);
+
+ ///
+ /// This method will parameterize the given SqlCommand.
+ /// Any single literal on the RHS of a declare statement will be parameterized
+ /// Any other literals will be ignored
+ ///
+ /// Command that will need to be parameterized
+ public static void Parameterize(this DbCommand commandToParameterize)
+ {
+ TSqlFragment rootFragment = GetAbstractSyntaxTree(commandToParameterize);
+ TsqlMultiVisitor multiVisitor = new TsqlMultiVisitor(isCodeSenseRequest: false); // Use the vistor pattern to examine the parse tree
+ rootFragment.AcceptChildren(multiVisitor); // Now walk the tree
+
+ //reformat and validate the transformed command
+ SqlScriptGenerator scriptGenerator = GetScriptGenerator();
+ scriptGenerator.GenerateScript(rootFragment, out string formattedSQL);
+
+ if (!string.IsNullOrEmpty(formattedSQL))
+ {
+ commandToParameterize.CommandText = formattedSQL;
+ }
+
+ commandToParameterize.Parameters.AddRange(multiVisitor.Parameters.ToArray());
+
+ multiVisitor.Reset();
+ }
+
+ ///
+ /// Parses the given script to provide message, warning, error.
+ ///
+ /// Script that will be parsed
+ /// Used to emit telemetry events
+ ///
+ public static IList CodeSense(string scriptToParse)
+ {
+ if (scriptToParse == null)
+ {
+ return EmptyCodeSenseItemList;
+ }
+
+ int CurrentScriptlength = scriptToParse.Length;
+ if (CurrentScriptlength > maxStringLength)
+ {
+ ScriptFileMarker maxStringLengthCodeSenseItem = new ScriptFileMarker
+ {
+ Level = ScriptFileMarkerLevel.Error,
+ Message = SR.ScriptTooLarge(maxStringLength, CurrentScriptlength),
+ ScriptRegion = new ScriptRegion
+ {
+ // underline first row in the text
+ StartLineNumber = 1,
+ StartColumnNumber = 1,
+ EndLineNumber = 2,
+ EndColumnNumber = 1
+ }
+ };
+
+ return new ScriptFileMarker[] { maxStringLengthCodeSenseItem };
+ }
+
+ TSqlFragment rootFragment = GetAbstractSyntaxTree(scriptToParse);
+ TsqlMultiVisitor multiVisitor = new TsqlMultiVisitor(isCodeSenseRequest: true); // Use the vistor pattern to examine the parse tree
+ rootFragment.AcceptChildren(multiVisitor); // Now walk the tree
+
+ if (multiVisitor.CodeSenseErrors != null && multiVisitor.CodeSenseErrors.Count != 0)
+ {
+ multiVisitor.CodeSenseMessages.AddRange(multiVisitor.CodeSenseErrors);
+ }
+
+ return multiVisitor.CodeSenseMessages;
+ }
+
+ private static TSqlFragment GetAbstractSyntaxTree(DbCommand commandToParameterize)
+ {
+ // Capture the current CommandText in a format that the parser can work with
+ string commandText = commandToParameterize.CommandText;
+ int currentScriptLength = commandText.Length;
+
+ if (currentScriptLength > maxStringLength)
+ {
+ throw new ParameterizationScriptTooLargeException(currentScriptLength, errorMessage: SR.ScriptTooLarge(maxStringLength, currentScriptLength));
+ }
+
+ return GetAbstractSyntaxTree(commandText);
+ }
+
+ private static TSqlFragment GetAbstractSyntaxTree(string script)
+ {
+ using (TextReader textReader = new StringReader(script))
+ {
+ TSqlParser parser = GetTSqlParser(true);
+ TSqlFragment rootFragment = parser.Parse(textReader, out IList parsingErrors); // Get the parse tree
+
+ // if we could not parse the SQL we will throw an exception. Better here than on the server
+ if (parsingErrors.Count > 0)
+ {
+ throw new ParameterizationParsingException(parsingErrors[0].Line, parsingErrors[0].Column, parsingErrors[0].Message);
+ }
+
+ return rootFragment;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/TsqlMultiVisitor.cs b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/TsqlMultiVisitor.cs
new file mode 100644
index 00000000..bb0f847f
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/AutoParameterizaition/TsqlMultiVisitor.cs
@@ -0,0 +1,170 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Data.SqlClient;
+using Microsoft.SqlServer.TransactSql.ScriptDom;
+using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.AutoParameterizaition
+{
+ ///
+ /// Entry point for SqlParameterization, this class is responsible for visiting the parse tree and identifying the scalar expressions to be parameterized
+ ///
+ internal class TsqlMultiVisitor : TSqlFragmentVisitor
+ {
+ private readonly ScalarExpressionTransformer ScalarExpressionTransformer;
+ private readonly bool IsCodeSenseRequest;
+
+ private Dictionary _executionParameters = null;
+
+ public List Parameters { get; private set; }
+
+ public List CodeSenseMessages { get; private set; }
+
+ public List CodeSenseErrors { get; private set; }
+
+ public Dictionary ExecutionParameters
+ {
+ get
+ {
+ if (_executionParameters == null)
+ {
+ _executionParameters = new Dictionary();
+ }
+
+ return _executionParameters;
+ }
+ }
+
+ public TsqlMultiVisitor(bool isCodeSenseRequest)
+ {
+ Parameters = new List();
+ IsCodeSenseRequest = isCodeSenseRequest;
+ CodeSenseMessages = new List();
+ CodeSenseErrors = new List();
+ ScalarExpressionTransformer = new ScalarExpressionTransformer(isCodeSenseRequest, CodeSenseErrors);
+ }
+
+ public override void ExplicitVisit(DeclareVariableStatement node)
+ {
+ if (node == null || node.Declarations == null)
+ {
+ return;
+ }
+
+ StringBuilder codeSenseMessageStringBuilder = new StringBuilder();
+ int endLine = -1;
+ int endCol = -1;
+
+ foreach (DeclareVariableElement declareVariableElement in node.Declarations)
+ {
+ if (declareVariableElement.DataType is SqlDataTypeReference dataTypeReference)
+ {
+ SqlDataTypeOption sqlDataTypeOption = dataTypeReference.SqlDataTypeOption;
+
+ if (ShouldParamterize(sqlDataTypeOption))
+ {
+ IList sqlDataTypeParameters = dataTypeReference.Parameters;
+ ScalarExpressionTransformer.SqlDataTypeOption = sqlDataTypeOption;
+ ScalarExpressionTransformer.SqlDataTypeParameters = sqlDataTypeParameters;
+ ScalarExpressionTransformer.VariableName = declareVariableElement.VariableName.Value;
+
+ ScalarExpression declareVariableElementValue = declareVariableElement.Value;
+ ScalarExpressionTransformer.ExplicitVisit(declareVariableElementValue);
+ declareVariableElement.Value = ScalarExpressionTransformer.GetTransformedExpression();
+ IList sqlParameters = ScalarExpressionTransformer.Parameters;
+
+ if (sqlParameters.Count == 1 && declareVariableElementValue != null)
+ {
+ codeSenseMessageStringBuilder.Append(SR.ParameterizationDetails(declareVariableElement.VariableName.Value,
+ sqlParameters[0].SqlDbType.ToString(),
+ sqlParameters[0].Size,
+ sqlParameters[0].Precision,
+ sqlParameters[0].Scale,
+ sqlParameters[0].SqlValue.ToString()));
+
+ endLine = declareVariableElementValue.StartLine;
+ endCol = declareVariableElementValue.StartColumn + declareVariableElementValue.FragmentLength;
+
+ if (!IsCodeSenseRequest)
+ {
+ string sqlParameterKey = sqlParameters[0].SqlDbType.ToString();
+ ExecutionParameters.TryGetValue(sqlParameterKey, out int currentCount);
+ ExecutionParameters[sqlParameterKey] = currentCount + 1;
+ }
+ }
+
+ Parameters.AddRange(sqlParameters);
+ ScalarExpressionTransformer.Reset();
+ }
+ }
+ }
+
+ if (codeSenseMessageStringBuilder.Length > 0)
+ {
+ CodeSenseMessages.Add(new ScriptFileMarker
+ {
+ Level = ScriptFileMarkerLevel.Information,
+ Message = codeSenseMessageStringBuilder.ToString(),
+ ScriptRegion = new ScriptRegion
+ {
+ StartLineNumber = node.StartLine,
+ StartColumnNumber = node.StartColumn,
+ EndLineNumber = endLine == -1 ? node.StartLine : endLine,
+ EndColumnNumber = endCol == -1 ? node.StartColumn + node.LastTokenIndex - node.FirstTokenIndex : endCol
+ }
+ });
+ }
+
+ node.AcceptChildren(this);
+ base.ExplicitVisit(node); // let the base class finish up
+ }
+
+ private bool ShouldParamterize(SqlDataTypeOption sqlDataTypeOption)
+ {
+ switch (sqlDataTypeOption)
+ {
+ case SqlDataTypeOption.BigInt:
+ case SqlDataTypeOption.Int:
+ case SqlDataTypeOption.SmallInt:
+ case SqlDataTypeOption.TinyInt:
+ case SqlDataTypeOption.Bit:
+ case SqlDataTypeOption.Decimal:
+ case SqlDataTypeOption.Numeric:
+ case SqlDataTypeOption.Money:
+ case SqlDataTypeOption.SmallMoney:
+ case SqlDataTypeOption.Float:
+ case SqlDataTypeOption.Real:
+ case SqlDataTypeOption.DateTime:
+ case SqlDataTypeOption.SmallDateTime:
+ case SqlDataTypeOption.Char:
+ case SqlDataTypeOption.VarChar:
+ case SqlDataTypeOption.NChar:
+ case SqlDataTypeOption.NVarChar:
+ case SqlDataTypeOption.Binary:
+ case SqlDataTypeOption.VarBinary:
+ case SqlDataTypeOption.UniqueIdentifier:
+ case SqlDataTypeOption.Date:
+ case SqlDataTypeOption.Time:
+ case SqlDataTypeOption.DateTime2:
+ case SqlDataTypeOption.DateTimeOffset:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public void Reset()
+ {
+ Parameters.Clear();
+ CodeSenseMessages.Clear();
+ CodeSenseErrors.Clear();
+ ExecutionParameters.Clear();
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
index df7350a9..0bbdf45f 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
@@ -22,6 +22,7 @@ using Microsoft.SqlServer.Management.SqlParser.Parser;
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
using Microsoft.SqlTools.Extensibility;
using Microsoft.SqlTools.Hosting.Protocol;
+using Microsoft.SqlTools.ServiceLayer.AutoParameterizaition;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
@@ -790,6 +791,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
try
{
bool oldEnableIntelliSense = oldSettings.SqlTools.IntelliSense.EnableIntellisense;
+ bool oldAlwaysEncryptedParameterizationEnabled = oldSettings.SqlTools.QueryExecutionSettings.IsAlwaysEncryptedParameterizationEnabled;
bool? oldEnableDiagnostics = oldSettings.SqlTools.IntelliSense.EnableErrorChecking;
// update the current settings to reflect any changes
@@ -797,13 +799,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// if script analysis settings have changed we need to clear the current diagnostic markers
if (oldEnableIntelliSense != newSettings.SqlTools.IntelliSense.EnableIntellisense
- || oldEnableDiagnostics != newSettings.SqlTools.IntelliSense.EnableErrorChecking)
+ || oldEnableDiagnostics != newSettings.SqlTools.IntelliSense.EnableErrorChecking
+ || oldAlwaysEncryptedParameterizationEnabled != newSettings.SqlTools.QueryExecutionSettings.IsAlwaysEncryptedParameterizationEnabled)
{
// if the user just turned off diagnostics then send an event to clear the error markers
if (!newSettings.IsDiagnosticsEnabled)
{
- ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0];
-
foreach (var scriptFile in CurrentWorkspace.GetOpenedFiles())
{
await DiagnosticsHelper.ClearScriptDiagnostics(scriptFile.ClientUri, eventContext);
@@ -812,7 +813,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// otherwise rerun diagnostic analysis on all opened SQL files
else
{
- await this.RunScriptDiagnostics(CurrentWorkspace.GetOpenedFiles(), eventContext);
+ await RunScriptDiagnostics(CurrentWorkspace.GetOpenedFiles(), eventContext);
}
}
}
@@ -1698,6 +1699,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
}
+ if (CurrentWorkspaceSettings.QueryExecutionSettings.IsAlwaysEncryptedParameterizationEnabled)
+ {
+ markers.AddRange(SqlParameterizer.CodeSense(scriptFile.Contents));
+ }
+
return markers.ToArray();
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
index 8e7aae92..0bb8c7f2 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
@@ -3068,6 +3068,41 @@ namespace Microsoft.SqlTools.ServiceLayer
return Keys.GetString(Keys.QueryServiceSaveAsFail, fileName, message);
}
+ public static string ParameterizationDetails(string variableName, string sqlDbType, int size, int precision, int scale, string sqlValue)
+ {
+ return Keys.GetString(Keys.ParameterizationDetails, variableName, sqlDbType, size, precision, scale, sqlValue);
+ }
+
+ public static string ErrorMessageHeader(int lineNumber)
+ {
+ return Keys.GetString(Keys.ErrorMessageHeader, lineNumber);
+ }
+
+ public static string ErrorMessage(string variableName, string sqlDataType, string literalValue)
+ {
+ return Keys.GetString(Keys.ErrorMessage, variableName, sqlDataType, literalValue);
+ }
+
+ public static string DateTimeErrorMessage(string variableName, string sqlDataType, string literalValue)
+ {
+ return Keys.GetString(Keys.DateTimeErrorMessage, variableName, sqlDataType, literalValue);
+ }
+
+ public static string BinaryLiteralPrefixMissingError(string variableName, string sqlDataType, string literalValue)
+ {
+ return Keys.GetString(Keys.BinaryLiteralPrefixMissingError, variableName, sqlDataType, literalValue);
+ }
+
+ public static string ParsingErrorHeader(int lineNumber, int columnNumber)
+ {
+ return Keys.GetString(Keys.ParsingErrorHeader, lineNumber, columnNumber);
+ }
+
+ public static string ScriptTooLarge(int maxChars, int currentChars)
+ {
+ return Keys.GetString(Keys.ScriptTooLarge, maxChars, currentChars);
+ }
+
public static string SerializationServiceUnsupportedFormat(string formatName)
{
return Keys.GetString(Keys.SerializationServiceUnsupportedFormat, formatName);
@@ -3366,6 +3401,27 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string SqlCmdUnsupportedToken = "SqlCmdUnsupportedToken";
+ public const string ParameterizationDetails = "ParameterizationDetails";
+
+
+ public const string ErrorMessageHeader = "ErrorMessageHeader";
+
+
+ public const string ErrorMessage = "ErrorMessage";
+
+
+ public const string DateTimeErrorMessage = "DateTimeErrorMessage";
+
+
+ public const string BinaryLiteralPrefixMissingError = "BinaryLiteralPrefixMissingError";
+
+
+ public const string ParsingErrorHeader = "ParsingErrorHeader";
+
+
+ public const string ScriptTooLarge = "ScriptTooLarge";
+
+
public const string SerializationServiceUnsupportedFormat = "SerializationServiceUnsupportedFormat";
@@ -4461,6 +4517,12 @@ namespace Microsoft.SqlTools.ServiceLayer
}
+ public static string GetString(string key, object arg0, object arg1, object arg2)
+ {
+ return string.Format(global::System.Globalization.CultureInfo.CurrentCulture, resourceManager.GetString(key, _culture), arg0, arg1, arg2);
+ }
+
+
public static string GetString(string key, object arg0, object arg1, object arg2, object arg3)
{
return string.Format(global::System.Globalization.CultureInfo.CurrentCulture, resourceManager.GetString(key, _culture), arg0, arg1, arg2, arg3);
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
index 027ae7cc..253f8b35 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
@@ -348,6 +348,41 @@
Encountered unsupported token {0}
+
+ {0} will be converted to a Microsoft.Data.SqlClient.SqlParameter object with the following properties: SqlDbType = {1}, Size = {2}, Precision = {3}, Scale = {4}, SqlValue = {5}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDbType (string), 2 - size (int), 3 - precision (int), 4 - scale (int), 5 - sqlValue (string)
+
+
+ Line {0}
+ .
+ Parameters: 0 - lineNumber (int)
+
+
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType). Literal value: {2}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDataType (string), 2 - literalValue (string)
+
+
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as it used an unsupported date/time format. Use one of the supported date/time formats. Literal value: {2}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDataType (string), 2 - literalValue (string)
+
+
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as prefix 0x is expected for a binary literals. Literal value: {2}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDataType (string), 2 - literalValue (string)
+
+
+ Line {0}, column {1}
+ .
+ Parameters: 0 - lineNumber (int), 1 - columnNumber (int)
+
+
+ The current script is too large for Parameterization for Always Encrypted, please disable Parameterization for Always Encrypted in Query Options (Query > Query Options > Execution > Advanced). Maximum allowable length: {0} characters, Current script length: {1} characters
+ .
+ Parameters: 0 - maxChars (int), 1 - currentChars (int)
+
Unsupported Save Format: {0}
.
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
index 16ec5fb8..5004d962 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
@@ -150,6 +150,22 @@ SqlCmdExitOnError = An error was encountered during execution of batch. Exiting.
SqlCmdUnsupportedToken = Encountered unsupported token {0}
+### AutoParameterization for Always Encrypted strings
+
+ParameterizationDetails (string variableName, string sqlDbType, int size, int precision, int scale, string sqlValue) = {0} will be converted to a Microsoft.Data.SqlClient.SqlParameter object with the following properties: SqlDbType = {1}, Size = {2}, Precision = {3}, Scale = {4}, SqlValue = {5}
+
+ErrorMessageHeader(int lineNumber) = Line {0}
+
+ErrorMessage (string variableName, string sqlDataType, string literalValue) = Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType). Literal value: {2}
+
+DateTimeErrorMessage (string variableName, string sqlDataType, string literalValue) = Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as it used an unsupported date/time format. Use one of the supported date/time formats. Literal value: {2}
+
+BinaryLiteralPrefixMissingError (string variableName, string sqlDataType, string literalValue) = Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as prefix 0x is expected for a binary literals. Literal value: {2}
+
+ParsingErrorHeader (int lineNumber, int columnNumber) = Line {0}, column {1}
+
+ScriptTooLarge (int maxChars, int currentChars) = The current script is too large for Parameterization for Always Encrypted, please disable Parameterization for Always Encrypted in Query Options (Query > Query Options > Execution > Advanced). Maximum allowable length: {0} characters, Current script length: {1} characters
+
############################################################################
# Serialization Service
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
index 2605049f..c34c6ac7 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
@@ -2056,11 +2056,6 @@
Encountered unsupported token {0}
-
- A SQL Assessment operation's Execute method should not be called more than once
- A SQL Assessment operation's Execute method should not be called more than once
-
-
Generate SQL Assessment script
Generate SQL Assessment script
@@ -2081,6 +2076,48 @@
Unsupported engine edition {0}
.
Parameters: 0 - editionCode (int)
+
+
+ {0} will be converted to a Microsoft.Data.SqlClient.SqlParameter object with the following properties: SqlDbType = {1}, Size = {2}, Precision = {3}, Scale = {4}, SqlValue = {5}
+ {0} will be converted to a Microsoft.Data.SqlClient.SqlParameter object with the following properties: SqlDbType = {1}, Size = {2}, Precision = {3}, Scale = {4}, SqlValue = {5}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDbType (string), 2 - size (int), 3 - precision (int), 4 - scale (int), 5 - sqlValue (string)
+
+
+
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType). Literal value: {2}
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType). Literal value: {2}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDataType (string), 2 - literalValue (string)
+
+
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as it used an unsupported date/time format. Use one of the supported date/time formats. Literal value: {2}
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as it used an unsupported date/time format. Use one of the supported date/time formats. Literal value: {2}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDataType (string), 2 - literalValue (string)
+
+
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as prefix 0x is expected for a binary literals. Literal value: {2}
+ Unable to convert {0} to a Microsoft.Data.SqlClient.SqlParameter object. The specified literal cannot be converted to {1}(Microsoft.Data.SqlDbType), as prefix 0x is expected for a binary literals. Literal value: {2}
+ .
+ Parameters: 0 - variableName (string), 1 - sqlDataType (string), 2 - literalValue (string)
+
+
+
+ The current script is too large for Parameterization for Always Encrypted, please disable Parameterization for Always Encrypted in Query Options (Query > Query Options > Execution > Advanced). Maximum allowable length: {0} characters, Current script length: {1} characters
+ The current script is too large for Parameterization for Always Encrypted, please disable Parameterization for Always Encrypted in Query Options (Query > Query Options > Execution > Advanced). Maximum allowable length: {0} characters, Current script length: {1} characters
+ .
+ Parameters: 0 - maxChars (int), 1 - currentChars (int)