Skip to content

Commit

Permalink
fix decimal datatype handling (#1352)
Browse files Browse the repository at this point in the history
* Revert "Revert query execution changes (#1341)"

This reverts commit cb290cd.

* fix decimal and money

* timestamp

* fix code and tests

* add sql variant test
  • Loading branch information
alanrenmsft authored Jan 4, 2022
1 parent 18ca177 commit 995e9ba
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 25 deletions.
8 changes: 8 additions & 0 deletions src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8631,6 +8631,11 @@ public static string QueryServiceQueryFailed(string message)
return Keys.GetString(Keys.QueryServiceQueryFailed, message);
}

public static string QueryServiceUnsupportedSqlVariantType(string underlyingType, string columnName)
{
return Keys.GetString(Keys.QueryServiceUnsupportedSqlVariantType, underlyingType, columnName);
}

public static string QueryServiceSaveAsFail(string fileName, string message)
{
return Keys.GetString(Keys.QueryServiceSaveAsFail, fileName, message);
Expand Down Expand Up @@ -9011,6 +9016,9 @@ public class Keys
public const string QueryServiceResultSetTooLarge = "QueryServiceResultSetTooLarge";


public const string QueryServiceUnsupportedSqlVariantType = "QueryServiceUnsupportedSqlVariantType";


public const string QueryServiceSaveAsResultSetNotComplete = "QueryServiceSaveAsResultSetNotComplete";


Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@
<value>Result set has too many rows to be safely loaded</value>
<comment></comment>
</data>
<data name="QueryServiceUnsupportedSqlVariantType" xml:space="preserve">
<value>The underlying type &quot;{0}&quot; for sql variant column &quot;{1}&quot; could not be resolved.</value>
<comment>.
Parameters: 0 - underlyingType (string), 1 - columnName (string) </comment>
</data>
<data name="QueryServiceSaveAsResultSetNotComplete" xml:space="preserve">
<value>Result cannot be saved until query execution has completed</value>
<comment></comment>
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ QueryServiceResultSetHasNoResults = Query has no results to return

QueryServiceResultSetTooLarge = Result set has too many rows to be safely loaded

QueryServiceUnsupportedSqlVariantType(string underlyingType, string columnName) = The underlying type "{0}" for sql variant column "{1}" could not be resolved.

### Save As Requests

QueryServiceSaveAsResultSetNotComplete = Result cannot be saved until query execution has completed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
Expand Down Expand Up @@ -40,6 +41,8 @@ public class ServiceBufferFileStreamReader : IFileStreamReader

private readonly Dictionary<Type, ReadMethod> readMethods;

private readonly Dictionary<SqlDbType, Type> sqlDBTypeMap;

#endregion

/// <summary>
Expand Down Expand Up @@ -98,6 +101,37 @@ public ServiceBufferFileStreamReader(Stream stream, QueryExecutionSettings setti
{typeof(SqlGuid), (o, id, col) => ReadGuid(o, id)},
{typeof(SqlMoney), (o, id, col) => ReadMoney(o, id)},
};

sqlDBTypeMap = new Dictionary<SqlDbType, Type> {
{SqlDbType.BigInt, typeof(SqlInt64)},
{SqlDbType.Binary, typeof(SqlBinary)},
{SqlDbType.Bit, typeof(SqlBoolean)},
{SqlDbType.Char, typeof(SqlString)},
{SqlDbType.Date, typeof(SqlDateTime)},
{SqlDbType.DateTime, typeof(SqlDateTime)},
{SqlDbType.DateTime2, typeof(SqlDateTime)},
{SqlDbType.DateTimeOffset, typeof(DateTimeOffset)},
{SqlDbType.Decimal, typeof(SqlDecimal)},
{SqlDbType.Float, typeof(SqlDouble)},
{SqlDbType.Image, typeof(SqlBinary)},
{SqlDbType.Int, typeof(SqlInt32)},
{SqlDbType.Money, typeof(SqlMoney)},
{SqlDbType.NChar, typeof(SqlString)},
{SqlDbType.NText, typeof(SqlString)},
{SqlDbType.NVarChar, typeof(SqlString)},
{SqlDbType.Real, typeof(SqlSingle)},
{SqlDbType.SmallDateTime, typeof(SqlDateTime)},
{SqlDbType.SmallInt, typeof(SqlInt16)},
{SqlDbType.SmallMoney, typeof(SqlMoney)},
{SqlDbType.Text, typeof(SqlString)},
{SqlDbType.Time, typeof(TimeSpan)},
{SqlDbType.Timestamp, typeof(SqlBinary)},
{SqlDbType.TinyInt, typeof(SqlByte)},
{SqlDbType.UniqueIdentifier, typeof(SqlGuid)},
{SqlDbType.VarBinary, typeof(SqlBinary)},
{SqlDbType.VarChar, typeof(SqlString)},
{SqlDbType.Xml, typeof(SqlString)}
};
}

#region IFileStreamStorage Implementation
Expand Down Expand Up @@ -134,19 +168,25 @@ public IList<DbCellValue> ReadRow(long fileOffset, long rowId, IEnumerable<DbCol
continue;
}

// The typename is stored in the string
// We need to specify the assembly name for SQL types in order to resolve the type correctly.
if (sqlVariantType.StartsWith("System.Data.SqlTypes."))
{
sqlVariantType = sqlVariantType + ", System.Data.Common";
}
colType = Type.GetType(sqlVariantType);

// Workaround .NET bug, see sqlbu# 440643 and vswhidbey# 599834
// TODO: Is this workaround necessary for .NET Core?
if (colType == null && sqlVariantType == "System.Data.SqlTypes.SqlSingle")
if (colType == null)
{
colType = typeof(SqlSingle);
throw new ArgumentException(SR.QueryServiceUnsupportedSqlVariantType(sqlVariantType, column.ColumnName));
}
}
else
{
colType = column.DataType;
Type type;
if (!sqlDBTypeMap.TryGetValue(column.SqlDbType, out type))
{
type = typeof(SqlString);
}
colType = type;
}

// Use the right read function for the type to read the data from the file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,19 @@ public ServiceBufferFileStreamWriter(Stream stream, QueryExecutionSettings setti
{typeof(byte[]), val => WriteBytes((byte[]) val)},
{typeof(Guid), val => WriteGuid((Guid) val)},

{typeof(SqlString), val => WriteNullable((SqlString) val, obj => WriteString((string) obj))},
{typeof(SqlInt16), val => WriteNullable((SqlInt16) val, obj => WriteInt16((short) obj))},
{typeof(SqlInt32), val => WriteNullable((SqlInt32) val, obj => WriteInt32((int) obj))},
{typeof(SqlInt64), val => WriteNullable((SqlInt64) val, obj => WriteInt64((long) obj)) },
{typeof(SqlByte), val => WriteNullable((SqlByte) val, obj => WriteByte((byte) obj)) },
{typeof(SqlBoolean), val => WriteNullable((SqlBoolean) val, obj => WriteBoolean((bool) obj)) },
{typeof(SqlDouble), val => WriteNullable((SqlDouble) val, obj => WriteDouble((double) obj)) },
{typeof(SqlSingle), val => WriteNullable((SqlSingle) val, obj => WriteSingle((float) obj)) },
{typeof(SqlString), val => WriteNullable((SqlString) val, obj => WriteString(((SqlString) obj).Value))},
{typeof(SqlInt16), val => WriteNullable((SqlInt16) val, obj => WriteInt16(((SqlInt16) obj).Value))},
{typeof(SqlInt32), val => WriteNullable((SqlInt32) val, obj => WriteInt32(((SqlInt32)obj).Value))},
{typeof(SqlInt64), val => WriteNullable((SqlInt64) val, obj => WriteInt64(((SqlInt64) obj).Value)) },
{typeof(SqlByte), val => WriteNullable((SqlByte) val, obj => WriteByte(((SqlByte) obj).Value)) },
{typeof(SqlBoolean), val => WriteNullable((SqlBoolean) val, obj => WriteBoolean(((SqlBoolean) obj).Value)) },
{typeof(SqlDouble), val => WriteNullable((SqlDouble) val, obj => WriteDouble(((SqlDouble) obj).Value)) },
{typeof(SqlSingle), val => WriteNullable((SqlSingle) val, obj => WriteSingle(((SqlSingle) obj).Value)) },
{typeof(SqlDecimal), val => WriteNullable((SqlDecimal) val, obj => WriteSqlDecimal((SqlDecimal) obj)) },
{typeof(SqlDateTime), val => WriteNullable((SqlDateTime) val, obj => WriteDateTime((DateTime) obj)) },
{typeof(SqlBytes), val => WriteNullable((SqlBytes) val, obj => WriteBytes((byte[]) obj)) },
{typeof(SqlBinary), val => WriteNullable((SqlBinary) val, obj => WriteBytes((byte[]) obj)) },
{typeof(SqlGuid), val => WriteNullable((SqlGuid) val, obj => WriteGuid((Guid) obj)) },
{typeof(SqlDateTime), val => WriteNullable((SqlDateTime) val, obj => WriteDateTime(((SqlDateTime) obj).Value)) },
{typeof(SqlBytes), val => WriteNullable((SqlBytes) val, obj => WriteBytes(((SqlBytes) obj).Value)) },
{typeof(SqlBinary), val => WriteNullable((SqlBinary) val, obj => WriteBytes(((SqlBinary) obj).Value)) },
{typeof(SqlGuid), val => WriteNullable((SqlGuid) val, obj => WriteGuid(((SqlGuid) obj).Value)) },
{typeof(SqlMoney), val => WriteNullable((SqlMoney) val, obj => WriteMoney((SqlMoney) obj)) }
};
}
Expand Down Expand Up @@ -299,7 +299,7 @@ internal int WriteChar(char val)
internal int WriteBoolean(bool val)
{
byteBuffer[0] = 0x01; // length
byteBuffer[1] = (byte) (val ? 0x01 : 0x00);
byteBuffer[1] = (byte)(val ? 0x01 : 0x00);
return FileUtilities.WriteWithLength(fileStream, byteBuffer, 2);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public Task<bool> ReadAsync(CancellationToken cancellationToken)
/// <returns>The value of the given column</returns>
public object GetValue(int i)
{
return sqlDataReader == null ? DbDataReader.GetValue(i) : sqlDataReader.GetValue(i);
return sqlDataReader == null ? DbDataReader.GetValue(i) : sqlDataReader.GetSqlValue(i);
}

/// <summary>
Expand All @@ -113,7 +113,7 @@ public void GetValues(object[] values)
}
else
{
sqlDataReader.GetValues(values);
sqlDataReader.GetSqlValues(values);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ public void GetBytesWithMaxCapacityTest()
Assert.NotNull(bytes);
}

/// <summary>
/// Validate GetBytesWithMaxCapacity
/// </summary>
[Test]
public void GetLongDecimalTest()
{
// SQL Server support up to 38 digits of decimal
var storageReader = GetTestStorageDataReader(
"SELECT 99999999999999999999999999999999999999");
storageReader.DbDataReader.Read();
var value = storageReader.GetValue(0);
Assert.AreEqual("99999999999999999999999999999999999999", value.ToString());
}

/// <summary>
/// Validate GetCharsWithMaxCapacity
/// </summary>
Expand All @@ -63,7 +77,7 @@ public void GetCharsWithMaxCapacityTest()
Assert.True(shortName.Length == 2);

Assert.Throws<ArgumentOutOfRangeException>(() => storageReader.GetBytesWithMaxCapacity(0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => storageReader.GetCharsWithMaxCapacity(0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => storageReader.GetCharsWithMaxCapacity(0, 0));
}

/// <summary>
Expand Down Expand Up @@ -94,10 +108,10 @@ public void StringWriterWithMaxCapacityTest()
writer.Write(output);
Assert.True(writer.ToString().Equals(output));
writer.Write('.');
Assert.True(writer.ToString().Equals(output + '.'));
Assert.True(writer.ToString().Equals(output + '.'));
writer.Write(output);
writer.Write('.');
Assert.True(writer.ToString().Equals(output + '.'));
}
}
}
}
Loading

0 comments on commit 995e9ba

Please sign in to comment.