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.ComponentModel;
28 using System.Data.Common;
29 using System.Runtime.InteropServices;
32 namespace System.Data.SqlClient {
34 /// Represents a SQL statement that is executed
35 /// while connected to a SQL database.
37 // public sealed class SqlCommand : Component, IDbCommand, ICloneable
38 public sealed class SqlCommand : IDbCommand {
39 // FIXME: Console.WriteLine() is used for debugging throughout
43 private string sql = "";
44 private int timeout = 30;
45 // default is 30 seconds
46 // for command execution
48 private SqlConnection conn = null;
49 private SqlTransaction trans = null;
50 private CommandType cmdType = CommandType.Text;
51 private bool designTime = false;
52 private SqlParameterCollection parmCollection = new
53 SqlParameterCollection();
55 // SqlDataReader state data for ExecuteReader()
56 private SqlDataReader dataReader = null;
57 private string[] queries;
58 private int currentQuery;
59 CommandBehavior cmdBehavior = CommandBehavior.Default;
69 public SqlCommand (string cmdText) {
73 public SqlCommand (string cmdText, SqlConnection connection) {
78 public SqlCommand (string cmdText, SqlConnection connection,
79 SqlTransaction transaction) {
85 #endregion // Constructors
90 public void Cancel () {
91 // FIXME: use non-blocking Exec for this
92 throw new NotImplementedException ();
95 // FIXME: is this the correct way to return a stronger type?
97 IDbDataParameter IDbCommand.CreateParameter () {
98 return CreateParameter ();
102 public SqlParameter CreateParameter () {
103 return new SqlParameter ();
106 public int ExecuteNonQuery () {
107 IntPtr pgResult; // PGresult
108 int rowsAffected = -1;
109 ExecStatusType execStatus;
110 String rowsAffectedString;
112 if(conn.State != ConnectionState.Open)
113 throw new InvalidOperationException(
114 "ConnnectionState is not Open");
116 // FIXME: PQexec blocks
117 // while PQsendQuery is non-blocking
118 // which is better to use?
119 // int PQsendQuery(PGconn *conn,
120 // const char *query);
122 // execute SQL command
123 // uses internal property to get the PGConn IntPtr
124 pgResult = PostgresLibrary.
125 PQexec (conn.PostgresConnection, sql);
127 execStatus = PostgresLibrary.
128 PQresultStatus (pgResult);
130 if(execStatus == ExecStatusType.PGRES_COMMAND_OK) {
131 rowsAffectedString = PostgresLibrary.
132 PQcmdTuples (pgResult);
134 if(rowsAffectedString != null)
135 if(rowsAffectedString.Equals("") == false)
136 rowsAffected = int.Parse(rowsAffectedString);
138 PostgresLibrary.PQclear (pgResult);
143 errorMessage = PostgresLibrary.
144 PQresStatus(execStatus);
146 errorMessage += " " + PostgresLibrary.
\r
147 PQresultErrorMessage(pgResult);
\r
149 throw new SqlException(0, 0,
151 conn.DataSource, "SqlCommand", 0);
\r
158 IDataReader IDbCommand.ExecuteReader () {
159 return ExecuteReader ();
163 public SqlDataReader ExecuteReader () {
164 return ExecuteReader(CommandBehavior.Default);
168 IDataReader IDbCommand.ExecuteReader (
169 CommandBehavior behavior) {
170 return ExecuteReader (behavior);
174 public SqlDataReader ExecuteReader (CommandBehavior behavior)
176 if(conn.State != ConnectionState.Open)
177 throw new InvalidOperationException(
178 "ConnectionState is not Open");
180 cmdBehavior = behavior;
184 dataReader = new SqlDataReader(this);
186 if((behavior & CommandBehavior.SingleResult) == CommandBehavior.SingleResult) {
187 queries = new String[1];
191 queries = sql.Split(new Char[] {';'});
194 dataReader.NextResult();
199 internal SqlResult NextResult()
201 SqlResult res = new SqlResult();
202 res.Connection = this.Connection;
206 if(currentQuery < queries.Length && queries[currentQuery].Equals("") == false) {
207 ExecuteQuery(queries[currentQuery], res);
208 res.ResultReturned = true;
211 res.ResultReturned = false;
217 private void ExecuteQuery (string query, SqlResult res)
221 ExecStatusType execStatus;
223 if(conn.State != ConnectionState.Open)
224 throw new InvalidOperationException(
225 "ConnectionState is not Open");
227 // FIXME: PQexec blocks
228 // while PQsendQuery is non-blocking
229 // which is better to use?
230 // int PQsendQuery(PGconn *conn,
231 // const char *query);
233 // execute SQL command
234 // uses internal property to get the PGConn IntPtr
235 pgResult = PostgresLibrary.
236 PQexec (conn.PostgresConnection, query);
238 execStatus = PostgresLibrary.
239 PQresultStatus (pgResult);
241 if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
242 res.BuildTableSchema(pgResult);
247 errorMessage = PostgresLibrary.
248 PQresStatus(execStatus);
250 errorMessage += " " + PostgresLibrary.
\r
251 PQresultErrorMessage(pgResult);
\r
253 throw new SqlException(0, 0,
255 conn.DataSource, "SqlCommand", 0);
\r
260 public object ExecuteScalar () {
261 IntPtr pgResult; // PGresult
262 ExecStatusType execStatus;
263 object obj = null; // return
264 int nRow = 0; // first row
265 int nCol = 0; // first column
270 if(conn.State != ConnectionState.Open)
271 throw new InvalidOperationException(
272 "ConnnectionState is not Open");
274 // FIXME: PQexec blocks
275 // while PQsendQuery is non-blocking
276 // which is better to use?
277 // int PQsendQuery(PGconn *conn,
278 // const char *query);
280 // execute SQL command
281 // uses internal property to get the PGConn IntPtr
282 pgResult = PostgresLibrary.
283 PQexec (conn.PostgresConnection, sql);
285 execStatus = PostgresLibrary.
286 PQresultStatus (pgResult);
288 if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
289 nRows = PostgresLibrary.
292 nFields = PostgresLibrary.
295 if(nRows > 0 && nFields > 0) {
299 //fieldName = PostgresLibrary.
300 // PQfname(pgResult, nCol);
305 // get PostgreSQL data type (OID)
306 oid = PostgresLibrary.
307 PQftype(pgResult, nCol);
308 sType = PostgresHelper.
309 OidToTypname (oid, conn.Types);
310 dbType = PostgresHelper.
311 TypnameToSqlDbType(sType);
314 // get defined size of column
315 definedSize = PostgresLibrary.
316 PQfsize(pgResult, nCol);
319 value = PostgresLibrary.
326 columnIsNull = PostgresLibrary.
327 PQgetisnull(pgResult,
332 actualLength = PostgresLibrary.
333 PQgetlength(pgResult,
336 obj = PostgresHelper.
337 ConvertDbTypeToSystem (
343 PostgresLibrary.PQclear (pgResult);
349 errorMessage = PostgresLibrary.
350 PQresStatus(execStatus);
352 errorMessage += " " + PostgresLibrary.
\r
353 PQresultErrorMessage(pgResult);
\r
355 throw new SqlException(0, 0,
357 conn.DataSource, "SqlCommand", 0);
\r
364 public XmlReader ExecuteXmlReader () {
365 throw new NotImplementedException ();
369 public void Prepare () {
370 // FIXME: parameters have to be implemented for this
371 throw new NotImplementedException ();
375 public SqlCommand Clone () {
376 throw new NotImplementedException ();
379 #endregion // Methods
383 public string CommandText {
393 public int CommandTimeout {
399 // FIXME: if value < 0, throw
402 // throw ArgumentException;
407 public CommandType CommandType {
417 // FIXME: for property Connection, is this the correct
418 // way to handle a return of a stronger type?
419 IDbConnection IDbCommand.Connection {
425 // FIXME: throw an InvalidOperationException
426 // if the change was during a
427 // transaction in progress
430 Connection = (SqlConnection) value;
432 // Connection = value;
434 // FIXME: set Transaction property to null
438 public SqlConnection Connection {
440 // conn defaults to null
445 // FIXME: throw an InvalidOperationException
446 // if the change was during
447 // a transaction in progress
449 // FIXME: set Transaction property to null
453 public bool DesignTimeVisible {
463 // FIXME; for property Parameters, is this the correct
464 // way to handle a stronger return type?
465 IDataParameterCollection IDbCommand.Parameters {
471 SqlParameterCollection Parameters {
473 return parmCollection;
477 // FIXME: for property Transaction, is this the correct
478 // way to handle a return of a stronger type?
479 IDbTransaction IDbCommand.Transaction {
485 // FIXME: error handling - do not allow
486 // setting of transaction if transaction
490 Transaction = (SqlTransaction) value;
492 // Transaction = value;
496 public SqlTransaction Transaction {
502 // FIXME: error handling
508 public UpdateRowSource UpdatedRowSource {
509 // FIXME: do this once DbDataAdaptor
510 // and DataRow are done
512 throw new NotImplementedException ();
515 throw new NotImplementedException ();
519 #endregion // Properties
521 #region Inner Classes
523 #endregion // Inner Classes
528 public void Dispose() {
529 // FIXME: need proper way to release resources
535 // FIXME: need proper way to release resources
539 #endregion //Destructors
542 // SqlResult is used for passing Result Set data
543 // from SqlCommand to SqlDataReader
544 internal class SqlResult {
546 private DataTable dataTableSchema; // only will contain the schema
547 private IntPtr pg_result; // native PostgreSQL PGresult
548 private int rowCount;
549 private int fieldCount;
550 private string[] pgtypes; // PostgreSQL types (typname)
551 private bool resultReturned = false;
552 private SqlConnection con;
554 internal SqlConnection Connection {
560 internal bool ResultReturned {
562 return resultReturned;
565 resultReturned = value;
569 internal DataTable Table {
571 return dataTableSchema;
575 internal IntPtr PgResult {
581 internal int RowCount {
587 internal int FieldCount {
593 internal string[] PgTypes {
599 internal void BuildTableSchema (IntPtr pgResult) {
600 pg_result = pgResult;
604 dataTableSchema = new DataTable();
606 rowCount = PostgresLibrary.
609 fieldCount = PostgresLibrary.
613 pgtypes = new string[fieldCount];
615 for(nCol = 0; nCol < fieldCount; nCol++) {
621 fieldName = PostgresLibrary.
622 PQfname(pgResult, nCol);
624 // get PostgreSQL data type (OID)
625 oid = PostgresLibrary.
626 PQftype(pgResult, nCol);
627 pgtypes[nCol] = PostgresHelper.
628 OidToTypname (oid, con.Types);
631 // get defined size of column
632 definedSize = PostgresLibrary.
633 PQfsize(pgResult, nCol);
635 // build the data column and add it the table
636 DataColumn dc = new DataColumn(fieldName);
638 dbType = PostgresHelper.
639 TypnameToSqlDbType(pgtypes[nCol]);
640 dc.DataType = PostgresHelper.
641 DbTypeToSystemType(dbType);
642 dc.MaxLength = definedSize;
643 dc.SetTable(dataTableSchema);
645 dataTableSchema.Columns.Add(dc);