* OdbcColumn.cs: Treat OdbcType.Numeric as decimal.
* OdbcConnection.cs: Keep (weak) list of commands to allow us to
dispose them (and free their corresponding statement handles) when
the connection is closed. Fixed bug #341743.
* OdbcCommand.cs: Add internal Unlink method that is invoked when the
OdbcConnection is disposed, and which frees the statement handle.
Modified ExecuteNonQuery helper to take CommandBehavior and rename
freeHandle argument to createReader. Added CommandBehavior argument
to ExecSQL. Added FreeStatement overload that takes an option to not
perform an unlink. Fixes bug #
3417434.
svn path=/trunk/mcs/; revision=122226
+2008-12-30 Gert Driesen <drieseng@users.sourceforge.net>
+
+ * OdbcColumn.cs: Treat OdbcType.Numeric as decimal.
+ * OdbcConnection.cs: Keep (weak) list of commands to allow us to
+ dispose them (and free their corresponding statement handles) when
+ the connection is closed. Fixed bug #341743.
+ * OdbcCommand.cs: Add internal Unlink method that is invoked when the
+ OdbcConnection is disposed, and which frees the statement handle.
+ Modified ExecuteNonQuery helper to take CommandBehavior and rename
+ freeHandle argument to createReader. Added CommandBehavior argument
+ to ExecSQL. Added FreeStatement overload that takes an option to not
+ perform an unlink. Fixes bug #3417434.
+
2008-12-30 Gert Driesen <drieseng@users.sourceforge.net>
* libodbc.cs: Add IdentifierQuoteChar to OdbcInfo.
case OdbcType.SmallDateTime:
return typeof (DateTime);
case OdbcType.Decimal:
- return typeof (Decimal);
case OdbcType.Numeric:
+ return typeof (Decimal);
case OdbcType.Double:
return typeof (Double);
case OdbcType.Int:
internal IntPtr hStmt {
get { return hstmt; }
}
-
[OdbcCategory ("Data")]
[DefaultValue ("")]
return commandText;
}
set {
+#if NET_2_0
prepared = false;
+#endif
commandText = value;
}
}
}
}
-
[OdbcCategory ("Data")]
[OdbcDescriptionAttribute ("The parameters collection")]
[DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
}
}
}
- #else
+#else
protected override DbTransaction DbTransaction {
get { return transaction; }
set { transaction = (OdbcTransaction) value; }
return new OdbcParameter ();
}
+ internal void Unlink ()
+ {
+ if (disposed)
+ return;
+
+ FreeStatement (false);
+ }
+
protected override void Dispose (bool disposing)
{
if (disposed)
return;
-
+
FreeStatement (); // free handles
Connection = null;
Transaction = null;
if (hstmt != IntPtr.Zero)
FreeStatement ();
+ else
+ Connection.Link (this);
ret = libodbc.SQLAllocHandle (OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
return hstmt;
}
- private void FreeStatement ()
+ void FreeStatement ()
+ {
+ FreeStatement (true);
+ }
+
+ private void FreeStatement (bool unlink)
{
+ prepared = false;
+
if (hstmt == IntPtr.Zero)
return;
-
+
+ if (unlink)
+ Connection.Unlink (this);
+
// free previously allocated handle.
OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
hstmt = IntPtr.Zero;
}
- private void ExecSQL (string sql)
+ private void ExecSQL (CommandBehavior behavior, bool createReader, string sql)
{
OdbcReturn ret;
- if (! prepared && Parameters.Count <= 0) {
+ if (!prepared && Parameters.Count == 0) {
ReAllocStatment ();
-
+
ret = libodbc.SQLExecDirect (hstmt, sql, libodbc.SQL_NTS);
- if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo) &&
- (ret != OdbcReturn.NoData))
+ if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo) && (ret != OdbcReturn.NoData))
throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
return;
}
#endif // NET_2_0
int ExecuteNonQuery ()
{
- return ExecuteNonQuery (true);
+ return ExecuteNonQuery (CommandBehavior.Default, false);
}
- private int ExecuteNonQuery (bool freeHandle)
+ private int ExecuteNonQuery (CommandBehavior behavior, bool createReader)
{
int records = 0;
if (Connection == null)
if (Connection.State == ConnectionState.Closed)
throw new InvalidOperationException ("Connection state is closed");
// FIXME: a third check is mentioned in .NET docs
-
- ExecSQL(CommandText);
+ ExecSQL (behavior, createReader, CommandText);
// .NET documentation says that except for INSERT, UPDATE and
// DELETE where the return value is the number of rows affected
} else
records = -1;
- if (freeHandle && !prepared)
+ if (!createReader && !prepared)
FreeStatement ();
return records;
#endif // NET_2_0
OdbcDataReader ExecuteReader (CommandBehavior behavior)
{
- int recordsAffected = ExecuteNonQuery(false);
+ int recordsAffected = ExecuteNonQuery(behavior, true);
OdbcDataReader dataReader = new OdbcDataReader (this, behavior, recordsAffected);
return dataReader;
}
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+
+using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
IntPtr henv = IntPtr.Zero;
IntPtr hdbc = IntPtr.Zero;
bool disposed;
+ ArrayList linkedCommands;
#endregion
{
OdbcReturn ret = OdbcReturn.Error;
if (State == ConnectionState.Open) {
+ // close any associated commands
+ if (linkedCommands != null) {
+ for (int i = 0; i < linkedCommands.Count; i++) {
+ WeakReference wr = (WeakReference) linkedCommands [i];
+ if (wr == null)
+ continue;
+ OdbcCommand c = (OdbcCommand) wr.Target;
+ if (c != null)
+ c.Unlink ();
+ }
+ linkedCommands = null;
+ }
+
// disconnect
ret = libodbc.SQLDisconnect (hdbc);
if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
return value.TrimEnd ('\0');
}
+ internal void Link (OdbcCommand cmd)
+ {
+ if (linkedCommands == null)
+ linkedCommands = new ArrayList ();
+ linkedCommands.Add (new WeakReference (cmd));
+ }
+
+ internal void Unlink (OdbcCommand cmd)
+ {
+ if (linkedCommands == null)
+ return;
+
+ for (int i = 0; i < linkedCommands.Count; i++) {
+ WeakReference wr = (WeakReference) linkedCommands [i];
+ if (wr == null)
+ continue;
+ OdbcCommand c = (OdbcCommand) wr.Target;
+ if (c == cmd) {
+ linkedCommands [i] = null;
+ break;
+ }
+ }
+ }
+
#endregion
#region Events and Delegates
+2008-12-30 Gert Driesen <drieseng@users.sourceforge.net>
+
+ * OdbcCommandTest.cs: Added test for bug #341743.
+
2008-07-28 Gert Driesen <drieseng@users.sourceforge.net>
* OdbcParameterTest.cs: Fixed compilation on 1.0 profile.
}
}
+ [Test]
+ public void bug341743 ()
+ {
+ OdbcConnection conn = (OdbcConnection) ConnectionManager.Singleton.Connection;
+ ConnectionManager.Singleton.OpenConnection ();
+
+ OdbcCommand cmd = null;
+ try {
+ cmd = conn.CreateCommand ();
+ cmd.CommandText = "SELECT 'a'";
+ cmd.ExecuteNonQuery ();
+
+ conn.Dispose ();
+
+ Assert.AreSame (conn, cmd.Connection, "#1");
+ cmd.Dispose ();
+ Assert.IsNull (cmd.Connection, "#2");
+ } finally {
+ if (cmd != null)
+ cmd.Dispose ();
+ ConnectionManager.Singleton.CloseConnection ();
+ }
+ }
+
/// <summary>
/// Test ExecuteNonQuery
/// </summary>