2 // System.Data.SqlClient.SqlCommand.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Daniel Morgan (danmorg@sc.rr.com)
8 // (C) Ximian, Inc 2002 http://www.ximian.com/
9 // (C) Daniel Morgan, 2002
12 // SQL and concepts were used from libgda 0.8.190 (GNOME Data Access)
\r
13 // http://www.gnome-db.org/
\r
14 // with permission from the authors of the
\r
15 // PostgreSQL provider in libgda:
\r
16 // Michael Lausch <michael@lausch.at>
17 // Rodrigo Moya <rodrigo@gnome-db.org>
18 // Vivien Malerba <malerba@gnome-db.org>
19 // Gonzalo Paniagua Javier <gonzalo@gnome-db.org>
22 // use #define DEBUG_SqlCommand if you want to spew debug messages
23 // #define DEBUG_SqlCommand
26 using System.Collections;
27 using System.ComponentModel;
29 using System.Data.Common;
30 using System.Runtime.InteropServices;
34 namespace System.Data.SqlClient {
36 /// Represents a SQL statement that is executed
37 /// while connected to a SQL database.
39 // public sealed class SqlCommand : Component, IDbCommand, ICloneable
40 public sealed class SqlCommand : IDbCommand {
44 private string sql = "";
45 private int timeout = 30;
46 // default is 30 seconds
47 // for command execution
49 private SqlConnection conn = null;
50 private SqlTransaction trans = null;
51 private CommandType cmdType = CommandType.Text;
52 private bool designTime = false;
53 private SqlParameterCollection parmCollection = new
54 SqlParameterCollection();
56 // SqlDataReader state data for ExecuteReader()
57 private SqlDataReader dataReader = null;
58 private string[] queries = null;
59 private int currentQuery;
60 private CommandBehavior cmdBehavior = CommandBehavior.Default;
62 private ParmUtil parmUtil = null;
72 public SqlCommand (string cmdText) {
76 public SqlCommand (string cmdText, SqlConnection connection) {
81 public SqlCommand (string cmdText, SqlConnection connection,
82 SqlTransaction transaction) {
88 #endregion // Constructors
93 public void Cancel () {
94 // FIXME: use non-blocking Exec for this
95 throw new NotImplementedException ();
98 // FIXME: is this the correct way to return a stronger type?
100 IDbDataParameter IDbCommand.CreateParameter () {
101 return CreateParameter ();
105 public SqlParameter CreateParameter () {
106 return new SqlParameter ();
109 public int ExecuteNonQuery () {
110 IntPtr pgResult; // PGresult
111 int rowsAffected = -1;
112 ExecStatusType execStatus;
113 String rowsAffectedString;
116 if(conn.State != ConnectionState.Open)
117 throw new InvalidOperationException(
118 "ConnnectionState is not Open");
120 query = TweakQuery(sql, cmdType);
122 // FIXME: PQexec blocks
123 // while PQsendQuery is non-blocking
124 // which is better to use?
125 // int PQsendQuery(PGconn *conn,
126 // const char *query);
128 // execute SQL command
129 // uses internal property to get the PGConn IntPtr
130 pgResult = PostgresLibrary.
131 PQexec (conn.PostgresConnection, query);
133 execStatus = PostgresLibrary.
134 PQresultStatus (pgResult);
136 if(execStatus == ExecStatusType.PGRES_COMMAND_OK) {
137 rowsAffectedString = PostgresLibrary.
138 PQcmdTuples (pgResult);
140 if(rowsAffectedString != null)
141 if(rowsAffectedString.Equals("") == false)
142 rowsAffected = int.Parse(rowsAffectedString);
144 PostgresLibrary.PQclear (pgResult);
145 pgResult = IntPtr.Zero;
150 errorMessage = PostgresLibrary.
151 PQresStatus(execStatus);
153 errorMessage += " " + PostgresLibrary.
\r
154 PQresultErrorMessage(pgResult);
\r
156 PostgresLibrary.PQclear (pgResult);
157 pgResult = IntPtr.Zero;
159 throw new SqlException(0, 0,
161 conn.DataSource, "SqlCommand", 0);
\r
168 IDataReader IDbCommand.ExecuteReader () {
169 return ExecuteReader ();
173 public SqlDataReader ExecuteReader () {
174 return ExecuteReader(CommandBehavior.Default);
178 IDataReader IDbCommand.ExecuteReader (
179 CommandBehavior behavior) {
180 return ExecuteReader (behavior);
184 public SqlDataReader ExecuteReader (CommandBehavior behavior)
186 if(conn.State != ConnectionState.Open)
187 throw new InvalidOperationException(
188 "ConnectionState is not Open");
190 cmdBehavior = behavior;
194 dataReader = new SqlDataReader(this);
196 if((behavior & CommandBehavior.SingleResult) == CommandBehavior.SingleResult) {
197 queries = new String[1];
201 queries = sql.Split(new Char[] {';'});
204 dataReader.NextResult();
209 internal SqlResult NextResult()
211 SqlResult res = new SqlResult();
212 res.Connection = this.Connection;
217 if(currentQuery < queries.Length && queries[currentQuery].Equals("") == false) {
218 statement = TweakQuery(queries[currentQuery], cmdType);
219 ExecuteQuery(statement, res);
220 res.ResultReturned = true;
223 res.ResultReturned = false;
229 private string TweakQuery(string query, CommandType commandType) {
230 string statement = "";
234 Console.WriteLine("---------[][] TweakQuery() [][]--------");
235 Console.WriteLine("CommandType: " + commandType + " CommandBehavior: " + cmdBehavior);
236 Console.WriteLine("SQL before command type: " + query);
238 // finish building SQL based on CommandType
239 switch(commandType) {
240 case CommandType.Text:
243 case CommandType.StoredProcedure:
245 "SELECT " + query + "()";
247 case CommandType.TableDirect:
248 // NOTE: this is for the PostgreSQL provider
249 // and for OleDb, according to the docs,
250 // an exception is thrown if you try to use
251 // this with SqlCommand
252 string[] directTables = query.Split(
255 td = new StringBuilder("SELECT * FROM ");
257 for(int tab = 0; tab < directTables.Length; tab++) {
260 td.Append(directTables[tab]);
261 // FIXME: if multipe tables, how do we
262 // join? based on Primary/Foreign Keys?
263 // Otherwise, a Cartesian Product happens
265 statement = td.ToString();
268 // FIXME: throw an exception?
273 Console.WriteLine("SQL after command type: " + statement);
275 // TODO: this parameters utility
276 // currently only support input variables
277 // need todo output, input/output, and return.
279 Console.WriteLine("using ParmUtil in TweakQuery()...");
281 parmUtil = new ParmUtil(statement, parmCollection);
283 Console.WriteLine("ReplaceWithParms...");
286 statement = parmUtil.ReplaceWithParms();
289 Console.WriteLine("SQL after ParmUtil: " + statement);
294 private void ExecuteQuery (string query, SqlResult res)
298 ExecStatusType execStatus;
300 if(conn.State != ConnectionState.Open)
301 throw new InvalidOperationException(
302 "ConnectionState is not Open");
304 // FIXME: PQexec blocks
305 // while PQsendQuery is non-blocking
306 // which is better to use?
307 // int PQsendQuery(PGconn *conn,
308 // const char *query);
310 // execute SQL command
311 // uses internal property to get the PGConn IntPtr
312 pgResult = PostgresLibrary.
313 PQexec (conn.PostgresConnection, query);
315 execStatus = PostgresLibrary.
316 PQresultStatus (pgResult);
318 if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
319 res.BuildTableSchema(pgResult);
324 errorMessage = PostgresLibrary.
325 PQresStatus(execStatus);
327 errorMessage += " " + PostgresLibrary.
\r
328 PQresultErrorMessage(pgResult);
\r
330 PostgresLibrary.PQclear (pgResult);
331 pgResult = IntPtr.Zero;
333 throw new SqlException(0, 0,
335 conn.DataSource, "SqlCommand", 0);
\r
339 // since SqlCommand has resources so SqlDataReader
340 // can do Read() and NextResult(), need to free
341 // those resources. Also, need to allow this SqlCommand
342 // and this SqlConnection to do things again.
343 internal void CloseReader() {
344 conn.OpenReader = false;
350 /// ExecuteScalar is used to retrieve one object
351 /// from one result set
\r
352 /// that has one row and one column.
\r
353 /// It is lightweight compared to ExecuteReader.
\r
356 public object ExecuteScalar () {
357 IntPtr pgResult; // PGresult
358 ExecStatusType execStatus;
359 object obj = null; // return
360 int nRow = 0; // first row
361 int nCol = 0; // first column
367 if(conn.State != ConnectionState.Open)
368 throw new InvalidOperationException(
369 "ConnnectionState is not Open");
371 query = TweakQuery(sql, cmdType);
373 // FIXME: PQexec blocks
374 // while PQsendQuery is non-blocking
375 // which is better to use?
376 // int PQsendQuery(PGconn *conn,
377 // const char *query);
379 // execute SQL command
380 // uses internal property to get the PGConn IntPtr
381 pgResult = PostgresLibrary.
382 PQexec (conn.PostgresConnection, query);
384 execStatus = PostgresLibrary.
385 PQresultStatus (pgResult);
387 if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
388 nRows = PostgresLibrary.
391 nFields = PostgresLibrary.
394 if(nRows > 0 && nFields > 0) {
398 //fieldName = PostgresLibrary.
399 // PQfname(pgResult, nCol);
404 // get PostgreSQL data type (OID)
405 oid = PostgresLibrary.
406 PQftype(pgResult, nCol);
407 sType = PostgresHelper.
408 OidToTypname (oid, conn.Types);
409 dbType = PostgresHelper.
410 TypnameToSqlDbType(sType);
413 // get defined size of column
414 definedSize = PostgresLibrary.
415 PQfsize(pgResult, nCol);
418 value = PostgresLibrary.
425 columnIsNull = PostgresLibrary.
426 PQgetisnull(pgResult,
431 actualLength = PostgresLibrary.
432 PQgetlength(pgResult,
435 obj = PostgresHelper.
436 ConvertDbTypeToSystem (
442 PostgresLibrary.PQclear (pgResult);
443 pgResult = IntPtr.Zero;
449 errorMessage = PostgresLibrary.
450 PQresStatus(execStatus);
452 errorMessage += " " + PostgresLibrary.
\r
453 PQresultErrorMessage(pgResult);
\r
455 PostgresLibrary.PQclear (pgResult);
456 pgResult = IntPtr.Zero;
458 throw new SqlException(0, 0,
460 conn.DataSource, "SqlCommand", 0);
\r
467 public XmlReader ExecuteXmlReader () {
468 throw new NotImplementedException ();
472 public void Prepare () {
473 // FIXME: parameters have to be implemented for this
474 throw new NotImplementedException ();
478 public SqlCommand Clone () {
479 throw new NotImplementedException ();
482 #endregion // Methods
486 public string CommandText {
496 public int CommandTimeout {
502 // FIXME: if value < 0, throw
505 // throw ArgumentException;
510 public CommandType CommandType {
520 // FIXME: for property Connection, is this the correct
521 // way to handle a return of a stronger type?
522 IDbConnection IDbCommand.Connection {
528 // FIXME: throw an InvalidOperationException
529 // if the change was during a
530 // transaction in progress
533 Connection = (SqlConnection) value;
535 // Connection = value;
537 // FIXME: set Transaction property to null
541 public SqlConnection Connection {
543 // conn defaults to null
548 // FIXME: throw an InvalidOperationException
549 // if the change was during
550 // a transaction in progress
552 // FIXME: set Transaction property to null
556 public bool DesignTimeVisible {
566 // FIXME; for property Parameters, is this the correct
567 // way to handle a stronger return type?
568 IDataParameterCollection IDbCommand.Parameters {
574 public SqlParameterCollection Parameters {
576 return parmCollection;
580 // FIXME: for property Transaction, is this the correct
581 // way to handle a return of a stronger type?
582 IDbTransaction IDbCommand.Transaction {
588 // FIXME: error handling - do not allow
589 // setting of transaction if transaction
593 Transaction = (SqlTransaction) value;
595 // Transaction = value;
599 public SqlTransaction Transaction {
605 // FIXME: error handling
611 public UpdateRowSource UpdatedRowSource {
612 // FIXME: do this once DbDataAdaptor
613 // and DataRow are done
615 throw new NotImplementedException ();
618 throw new NotImplementedException ();
622 #endregion // Properties
624 #region Inner Classes
626 #endregion // Inner Classes
631 public void Dispose() {
632 // FIXME: need proper way to release resources
638 // FIXME: need proper way to release resources
642 #endregion //Destructors
645 // SqlResult is used for passing Result Set data
646 // from SqlCommand to SqlDataReader
647 internal class SqlResult {
649 private DataTable dataTableSchema; // only will contain the schema
650 private IntPtr pg_result; // native PostgreSQL PGresult
651 private int rowCount;
652 private int fieldCount;
653 private string[] pgtypes; // PostgreSQL types (typname)
654 private bool resultReturned = false;
655 private SqlConnection con;
657 internal SqlConnection Connection {
663 internal bool ResultReturned {
665 return resultReturned;
668 resultReturned = value;
672 internal DataTable Table {
674 return dataTableSchema;
678 internal IntPtr PgResult {
684 internal int RowCount {
690 internal int FieldCount {
696 internal string[] PgTypes {
702 internal void BuildTableSchema (IntPtr pgResult) {
703 pg_result = pgResult;
707 dataTableSchema = new DataTable();
709 rowCount = PostgresLibrary.
712 fieldCount = PostgresLibrary.
716 pgtypes = new string[fieldCount];
718 for(nCol = 0; nCol < fieldCount; nCol++) {
724 fieldName = PostgresLibrary.
725 PQfname(pgResult, nCol);
727 // get PostgreSQL data type (OID)
728 oid = PostgresLibrary.
729 PQftype(pgResult, nCol);
730 pgtypes[nCol] = PostgresHelper.
731 OidToTypname (oid, con.Types);
734 // get defined size of column
735 definedSize = PostgresLibrary.
736 PQfsize(pgResult, nCol);
738 // build the data column and add it the table
739 DataColumn dc = new DataColumn(fieldName);
741 dbType = PostgresHelper.
742 TypnameToSqlDbType(pgtypes[nCol]);
743 dc.DataType = PostgresHelper.
744 DbTypeToSystemType(dbType);
745 dc.MaxLength = definedSize;
746 dc.SetTable(dataTableSchema);
748 dataTableSchema.Columns.Add(dc);