Skip to content

Commit 44cfa24

Browse files
SNOW-1618183: Fix ConstraintException when calling DataTable.Load (#1100)
1 parent b0c398d commit 44cfa24

File tree

6 files changed

+176
-1
lines changed

6 files changed

+176
-1
lines changed

Snowflake.Data.Tests/IntegrationTests/SFDbDataReaderIT.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,6 +1636,38 @@ public void TestTimestampNtz(string testValue, int scale)
16361636
}
16371637
}
16381638

1639+
[Test]
1640+
[TestCase("array")]
1641+
[TestCase("object")]
1642+
[TestCase("variant")]
1643+
public void TestDataTableLoadOnSemiStructuredColumn(string type)
1644+
{
1645+
using (var conn = CreateAndOpenConnection())
1646+
{
1647+
var colName = "c1";
1648+
var expectedVal = "id:1";
1649+
CreateOrReplaceTable(conn, TableName, new[] { $"{colName} {type}" });
1650+
1651+
using (var cmd = conn.CreateCommand())
1652+
{
1653+
string insertCommand = $"insert into {TableName} select parse_json('{{{expectedVal}}}')";
1654+
cmd.CommandText = insertCommand;
1655+
1656+
var count = cmd.ExecuteNonQuery();
1657+
Assert.AreEqual(1, count);
1658+
1659+
cmd.CommandText = $"select {colName} from {TableName}";
1660+
using (var reader = cmd.ExecuteReader())
1661+
{
1662+
ValidateResultFormat(reader);
1663+
var dt = new DataTable();
1664+
dt.Load(reader);
1665+
Assert.AreEqual(expectedVal, DataTableParser.GetFirstRowValue(dt, colName));
1666+
}
1667+
}
1668+
}
1669+
}
1670+
16391671
private DbConnection CreateAndOpenConnection()
16401672
{
16411673
var conn = new SnowflakeDbConnection(ConnectionString);

Snowflake.Data.Tests/IntegrationTests/StructuredArraysIT.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Data;
34
using System.Linq;
45
using NUnit.Framework;
56
using Snowflake.Data.Client;
@@ -12,6 +13,36 @@ namespace Snowflake.Data.Tests.IntegrationTests
1213
[TestFixture]
1314
public class StructuredArraysIT: StructuredTypesIT
1415
{
16+
[Test]
17+
public void TestDataTableLoadOnStructuredArray()
18+
{
19+
// arrange
20+
using (var connection = new SnowflakeDbConnection(ConnectionString))
21+
{
22+
connection.Open();
23+
using (var command = connection.CreateCommand())
24+
{
25+
EnableStructuredTypes(connection);
26+
var expectedValueA = 'a';
27+
var expectedValueB = 'b';
28+
var expectedValueC = 'c';
29+
var arraySFString = $"ARRAY_CONSTRUCT('{expectedValueA}','{expectedValueB}','{expectedValueC}')::ARRAY(TEXT)";
30+
var colName = "colA";
31+
command.CommandText = $"SELECT {arraySFString} AS {colName}";
32+
33+
// act
34+
using (var reader = command.ExecuteReader())
35+
{
36+
var dt = new DataTable();
37+
dt.Load(reader);
38+
39+
// assert
40+
Assert.AreEqual($"{expectedValueA},{expectedValueB},{expectedValueC}", DataTableParser.GetFirstRowValue(dt, colName));
41+
}
42+
}
43+
}
44+
}
45+
1546
[Test]
1647
public void TestSelectArray()
1748
{

Snowflake.Data.Tests/IntegrationTests/StructuredMapsIT.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Data;
34
using NUnit.Framework;
45
using Snowflake.Data.Client;
56
using Snowflake.Data.Core;
@@ -11,6 +12,35 @@ namespace Snowflake.Data.Tests.IntegrationTests
1112
[TestFixture]
1213
public class StructuredMapsIT: StructuredTypesIT
1314
{
15+
[Test]
16+
public void TestDataTableLoadOnStructuredMap()
17+
{
18+
// arrange
19+
using (var connection = new SnowflakeDbConnection(ConnectionString))
20+
{
21+
connection.Open();
22+
using (var command = connection.CreateCommand())
23+
{
24+
EnableStructuredTypes(connection);
25+
var key = "city";
26+
var value = "San Mateo";
27+
var addressAsSFString = $"OBJECT_CONSTRUCT('{key}','{value}')::MAP(VARCHAR, VARCHAR)";
28+
var colName = "colA";
29+
command.CommandText = $"SELECT {addressAsSFString} AS {colName}";
30+
31+
// act
32+
using (var reader = command.ExecuteReader())
33+
{
34+
var dt = new DataTable();
35+
dt.Load(reader);
36+
37+
// assert
38+
Assert.AreEqual($"{key}:{value}", DataTableParser.GetFirstRowValue(dt, colName));
39+
}
40+
}
41+
}
42+
}
43+
1444
[Test]
1545
public void TestSelectMap()
1646
{

Snowflake.Data.Tests/IntegrationTests/StructuredObjectsIT.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Data;
23
using NUnit.Framework;
34
using Snowflake.Data.Client;
45
using Snowflake.Data.Core;
@@ -10,6 +11,35 @@ namespace Snowflake.Data.Tests.IntegrationTests
1011
[TestFixture]
1112
public class StructuredObjectIT: StructuredTypesIT
1213
{
14+
[Test]
15+
public void TestDataTableLoadOnStructuredObject()
16+
{
17+
// arrange
18+
using (var connection = new SnowflakeDbConnection(ConnectionString))
19+
{
20+
connection.Open();
21+
using (var command = connection.CreateCommand())
22+
{
23+
EnableStructuredTypes(connection);
24+
var key = "city";
25+
var value = "San Mateo";
26+
var addressAsSFString = $"OBJECT_CONSTRUCT('{key}','{value}')::OBJECT(city VARCHAR)";
27+
var colName = "colA";
28+
command.CommandText = $"SELECT {addressAsSFString} AS {colName}";
29+
30+
// act
31+
using (var reader = command.ExecuteReader())
32+
{
33+
var dt = new DataTable();
34+
dt.Load(reader);
35+
36+
// assert
37+
Assert.AreEqual($"{key}:{value}", DataTableParser.GetFirstRowValue(dt, colName));
38+
}
39+
}
40+
}
41+
}
42+
1343
[Test]
1444
public void TestSelectStructuredTypeObject()
1545
{
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Newtonsoft.Json.Linq;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using System.Linq;
5+
6+
namespace Snowflake.Data.Tests.Util
7+
{
8+
public static class DataTableParser
9+
{
10+
internal static string GetFirstRowValue(DataTable dt, string colName)
11+
{
12+
var jsonString = dt.Rows[0][colName].ToString();
13+
var token = JToken.Parse(jsonString);
14+
15+
if (token.Type == JTokenType.Object)
16+
{
17+
return ParseKeyValue(token);
18+
}
19+
else if (token.Type == JTokenType.Array)
20+
{
21+
var element = token.First();
22+
if (element.Type == JTokenType.Object)
23+
{
24+
return ParseKeyValue(element);
25+
}
26+
else
27+
{
28+
var list = token.ToObject<List<string>>();
29+
return string.Join(",", list);
30+
}
31+
}
32+
33+
return jsonString;
34+
}
35+
36+
private static string ParseKeyValue(JToken token)
37+
{
38+
var keyValue = token.ToObject<Dictionary<string, string>>();
39+
var key = keyValue.Keys.First();
40+
var value = keyValue[key];
41+
return $"{key}:{value}";
42+
}
43+
}
44+
}

Snowflake.Data/Client/SnowflakeDbDataReader.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public class SnowflakeDbDataReader : DbDataReader
2828

2929
internal ResultFormat ResultFormat => resultSet.ResultFormat;
3030

31+
private const int MaxStringLength = 16777216; // Default maximum allowed length for VARCHAR
32+
3133
internal SnowflakeDbDataReader(SnowflakeDbCommand command, SFBaseResultSet resultSet)
3234
{
3335
this.dbCommand = command;
@@ -118,7 +120,7 @@ private DataTable PopulateSchemaTable(SFBaseResultSet resultSet)
118120

119121
row[SchemaTableColumn.ColumnName] = rowType.name;
120122
row[SchemaTableColumn.ColumnOrdinal] = columnOrdinal;
121-
row[SchemaTableColumn.ColumnSize] = (int)rowType.length;
123+
row[SchemaTableColumn.ColumnSize] = IsStructuredOrSemiStructuredType(rowType.type) && rowType.length == 0 ? MaxStringLength : (int)rowType.length;
122124
row[SchemaTableColumn.NumericPrecision] = (int)rowType.precision;
123125
row[SchemaTableColumn.NumericScale] = (int)rowType.scale;
124126
row[SchemaTableColumn.AllowDBNull] = rowType.nullable;
@@ -365,5 +367,11 @@ public override void Close()
365367
resultSet.close();
366368
isClosed = true;
367369
}
370+
371+
private bool IsStructuredOrSemiStructuredType(string type)
372+
{
373+
type = type.ToLower();
374+
return type == "array" || type == "object" || type == "variant"|| type == "map";
375+
}
368376
}
369377
}

0 commit comments

Comments
 (0)