// Copyright (C) Tim Coleman, 2002
//
-using Mono.Data.TdsClient.Internal;
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// 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 Mono.Data.Tds;
+using Mono.Data.Tds.Protocol;
using System;
+using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Runtime.InteropServices;
using System.Text;
-using System.Xml;
namespace Mono.Data.SybaseClient {
public sealed class SybaseCommand : Component, IDbCommand, ICloneable
{
#region Fields
+ bool disposed = false;
int commandTimeout;
bool designTimeVisible;
string commandText;
-
CommandType commandType;
SybaseConnection connection;
SybaseTransaction transaction;
-
- SybaseParameterCollection parameters = new SybaseParameterCollection ();
- private CommandBehavior behavior = CommandBehavior.Default;
-
- NameValueCollection procedureCache = new NameValueCollection ();
+ UpdateRowSource updatedRowSource;
+ CommandBehavior behavior = CommandBehavior.Default;
+ SybaseParameterCollection parameters;
+ string preparedStatement = null;
#endregion // Fields
this.connection = connection;
this.transaction = transaction;
this.commandType = CommandType.Text;
+ this.updatedRowSource = UpdateRowSource.Both;
+
this.designTimeVisible = false;
this.commandTimeout = 30;
+ parameters = new SybaseParameterCollection (this);
}
#endregion // Constructors
}
public string CommandText {
- get { return CommandText; }
- set { commandText = value; }
+ get { return commandText; }
+ set {
+ if (value != commandText && preparedStatement != null)
+ Unprepare ();
+ commandText = value;
+ }
}
public int CommandTimeout {
public CommandType CommandType {
get { return commandType; }
- [MonoTODO ("Validate")]
- set { commandType = value; }
+ set {
+ if (value == CommandType.TableDirect)
+ throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono SybaseClient Data Provider.");
+ commandType = value;
+ }
}
public SybaseConnection Connection {
}
internal ITds Tds {
- get { return connection.Tds; }
+ get { return Connection.Tds; }
}
IDbConnection IDbCommand.Connection {
set { transaction = value; }
}
- [MonoTODO]
public UpdateRowSource UpdatedRowSource {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
+ get { return updatedRowSource; }
+ set { updatedRowSource = value; }
}
#endregion // Fields
public void Cancel ()
{
- if (connection == null || connection.Tds == null)
+ if (Connection == null || Connection.Tds == null)
return;
- connection.Tds.Cancel ();
- connection.CheckForErrors ();
+ Connection.Tds.Cancel ();
}
internal void CloseDataReader (bool moreResults)
{
- while (moreResults)
- moreResults = connection.Tds.NextResult ();
+ GetOutputParameters ();
+ Connection.DataReader = null;
- if (connection.Tds.OutputParameters.Count > 0) {
- int index = 0;
- foreach (SybaseParameter parameter in parameters) {
- if (parameter.Direction != ParameterDirection.Input)
- parameter.Value = connection.Tds.OutputParameters[index];
- index += 1;
- if (index >= connection.Tds.OutputParameters.Count)
- break;
- }
- }
- connection.DataReaderOpen = false;
if ((behavior & CommandBehavior.CloseConnection) != 0)
- connection.Close ();
+ Connection.Close ();
}
public SybaseParameter CreateParameter ()
return new SybaseParameter ();
}
+ internal void DeriveParameters ()
+ {
+ if (commandType != CommandType.StoredProcedure)
+ throw new InvalidOperationException (String.Format ("SybaseCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
+ ValidateCommand ("DeriveParameters");
+
+ SybaseParameterCollection localParameters = new SybaseParameterCollection (this);
+ localParameters.Add ("@P1", SybaseType.NVarChar, commandText.Length).Value = commandText;
+
+ string sql = "sp_procedure_params_rowset";
+
+ Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
+
+ SybaseDataReader reader = new SybaseDataReader (this);
+ parameters.Clear ();
+ object[] dbValues = new object[reader.FieldCount];
+
+ while (reader.Read ()) {
+ reader.GetValues (dbValues);
+ parameters.Add (new SybaseParameter (dbValues));
+ }
+ reader.Close ();
+ }
+
+ private void Execute (CommandBehavior behavior, bool wantResults)
+ {
+ TdsMetaParameterCollection parms = Parameters.MetaParameters;
+ if (preparedStatement == null) {
+ bool schemaOnly = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
+ bool keyInfo = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
+
+ StringBuilder sql1 = new StringBuilder ();
+ StringBuilder sql2 = new StringBuilder ();
+
+ if (schemaOnly || keyInfo)
+ sql1.Append ("SET FMTONLY OFF;");
+ if (keyInfo) {
+ sql1.Append ("SET NO_BROWSETABLE ON;");
+ sql2.Append ("SET NO_BROWSETABLE OFF;");
+ }
+ if (schemaOnly) {
+ sql1.Append ("SET FMTONLY ON;");
+ sql2.Append ("SET FMTONLY OFF;");
+ }
+
+ switch (CommandType) {
+ case CommandType.StoredProcedure:
+ if (keyInfo || schemaOnly)
+ Connection.Tds.Execute (sql1.ToString ());
+ Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
+ if (keyInfo || schemaOnly)
+ Connection.Tds.Execute (sql2.ToString ());
+ break;
+ case CommandType.Text:
+ string sql = String.Format ("{0}{1}{2}", sql1.ToString (), CommandText, sql2.ToString ());
+ Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
+ break;
+ }
+ }
+ else
+ Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
+ }
+
public int ExecuteNonQuery ()
{
- int result = connection.Tds.ExecuteNonQuery (ValidateQuery ("ExecuteNonQuery"));
- connection.CheckForErrors ();
+ ValidateCommand ("ExecuteNonQuery");
+ int result = 0;
+
+ try {
+ Execute (CommandBehavior.Default, false);
+ }
+ catch (TdsTimeoutException e) {
+ throw SybaseException.FromTdsInternalException ((TdsInternalException) e);
+ }
+
+ GetOutputParameters ();
return result;
}
public SybaseDataReader ExecuteReader (CommandBehavior behavior)
{
- this.behavior = behavior;
- connection.Tds.ExecuteQuery (ValidateQuery ("ExecuteReader"));
- connection.CheckForErrors ();
- connection.DataReaderOpen = true;
- return new SybaseDataReader (this);
+ ValidateCommand ("ExecuteReader");
+ try {
+ Execute (behavior, true);
+ }
+ catch (TdsTimeoutException e) {
+ throw SybaseException.FromTdsInternalException ((TdsInternalException) e);
+ }
+ Connection.DataReader = new SybaseDataReader (this);
+ return Connection.DataReader;
}
public object ExecuteScalar ()
{
- connection.Tds.ExecuteQuery (ValidateQuery ("ExecuteScalar"));
-
- bool moreResults = connection.Tds.NextResult ();
- connection.CheckForErrors ();
-
- if (!moreResults)
- return null;
-
- moreResults = connection.Tds.NextRow ();
- connection.CheckForErrors ();
+ ValidateCommand ("ExecuteScalar");
+ try {
+ Execute (CommandBehavior.Default, true);
+ }
+ catch (TdsTimeoutException e) {
+ throw SybaseException.FromTdsInternalException ((TdsInternalException) e);
+ }
- if (!moreResults)
+ if (!Connection.Tds.NextResult () || !Connection.Tds.NextRow ())
return null;
- object result = connection.Tds.ColumnValues[0];
+ object result = Connection.Tds.ColumnValues [0];
CloseDataReader (true);
return result;
}
- static string FormatParameter (SybaseParameter parameter)
+ private void GetOutputParameters ()
{
- if (parameter.Value == null)
- return "NULL";
-
- switch (parameter.SybaseType) {
- case SybaseType.BigInt :
- case SybaseType.Bit :
- case SybaseType.Decimal :
- case SybaseType.Float :
- case SybaseType.Int :
- case SybaseType.Money :
- case SybaseType.Real :
- case SybaseType.SmallInt :
- case SybaseType.SmallMoney :
- case SybaseType.TinyInt :
- return parameter.Value.ToString ();
- default:
- return String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
- }
- }
+ Connection.Tds.SkipToEnd ();
- static string FormatQuery (string commandText, CommandType commandType, SybaseParameterCollection parameters)
- {
- StringBuilder result = new StringBuilder ();
-
- switch (commandType) {
- case CommandType.Text :
- return commandText;
- case CommandType.TableDirect :
- return String.Format ("SELECT * FROM {0}", commandText);
- case CommandType.StoredProcedure :
-
- StringBuilder parms = new StringBuilder ();
- StringBuilder declarations = new StringBuilder ();
+ IList list = Connection.Tds.ColumnValues;
+ if (list != null && list.Count > 0) {
+ int index = 0;
foreach (SybaseParameter parameter in parameters) {
- switch (parameter.Direction) {
- case ParameterDirection.Input :
- if (parms.Length > 0)
- result.Append (",");
- parms.Append (FormatParameter (parameter));
- break;
- case ParameterDirection.Output :
- if (parms.Length > 0)
- parms.Append (",");
- parms.Append (parameter.ParameterName);
- parms.Append (" OUT");
-
- if (declarations.Length == 0)
- declarations.Append ("DECLARE ");
- else
- declarations.Append (",");
-
- declarations.Append (parameter.Prepare ());
- break;
- default :
- throw new NotImplementedException ("Only support input and output parameters.");
+ if (parameter.Direction != ParameterDirection.Input) {
+ parameter.Value = list [index];
+ index += 1;
}
+ if (index >= list.Count)
+ break;
}
- result.Append (declarations.ToString ());
- result.Append (" EXEC ");
- result.Append (commandText);
- result.Append (" ");
- result.Append (parms);
- return result.ToString ();
- default:
- throw new InvalidOperationException ("The CommandType was not recognized.");
}
}
- [MonoTODO]
object ICloneable.Clone ()
{
- throw new NotImplementedException ();
+ return new SybaseCommand (commandText, Connection);
}
IDbDataParameter IDbCommand.CreateParameter ()
return ExecuteReader (behavior);
}
- void IDisposable.Dispose ()
- {
- Dispose (true);
- }
-
public void Prepare ()
{
- bool prependComma = false;
- Guid uniqueId = Guid.NewGuid ();
- string procedureName = String.Format ("#mono#{0}", uniqueId.ToString ("N"));
- StringBuilder procedureString = new StringBuilder ();
-
- procedureString.Append ("CREATE PROC ");
- procedureString.Append (procedureName);
- procedureString.Append (" (");
-
- foreach (SybaseParameter parameter in parameters) {
- if (prependComma)
- procedureString.Append (", ");
- else
- prependComma = true;
- procedureString.Append (parameter.Prepare ());
- if (parameter.Direction == ParameterDirection.Output)
- procedureString.Append (" OUT");
- }
-
- procedureString.Append (") AS ");
- procedureString.Append (commandText);
- string cmdText = FormatQuery (procedureName, CommandType.StoredProcedure, parameters);
- connection.Tds.ExecuteNonQuery (procedureString.ToString ());
- procedureCache[commandText] = cmdText;
+ ValidateCommand ("Prepare");
+ if (CommandType == CommandType.Text)
+ preparedStatement = Connection.Tds.Prepare (CommandText, Parameters.MetaParameters);
}
public void ResetCommandTimeout ()
commandTimeout = 30;
}
- string ValidateQuery (string methodName)
+ private void Unprepare ()
{
- if (connection == null)
- throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", methodName));
- if (connection.Transaction != null && transaction != connection.Transaction)
+ Connection.Tds.Unprepare (preparedStatement);
+ preparedStatement = null;
+ }
+
+ private void ValidateCommand (string method)
+ {
+ if (Connection == null)
+ throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
+ if (Connection.Transaction != null && transaction != Connection.Transaction)
throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
- if (connection.State != ConnectionState.Open)
- throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", methodName));
+ if (Connection.State != ConnectionState.Open)
+ throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
if (commandText == String.Empty || commandText == null)
throw new InvalidOperationException ("The command text for this Command has not been set.");
-
- string sql = procedureCache[commandText];
- if (sql == null)
- sql = FormatQuery (commandText, commandType, parameters);
-
- if ((behavior & CommandBehavior.KeyInfo) != 0)
- sql += " FOR BROWSE";
-
- return sql;
+ if (Connection.DataReader != null)
+ throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
}
#endregion // Methods