diff --git a/README.md b/README.md
index da4ea2ad7..6bb3ad8bc 100644
--- a/README.md
+++ b/README.md
@@ -167,7 +167,7 @@ The following table lists all valid connection properties:
| FILE_TRANSFER_MEMORY_THRESHOLD | No | The maximum number of bytes to store in memory used in order to provide a file encryption. If encrypting/decrypting file size exceeds provided value a temporary file will be created and the work will be continued in the temporary file instead of memory. If no value provided 1MB will be used as a default value (that is 1048576 bytes). It is possible to configure any integer value bigger than zero representing maximal number of bytes to reside in memory. |
| CLIENT_CONFIG_FILE | No | The location of the client configuration json file. In this file you can configure easy logging feature. |
| ALLOWUNDERSCORESINHOST | No | Specifies whether to allow underscores in account names. This impacts PrivateLink customers whose account names contain underscores. In this situation, you must override the default value by setting allowUnderscoresInHost to true. |
-| QUERY_TAG | No | Optional string that can be used to tag queries and other SQL statements executed within a connection. The tags are displayed in the output of the QUERY_HISTORY , QUERY_HISTORY_BY_* functions. |
+| QUERY_TAG | No | Optional string that can be used to tag queries and other SQL statements executed within a connection. The tags are displayed in the output of the QUERY_HISTORY , QUERY_HISTORY_BY_* functions. To set QUERY_TAG on the statement level you can use SnowflakeDbCommand.QueryTag. |
| MAXPOOLSIZE | No | Maximum number of connections in a pool. Default value is 10. `maxPoolSize` value cannot be lower than `minPoolSize` value. |
| MINPOOLSIZE | No | Expected minimum number of connections in pool. When you get a connection from the pool, more connections might be initialised in background to increase the pool size to `minPoolSize`. If you specify 0 or 1 there will be no attempts to create extra initialisations in background. The default value is 2. `maxPoolSize` value cannot be lower than `minPoolSize` value. The parameter is used only in a new version of connection pool. |
| CHANGEDSESSION | No | Specifies what should happen with a closed connection when some of its session variables are altered (e. g. you used `ALTER SESSION SET SCHEMA` to change the databese schema). The default behaviour is `OriginalPool` which means the session stays in the original pool. Currently no other option is possible. Parameter used only in a new version of connection pool. |
@@ -240,6 +240,25 @@ The following examples show how you can include different types of special chara
Note that previously you needed to use a double equal sign (==) to escape the character. However, beginning with version 2.0.18, you can use a single equal size.
+
+Snowflake supports using [double quote identifiers](https://docs.snowflake.com/en/sql-reference/identifiers-syntax#double-quoted-identifiers) for object property values (WAREHOUSE, DATABASE, SCHEMA AND ROLES). The value should be delimited with `\"` in the connection string. The value is case-sensitive and allow to use special characters as part of the value.
+
+ ```cs
+ string connectionString = String.Format(
+ "account=testaccount; " +
+ "database=\"testDB\";"
+ );
+ ```
+ - To include a `"` character as part of the value should be escaped using `\"\"`.
+
+ ```cs
+ string connectionString = String.Format(
+ "account=testaccount; " +
+ "database=\"\"\"test\"\"user\"\"\";" // DATABASE => ""test"db""
+ );
+ ```
+
+
### Other Authentication Methods
If you are using a different method for authentication, see the examples below:
diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs
index 7568473d7..04a6c62e5 100755
--- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs
+++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs
@@ -1621,5 +1621,25 @@ public void TestGetResultsOfUnknownQueryIdWithConfiguredRetry()
conn.Close();
}
}
+
+ [Test]
+ public void TestSetQueryTagOverridesConnectionString()
+ {
+ using (var conn = new SnowflakeDbConnection())
+ {
+ string expectedQueryTag = "Test QUERY_TAG 12345";
+ string connectQueryTag = "Test 123";
+ conn.ConnectionString = ConnectionString + $";query_tag={connectQueryTag}";
+
+ conn.Open();
+ var command = conn.CreateCommand();
+ ((SnowflakeDbCommand)command).QueryTag = expectedQueryTag;
+ // This query itself will be part of the history and will have the query tag
+ command.CommandText = "SELECT QUERY_TAG FROM table(information_schema.query_history_by_session())";
+ var queryTag = command.ExecuteScalar();
+
+ Assert.AreEqual(expectedQueryTag, queryTag);
+ }
+ }
}
}
diff --git a/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs b/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs
index 4eb8ff324..a0e3e8496 100644
--- a/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs
+++ b/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs
@@ -121,6 +121,24 @@ public void TestValidateSupportEscapedQuotesValuesForObjectProperties(string pro
Assert.AreEqual(value, properties[sessionProperty]);
}
+ [Test]
+ [TestCase("DB", SFSessionProperty.DB, "testdb", "testdb")]
+ [TestCase("DB", SFSessionProperty.DB, "\"testdb\"", "\"testdb\"")]
+ [TestCase("DB", SFSessionProperty.DB, "\"\"\"testDB\"\"\"", "\"\"testDB\"\"")]
+ [TestCase("DB", SFSessionProperty.DB, "\"\"\"test\"\"DB\"\"\"", "\"\"test\"DB\"\"")]
+ [TestCase("SCHEMA", SFSessionProperty.SCHEMA, "\"quoted\"\"Schema\"", "\"quoted\"Schema\"")]
+ public void TestValidateSupportEscapedQuotesInsideValuesForObjectProperties(string propertyName, SFSessionProperty sessionProperty, string value, string expectedValue)
+ {
+ // arrange
+ var connectionString = $"ACCOUNT=test;{propertyName}={value};USER=test;PASSWORD=test;";
+
+ // act
+ var properties = SFSessionProperties.ParseConnectionString(connectionString, null);
+
+ // assert
+ Assert.AreEqual(expectedValue, properties[sessionProperty]);
+ }
+
public static IEnumerable ConnectionStringTestCases()
{
string defAccount = "testaccount";
@@ -598,7 +616,6 @@ public static IEnumerable ConnectionStringTestCases()
private static string DefaultValue(SFSessionProperty property) =>
property.GetAttribute().defaultValue;
-
internal class TestCase
{
public string ConnectionString { get; set; }
diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs
index 36a04f151..15d8e0870 100755
--- a/Snowflake.Data/Client/SnowflakeDbCommand.cs
+++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs
@@ -54,6 +54,11 @@ public override int CommandTimeout
get; set;
}
+ public string QueryTag
+ {
+ get; set;
+ }
+
public override CommandType CommandType
{
get
@@ -132,7 +137,7 @@ protected override DbConnection DbConnection
connection = sfc;
if (sfc.SfSession != null)
{
- sfStatement = new SFStatement(sfc.SfSession);
+ sfStatement = new SFStatement(sfc.SfSession, QueryTag);
}
}
}
@@ -143,7 +148,7 @@ protected override DbParameterCollection DbParameterCollection
{
return this.parameterCollection;
}
- }
+ }
protected override DbTransaction DbTransaction
{
@@ -373,15 +378,15 @@ private static Dictionary convertToBindList(List vals = new List