Skip to content

Commit ebac54f

Browse files
committed
Added public method ExecuteScalarAsync<T>
1 parent c0ebd28 commit ebac54f

File tree

4 files changed

+254
-35
lines changed

4 files changed

+254
-35
lines changed

Diff for: Snowflake.Client.Tests/IntegrationTests/SnowflakeQueriesTest.cs

+31-7
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,60 @@ namespace Snowflake.Client.Tests.IntegrationTests
77
public class SnowflakeQueriesTest : IntegrationTestBase
88
{
99
[Test]
10-
public async Task ExecuteScalar_WithResult()
10+
public async Task ExecuteScalar_String()
1111
{
1212
var result = await _snowflakeClient.ExecuteScalarAsync("SELECT CURRENT_USER();");
13-
1413
Assert.IsTrue(!string.IsNullOrWhiteSpace(result));
1514
}
1615

1716
[Test]
1817
public async Task ExecuteScalar_Null()
1918
{
2019
var result = await _snowflakeClient.ExecuteScalarAsync("SELECT 1 WHERE 2 > 3;");
21-
20+
Assert.IsNull(result);
21+
}
22+
23+
[Test]
24+
public async Task ExecuteScalar_Number()
25+
{
26+
var result = await _snowflakeClient.ExecuteScalarAsync("SELECT 1;");
27+
Assert.AreEqual("1", result);
28+
}
29+
30+
[Test]
31+
public async Task ExecuteScalar_Typed_String()
32+
{
33+
var result = await _snowflakeClient.ExecuteScalarAsync<string>("SELECT CURRENT_USER();");
34+
Assert.IsTrue(!string.IsNullOrWhiteSpace(result));
35+
}
36+
37+
[Test]
38+
public async Task ExecuteScalar_Typed_Null()
39+
{
40+
var result = await _snowflakeClient.ExecuteScalarAsync<string>("SELECT 1 WHERE 2 > 3;");
2241
Assert.IsNull(result);
2342
}
2443

44+
[Test]
45+
public async Task ExecuteScalar_Typed_Number()
46+
{
47+
var result = await _snowflakeClient.ExecuteScalarAsync<int>("SELECT 1;");
48+
Assert.AreEqual(1, result);
49+
}
50+
2551
[Test]
2652
public async Task Execute()
2753
{
2854
// todo: do temporary insert to get affected rows > 0
2955

30-
var result = await _snowflakeClient.ExecuteAsync("SELECT 1;");
31-
32-
Assert.IsTrue(result == -1);
56+
var affectedRows = await _snowflakeClient.ExecuteAsync("SELECT 1;");
57+
Assert.AreEqual(-1, affectedRows);
3358
}
3459

3560
[Test]
3661
public async Task QueryRawResponse()
3762
{
3863
var result = await _snowflakeClient.QueryRawResponseAsync("SELECT CURRENT_USER();");
39-
4064
Assert.IsNotNull(result);
4165
}
4266
}

Diff for: Snowflake.Client.Tests/UnitTests/SnowflakeDataMapperTests.cs

+197-28
Original file line numberDiff line numberDiff line change
@@ -11,49 +11,186 @@ namespace Snowflake.Client.Tests.UnitTests
1111
public class SnowflakeDataMapperTests
1212
{
1313
[Test]
14-
public void SnowflakeDataMapper_MapTo_CustomClass()
14+
public void ResponseWithValues_MapTo_CustomClass()
1515
{
1616
var responseSample = GetFakeResponse();
17+
var firstObjectRowset = new List<List<string>>() { responseSample.RowSet[0] };
18+
var mappedObjects = SnowflakeDataMapper.MapTo<CustomClass>(responseSample.RowType, firstObjectRowset);
19+
var mappedObject = mappedObjects.FirstOrDefault();
1720

18-
var mappedObjects = SnowflakeDataMapper.MapTo<CustomClass>(responseSample.RowType, responseSample.RowSet);
19-
var firstObject = mappedObjects.FirstOrDefault();
20-
21-
Assert.IsNotNull(firstObject);
22-
Assert.AreEqual("Sometext", firstObject.StringProperty);
23-
Assert.AreEqual(true, firstObject.BoolProperty);
24-
Assert.AreEqual(7, firstObject.IntProperty);
25-
Assert.AreEqual(27.6F, firstObject.FloatProperty);
26-
Assert.AreEqual(19.239834M, firstObject.DecimalProperty);
27-
Assert.AreEqual(Guid.Parse("e7412bbf-88ee-4149-b341-101e0f72ec7c"), firstObject.GuidProperty);
28-
Assert.AreEqual(new byte[] { 0, 128, 255 }, firstObject.ByteArrayProperty);
21+
Assert.IsNotNull(mappedObject);
22+
Assert.AreEqual("Sometext", mappedObject.StringProperty);
23+
Assert.AreEqual(true, mappedObject.BoolProperty);
24+
Assert.AreEqual(7, mappedObject.IntProperty);
25+
Assert.AreEqual(27.6F, mappedObject.FloatProperty);
26+
Assert.AreEqual(19.239834M, mappedObject.DecimalProperty);
27+
Assert.AreEqual(Guid.Parse("e7412bbf-88ee-4149-b341-101e0f72ec7c"), mappedObject.GuidProperty);
28+
Assert.AreEqual(new byte[] { 0, 128, 255 }, mappedObject.ByteArrayProperty);
2929

3030
var dateTime = DateTime.ParseExact("2020-09-13 12:26:40.0000000", "yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture);
31-
Assert.AreEqual(dateTime, firstObject.DateTimeProperty);
31+
Assert.AreEqual(dateTime, mappedObject.DateTimeProperty);
3232

3333
var dateTimeOffset = DateTimeOffset.ParseExact("2020-09-13 12:26:40.0000000", "yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
34-
Assert.AreEqual(dateTimeOffset, firstObject.DateTimeOffsetProperty);
34+
Assert.AreEqual(dateTimeOffset, mappedObject.DateTimeOffsetProperty);
3535
}
3636

3737
[Test]
38-
public void SnowflakeDataMapper_MapTo_CustomClassWithNullableProps()
38+
public void ResponseWithValues_MapTo_CustomClassWithNullableProps()
3939
{
4040
var responseSample = GetFakeResponse();
41+
var firstObjectRowset = new List<List<string>>() { responseSample.RowSet[0] };
42+
var mappedObjects = SnowflakeDataMapper.MapTo<CustomClassNullables>(responseSample.RowType, firstObjectRowset);
43+
var mappedObject = mappedObjects.FirstOrDefault();
4144

42-
var mappedObjects = SnowflakeDataMapper.MapTo<CustomClassNullables>(responseSample.RowType, responseSample.RowSet);
43-
var firstObject = mappedObjects.FirstOrDefault();
44-
45-
Assert.IsNotNull(firstObject);
46-
Assert.AreEqual(true, firstObject.BoolProperty);
47-
Assert.AreEqual(7, firstObject.IntProperty);
48-
Assert.AreEqual(27.6F, firstObject.FloatProperty);
49-
Assert.AreEqual(19.239834M, firstObject.DecimalProperty);
50-
Assert.AreEqual(Guid.Parse("e7412bbf-88ee-4149-b341-101e0f72ec7c"), firstObject.GuidProperty);
45+
Assert.IsNotNull(mappedObject);
46+
Assert.AreEqual(true, mappedObject.BoolProperty);
47+
Assert.AreEqual(7, mappedObject.IntProperty);
48+
Assert.AreEqual(27.6F, mappedObject.FloatProperty);
49+
Assert.AreEqual(19.239834M, mappedObject.DecimalProperty);
50+
Assert.AreEqual(Guid.Parse("e7412bbf-88ee-4149-b341-101e0f72ec7c"), mappedObject.GuidProperty);
5151

5252
var dateTime = DateTime.ParseExact("2020-09-13 12:26:40.0000000", "yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture);
53-
Assert.AreEqual(dateTime, firstObject.DateTimeProperty);
53+
Assert.AreEqual(dateTime, mappedObject.DateTimeProperty);
5454

5555
var dateTimeOffset = DateTimeOffset.ParseExact("2020-09-13 12:26:40.0000000", "yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
56-
Assert.AreEqual(dateTimeOffset, firstObject.DateTimeOffsetProperty);
56+
Assert.AreEqual(dateTimeOffset, mappedObject.DateTimeOffsetProperty);
57+
}
58+
59+
[Test]
60+
public void ResponseWithStringNull_MapTo_CustomClassWithNullables()
61+
{
62+
var responseSample = GetFakeResponse();
63+
64+
var firstObjectRowset = new List<List<string>>() { responseSample.RowSet[1] };
65+
var mappedObjects = SnowflakeDataMapper.MapTo<CustomClassNullables>(responseSample.RowType, firstObjectRowset);
66+
var mappedObject = mappedObjects.FirstOrDefault();
67+
68+
Assert.IsNotNull(mappedObject);
69+
Assert.AreEqual(null, mappedObject.StringProperty);
70+
Assert.AreEqual(null, mappedObject.BoolProperty);
71+
Assert.AreEqual(null, mappedObject.IntProperty);
72+
Assert.AreEqual(null, mappedObject.FloatProperty);
73+
Assert.AreEqual(null, mappedObject.DecimalProperty);
74+
Assert.AreEqual(null, mappedObject.GuidProperty);
75+
Assert.AreEqual(null, mappedObject.DateTimeProperty);
76+
Assert.AreEqual(null, mappedObject.DateTimeOffsetProperty);
77+
Assert.AreEqual(null, mappedObject.ByteArrayProperty);
78+
}
79+
80+
[Test]
81+
public void ResponseWithNull_MapTo_CustomClassWithNullables()
82+
{
83+
var responseSample = GetFakeResponse();
84+
85+
var firstObjectRowset = new List<List<string>>() { responseSample.RowSet[2] };
86+
var mappedObjects = SnowflakeDataMapper.MapTo<CustomClassNullables>(responseSample.RowType, firstObjectRowset);
87+
var mappedObject = mappedObjects.FirstOrDefault();
88+
89+
Assert.IsNotNull(mappedObject);
90+
Assert.AreEqual(null, mappedObject.StringProperty);
91+
Assert.AreEqual(null, mappedObject.BoolProperty);
92+
Assert.AreEqual(null, mappedObject.IntProperty);
93+
Assert.AreEqual(null, mappedObject.FloatProperty);
94+
Assert.AreEqual(null, mappedObject.DecimalProperty);
95+
Assert.AreEqual(null, mappedObject.GuidProperty);
96+
Assert.AreEqual(null, mappedObject.DateTimeProperty);
97+
Assert.AreEqual(null, mappedObject.DateTimeOffsetProperty);
98+
Assert.AreEqual(null, mappedObject.ByteArrayProperty);
99+
}
100+
101+
[Test]
102+
public void ResponseWithValues_MapTo_SingleValue()
103+
{
104+
var responseSample = GetFakeResponse();
105+
var rowSet = responseSample.RowSet[0];
106+
var rowType = responseSample.RowType;
107+
108+
var stringValue = SnowflakeDataMapper.MapTo<string>(rowType[0], rowSet[0]);
109+
Assert.AreEqual("Sometext", stringValue);
110+
111+
var boolValue = SnowflakeDataMapper.MapTo<bool>(rowType[1], rowSet[1]);
112+
Assert.AreEqual(true, boolValue);
113+
114+
var intValue = SnowflakeDataMapper.MapTo<int>(rowType[2], rowSet[2]);
115+
Assert.AreEqual(7, intValue);
116+
117+
var floatValue = SnowflakeDataMapper.MapTo<float>(rowType[3], rowSet[3]);
118+
Assert.AreEqual(27.6F, floatValue);
119+
120+
var decimalValue = SnowflakeDataMapper.MapTo<decimal>(rowType[4], rowSet[4]);
121+
Assert.AreEqual(19.239834M, decimalValue);
122+
123+
var dateTimeExpected = DateTime.ParseExact("2020-09-13 12:26:40.0000000", "yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture);
124+
var dateTimeValue = SnowflakeDataMapper.MapTo<DateTime>(rowType[5], rowSet[5]);
125+
Assert.AreEqual(dateTimeExpected, dateTimeValue);
126+
127+
var dateTimeOffsetExpected = DateTimeOffset.ParseExact("2020-09-13 12:26:40.0000000", "yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
128+
var dateTimeOffsetValue = SnowflakeDataMapper.MapTo<DateTimeOffset>(rowType[6], rowSet[6]);
129+
Assert.AreEqual(dateTimeOffsetExpected, dateTimeOffsetValue);
130+
131+
var guidValue = SnowflakeDataMapper.MapTo<Guid>(rowType[7], rowSet[7]);
132+
Assert.AreEqual(Guid.Parse("e7412bbf-88ee-4149-b341-101e0f72ec7c"), guidValue);
133+
134+
var bytesValues = SnowflakeDataMapper.MapTo<byte[]>(rowType[8], rowSet[8]);
135+
Assert.AreEqual(new byte[] { 0, 128, 255 }, bytesValues);
136+
}
137+
138+
[Test]
139+
public void ResponseWithStringNull_MapTo_SingleValueNullable()
140+
{
141+
var responseSample = GetFakeResponse();
142+
var rowSet = responseSample.RowSet[1];
143+
var rowType = responseSample.RowType;
144+
145+
var boolValue = SnowflakeDataMapper.MapTo<bool?>(rowType[1], rowSet[1]);
146+
Assert.AreEqual(null, boolValue);
147+
148+
var intValue = SnowflakeDataMapper.MapTo<int?>(rowType[2], rowSet[2]);
149+
Assert.AreEqual(null, intValue);
150+
151+
var floatValue = SnowflakeDataMapper.MapTo<float?>(rowType[3], rowSet[3]);
152+
Assert.AreEqual(null, floatValue);
153+
154+
var decimalValue = SnowflakeDataMapper.MapTo<decimal?>(rowType[4], rowSet[4]);
155+
Assert.AreEqual(null, decimalValue);
156+
157+
var dateTimeValue = SnowflakeDataMapper.MapTo<DateTime?>(rowType[5], rowSet[5]);
158+
Assert.AreEqual(null, dateTimeValue);
159+
160+
var dateTimeOffsetValue = SnowflakeDataMapper.MapTo<DateTimeOffset?>(rowType[6], rowSet[6]);
161+
Assert.AreEqual(null, dateTimeOffsetValue);
162+
163+
var guidValue = SnowflakeDataMapper.MapTo<Guid?>(rowType[7], rowSet[7]);
164+
Assert.AreEqual(null, guidValue);
165+
}
166+
167+
[Test]
168+
public void ResponseWithNull_MapTo_SingleValueNullable()
169+
{
170+
var responseSample = GetFakeResponse();
171+
var rowSet = responseSample.RowSet[2];
172+
var rowType = responseSample.RowType;
173+
174+
var boolValue = SnowflakeDataMapper.MapTo<bool?>(rowType[1], rowSet[1]);
175+
Assert.AreEqual(null, boolValue);
176+
177+
var intValue = SnowflakeDataMapper.MapTo<int?>(rowType[2], rowSet[2]);
178+
Assert.AreEqual(null, intValue);
179+
180+
var floatValue = SnowflakeDataMapper.MapTo<float?>(rowType[3], rowSet[3]);
181+
Assert.AreEqual(null, floatValue);
182+
183+
var decimalValue = SnowflakeDataMapper.MapTo<decimal?>(rowType[4], rowSet[4]);
184+
Assert.AreEqual(null, decimalValue);
185+
186+
var dateTimeValue = SnowflakeDataMapper.MapTo<DateTime?>(rowType[5], rowSet[5]);
187+
Assert.AreEqual(null, dateTimeValue);
188+
189+
var dateTimeOffsetValue = SnowflakeDataMapper.MapTo<DateTimeOffset?>(rowType[6], rowSet[6]);
190+
Assert.AreEqual(null, dateTimeOffsetValue);
191+
192+
var guidValue = SnowflakeDataMapper.MapTo<Guid?>(rowType[7], rowSet[7]);
193+
Assert.AreEqual(null, guidValue);
57194
}
58195

59196
private QueryExecResponseData GetFakeResponse()
@@ -70,7 +207,8 @@ private QueryExecResponseData GetFakeResponse()
70207
response.RowType.Add(new ColumnDescription() { Name = "GuidProperty", Type = "text" });
71208
response.RowType.Add(new ColumnDescription() { Name = "ByteArrayProperty", Type = "binary" });
72209

73-
response.RowSet.Add(new List<string>() {
210+
response.RowSet.Add(new List<string>()
211+
{
74212
"Sometext",
75213
"true",
76214
"7",
@@ -82,6 +220,33 @@ private QueryExecResponseData GetFakeResponse()
82220
"0080ff"
83221
});
84222

223+
response.RowSet.Add(new List<string>()
224+
{
225+
"null",
226+
"null",
227+
"null",
228+
"null",
229+
"null",
230+
"null",
231+
"null",
232+
"null",
233+
"null"
234+
});
235+
236+
237+
response.RowSet.Add(new List<string>()
238+
{
239+
null,
240+
null,
241+
null,
242+
null,
243+
null,
244+
null,
245+
null,
246+
null,
247+
null
248+
});
249+
85250
return response;
86251
}
87252

@@ -108,6 +273,8 @@ private class CustomClass
108273

109274
private class CustomClassNullables
110275
{
276+
public string StringProperty { get; set; }
277+
111278
public bool? BoolProperty { get; set; }
112279

113280
public int? IntProperty { get; set; }
@@ -121,6 +288,8 @@ private class CustomClassNullables
121288
public DateTimeOffset? DateTimeOffsetProperty { get; set; }
122289

123290
public Guid? GuidProperty { get; set; }
291+
292+
public byte[] ByteArrayProperty { get; set; }
124293
}
125294
}
126-
}
295+
}

Diff for: Snowflake.Client/SnowflakeClient.cs

+17
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,23 @@ public async Task<string> ExecuteScalarAsync(string sql, object sqlParams = null
146146
return rawResult;
147147
}
148148

149+
/// <summary>
150+
/// Execute SQL that selects a single value.
151+
/// </summary>
152+
/// <param name="sql">The SQL to execute.</param>
153+
/// <param name="sqlParams">The parameters to use for this command.</param>
154+
/// <returns>The first cell value returned as of type T.</returns>
155+
public async Task<T> ExecuteScalarAsync<T>(string sql, object sqlParams = null, CancellationToken ct = default)
156+
{
157+
var response = await QueryInternalAsync(sql, sqlParams, false, ct).ConfigureAwait(false);
158+
159+
var firstColumn = response.Data.RowType.FirstOrDefault();
160+
var firstColumnValue = response.Data.RowSet.FirstOrDefault()?.FirstOrDefault();
161+
162+
var result = SnowflakeDataMapper.MapTo<T>(firstColumn, firstColumnValue);
163+
return result;
164+
}
165+
149166
/// <summary>
150167
/// Execute parameterized SQL.
151168
/// </summary>

Diff for: Snowflake.Client/SnowflakeDataMapper.cs

+9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ public static void SetJsonMapperOptions(JsonSerializerOptions jsonMapperOptions)
2121
Configure(jsonMapperOptions);
2222
}
2323

24+
public static T MapTo<T>(ColumnDescription column, string value)
25+
{
26+
if (column == null)
27+
throw new ArgumentNullException(nameof(column));
28+
29+
var jsonToken = ConvertColumnValueToJsonToken(value, column.Type);
30+
return JsonSerializer.Deserialize<T>(jsonToken, _jsonMapperOptions);
31+
}
32+
2433
public static IEnumerable<T> MapTo<T>(List<ColumnDescription> columns, List<List<string>> rows)
2534
{
2635
if (columns == null || columns.Count == 0)

0 commit comments

Comments
 (0)