Skip to content

Commit

Permalink
Merge pull request #121 from snowflakedb/Dispose
Browse files Browse the repository at this point in the history
Implement Dispose for Connection and Transaction.
  • Loading branch information
howryu authored Apr 5, 2019
2 parents cfbd9af + 8f57667 commit 9a2737c
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 1 deletion.
37 changes: 37 additions & 0 deletions Snowflake.Data.Tests/SFConnectionIT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,42 @@ public void TestConnectWithDifferentRole()
conn.Close();
}
}

// Test that when a connection is disposed, a close would send out and unfinished transaction would be roll back.
[Test]
public void TestConnectionDispose()
{
using (IDbConnection conn = new SnowflakeDbConnection())
{
// Setup
conn.ConnectionString = connectionString;
conn.Open();
IDbCommand command = conn.CreateCommand();
command.CommandText = "create or replace table testConnDispose(c int)";
command.ExecuteNonQuery();

IDbTransaction t1 = conn.BeginTransaction();
IDbCommand t1c1 = conn.CreateCommand();
t1c1.Transaction = t1;
t1c1.CommandText = "insert into testConnDispose values (1)";
t1c1.ExecuteNonQuery();
}

using (IDbConnection conn = new SnowflakeDbConnection())
{
// Previous connection would be disposed and
// uncommitted txn would rollback at this point
conn.ConnectionString = connectionString;
conn.Open();
IDbCommand command = conn.CreateCommand();
command.CommandText = "SELECT * FROM testConnDispose";
IDataReader reader = command.ExecuteReader();
Assert.IsFalse(reader.Read());

// Cleanup
command.CommandText = "DROP TABLE IF EXISTS testConnDispose";
command.ExecuteNonQuery();
}
}
}
}
51 changes: 51 additions & 0 deletions Snowflake.Data.Tests/SFDbTransactionIT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2012-2019 Snowflake Computing Inc. All rights reserved.
*/

namespace Snowflake.Data.Tests
{
using NUnit.Framework;
using Snowflake.Data.Client;
using System.Data;

[TestFixture]
class SFDbTransactionIT : SFBaseTest
{
[Test]
// Test that when a transaction is disposed, rollback would be sent out
public void TestTransactionDispose()
{
var conn = new SnowflakeDbConnection();
try
{
conn.ConnectionString = connectionString;
conn.Open();

IDbCommand command = conn.CreateCommand();
command.CommandText = "create or replace table testTransactionDispose(c int)";
command.ExecuteNonQuery();

using (IDbTransaction t1 = conn.BeginTransaction())
{
IDbCommand t1c1 = conn.CreateCommand();
t1c1.Transaction = t1;
t1c1.CommandText = "insert into testTransactionDispose values (1)";
t1c1.ExecuteNonQuery();
}

// Transaction t1 would be disposed and rollback at this point, tuple inserted is not visible
IDbCommand c2 = conn.CreateCommand();
c2.CommandText = "SELECT * FROM testTransactionDispose";
IDataReader reader2 = c2.ExecuteReader();
Assert.IsFalse(reader2.Read());
}
finally
{
IDbCommand command = conn.CreateCommand();
command.CommandText = "DROP TABLE IF EXISTS testTransactionDispose";
command.ExecuteNonQuery();
conn.Close();
}
}
}
}
34 changes: 33 additions & 1 deletion Snowflake.Data/Client/SnowflakeDbConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class SnowflakeDbConnection : DbConnection

private int _connectionTimeout;

private bool disposed = false;

public SnowflakeDbConnection()
{
_connectionState = ConnectionState.Closed;
Expand All @@ -43,6 +45,11 @@ public SecureString Password
get; set;
}

public bool IsOpen()
{
return _connectionState == ConnectionState.Open;
}

public override string Database => _connectionState == ConnectionState.Open ? SfSession.database : string.Empty;

public override int ConnectionTimeout => this._connectionTimeout;
Expand Down Expand Up @@ -98,7 +105,16 @@ public override void Open()
{
logger.Debug("Open Connection.");
SetSession();
SfSession.Open();
try
{
SfSession.Open();
}
catch (Exception e)
{
// Otherwise when Dispose() is called, the close request would timeout.
_connectionState = ConnectionState.Closed;
throw e;
}
OnSessionEstablished();
}

Expand Down Expand Up @@ -140,5 +156,21 @@ protected override DbCommand CreateDbCommand()
{
return new SnowflakeDbCommand(this);
}

protected override void Dispose(bool disposing)
{
if (disposed)
return;

this.Close();
disposed = true;

base.Dispose(disposing);
}

~SnowflakeDbConnection()
{
Dispose(false);
}
}
}
23 changes: 23 additions & 0 deletions Snowflake.Data/Client/SnowflakeDbTransaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class SnowflakeDbTransaction : DbTransaction

private SnowflakeDbConnection connection;

private bool disposed = false;

public SnowflakeDbTransaction(IsolationLevel isolationLevel, SnowflakeDbConnection connection)
{
logger.Debug("Begin transaction.");
Expand Down Expand Up @@ -71,5 +73,26 @@ public override void Rollback()
command.ExecuteNonQuery();
}
}

protected override void Dispose(bool disposing)
{
if (disposed)
return;

// Rollback the uncommitted transaction when the connection is open
if (connection != null && connection.IsOpen())
{
// When there is no uncommitted transaction, Snowflake would just ignore the rollback request;
this.Rollback();
}
disposed = true;

base.Dispose(disposing);
}

~SnowflakeDbTransaction()
{
Dispose(false);
}
}
}

0 comments on commit 9a2737c

Please sign in to comment.