SqlCommand.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. //
  2. // System.Data.SqlClient.SqlCommand.cs
  3. //
  4. // Author:
  5. // Rodrigo Moya ([email protected])
  6. // Daniel Morgan ([email protected])
  7. //
  8. // (C) Ximian, Inc 2002 http://www.ximian.com/
  9. // (C) Daniel Morgan, 2002
  10. //
  11. // Credits:
  12. // SQL and concepts were used from libgda 0.8.190 (GNOME Data Access)
  13. // http://www.gnome-db.org/
  14. // with permission from the authors of the
  15. // PostgreSQL provider in libgda:
  16. // Michael Lausch <[email protected]>
  17. // Rodrigo Moya <[email protected]>
  18. // Vivien Malerba <[email protected]>
  19. // Gonzalo Paniagua Javier <[email protected]>
  20. //
  21. // use #define DEBUG_SqlCommand if you want to spew debug messages
  22. // #define DEBUG_SqlCommand
  23. using System;
  24. using System.ComponentModel;
  25. using System.Data;
  26. using System.Data.Common;
  27. using System.Runtime.InteropServices;
  28. using System.Xml;
  29. namespace System.Data.SqlClient {
  30. /// <summary>
  31. /// Represents a SQL statement that is executed
  32. /// while connected to a SQL database.
  33. /// </summary>
  34. // public sealed class SqlCommand : Component, IDbCommand, ICloneable
  35. public sealed class SqlCommand : IDbCommand {
  36. // FIXME: Console.WriteLine() is used for debugging throughout
  37. #region Fields
  38. string sql = "";
  39. int timeout = 30;
  40. // default is 30 seconds
  41. // for command execution
  42. SqlConnection conn = null;
  43. SqlTransaction trans = null;
  44. CommandType cmdType = CommandType.Text;
  45. bool designTime = false;
  46. SqlParameterCollection parmCollection = new
  47. SqlParameterCollection();
  48. #endregion // Fields
  49. #region Constructors
  50. public SqlCommand() {
  51. sql = "";
  52. }
  53. public SqlCommand (string cmdText) {
  54. sql = cmdText;
  55. }
  56. public SqlCommand (string cmdText, SqlConnection connection) {
  57. sql = cmdText;
  58. conn = connection;
  59. }
  60. public SqlCommand (string cmdText, SqlConnection connection,
  61. SqlTransaction transaction) {
  62. sql = cmdText;
  63. conn = connection;
  64. trans = transaction;
  65. }
  66. #endregion // Constructors
  67. #region Methods
  68. [MonoTODO]
  69. public void Cancel () {
  70. // FIXME: use non-blocking Exec for this
  71. throw new NotImplementedException ();
  72. }
  73. // FIXME: is this the correct way to return a stronger type?
  74. [MonoTODO]
  75. IDbDataParameter IDbCommand.CreateParameter () {
  76. return CreateParameter ();
  77. }
  78. [MonoTODO]
  79. public SqlParameter CreateParameter () {
  80. return new SqlParameter ();
  81. }
  82. public int ExecuteNonQuery () {
  83. IntPtr pgResult; // PGresult
  84. int rowsAffected = -1;
  85. ExecStatusType execStatus;
  86. String rowsAffectedString;
  87. if(conn.State != ConnectionState.Open)
  88. throw new InvalidOperationException(
  89. "ConnnectionState is not Open");
  90. // FIXME: PQexec blocks
  91. // while PQsendQuery is non-blocking
  92. // which is better to use?
  93. // int PQsendQuery(PGconn *conn,
  94. // const char *query);
  95. // execute SQL command
  96. // uses internal property to get the PGConn IntPtr
  97. pgResult = PostgresLibrary.
  98. PQexec (conn.PostgresConnection, sql);
  99. execStatus = PostgresLibrary.
  100. PQresultStatus (pgResult);
  101. if(execStatus == ExecStatusType.PGRES_COMMAND_OK) {
  102. rowsAffectedString = PostgresLibrary.
  103. PQcmdTuples (pgResult);
  104. if(rowsAffectedString != null)
  105. if(rowsAffectedString.Equals("") == false)
  106. rowsAffected = int.Parse(rowsAffectedString);
  107. PostgresLibrary.PQclear (pgResult);
  108. }
  109. else {
  110. String errorMessage;
  111. errorMessage = PostgresLibrary.
  112. PQresStatus(execStatus);
  113. errorMessage += " " + PostgresLibrary.
  114. PQresultErrorMessage(pgResult);
  115. throw new SqlException(0, 0,
  116. errorMessage, 0, "",
  117. conn.DataSource, "SqlCommand", 0);
  118. }
  119. return rowsAffected;
  120. }
  121. [MonoTODO]
  122. IDataReader IDbCommand.ExecuteReader () {
  123. return ExecuteReader ();
  124. }
  125. [MonoTODO]
  126. public SqlDataReader ExecuteReader () {
  127. return ExecuteReader(CommandBehavior.Default);
  128. }
  129. [MonoTODO]
  130. IDataReader IDbCommand.ExecuteReader (
  131. CommandBehavior behavior) {
  132. return ExecuteReader (behavior);
  133. }
  134. [MonoTODO]
  135. public SqlDataReader ExecuteReader (CommandBehavior behavior) {
  136. // FIXME: currently only works for a
  137. // single result set
  138. // ExecuteReader can be used
  139. // for multiple result sets
  140. SqlDataReader dataReader = null;
  141. IntPtr pgResult; // PGresult
  142. ExecStatusType execStatus;
  143. if(conn.State != ConnectionState.Open)
  144. throw new InvalidOperationException(
  145. "ConnnectionState is not Open");
  146. // FIXME: PQexec blocks
  147. // while PQsendQuery is non-blocking
  148. // which is better to use?
  149. // int PQsendQuery(PGconn *conn,
  150. // const char *query);
  151. // execute SQL command
  152. // uses internal property to get the PGConn IntPtr
  153. pgResult = PostgresLibrary.
  154. PQexec (conn.PostgresConnection, sql);
  155. execStatus = PostgresLibrary.
  156. PQresultStatus (pgResult);
  157. if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
  158. DataTable dt = null;
  159. int rows, cols;
  160. string[] types;
  161. // FIXME: maybe i should move the
  162. // BuildTableSchema code
  163. // to the SqlDataReader?
  164. dt = BuildTableSchema(pgResult,
  165. out rows, out cols, out types);
  166. dataReader = new SqlDataReader(this, dt, pgResult,
  167. rows, cols, types);
  168. }
  169. else {
  170. String errorMessage;
  171. errorMessage = PostgresLibrary.
  172. PQresStatus(execStatus);
  173. errorMessage += " " + PostgresLibrary.
  174. PQresultErrorMessage(pgResult);
  175. throw new SqlException(0, 0,
  176. errorMessage, 0, "",
  177. conn.DataSource, "SqlCommand", 0);
  178. }
  179. return dataReader;
  180. }
  181. internal DataTable BuildTableSchema (IntPtr pgResult,
  182. out int nRows,
  183. out int nFields,
  184. out string[] types) {
  185. int nCol;
  186. DataTable dt = new DataTable();
  187. nRows = PostgresLibrary.
  188. PQntuples(pgResult);
  189. nFields = PostgresLibrary.
  190. PQnfields(pgResult);
  191. int oid;
  192. types = new string[nFields];
  193. for(nCol = 0; nCol < nFields; nCol++) {
  194. DbType dbType;
  195. // get column name
  196. String fieldName;
  197. fieldName = PostgresLibrary.
  198. PQfname(pgResult, nCol);
  199. // get PostgreSQL data type (OID)
  200. oid = PostgresLibrary.
  201. PQftype(pgResult, nCol);
  202. types[nCol] = PostgresHelper.
  203. OidToTypname (oid, conn.Types);
  204. int definedSize;
  205. // get defined size of column
  206. definedSize = PostgresLibrary.
  207. PQfsize(pgResult, nCol);
  208. // build the data column and add it the table
  209. DataColumn dc = new DataColumn(fieldName);
  210. dbType = PostgresHelper.
  211. TypnameToSqlDbType(types[nCol]);
  212. dc.DataType = PostgresHelper.
  213. DbTypeToSystemType(dbType);
  214. dc.MaxLength = definedSize;
  215. dc.SetTable(dt);
  216. dt.Columns.Add(dc);
  217. }
  218. return dt;
  219. }
  220. [MonoTODO]
  221. public object ExecuteScalar () {
  222. IntPtr pgResult; // PGresult
  223. ExecStatusType execStatus;
  224. object obj = null; // return
  225. int nRow = 0; // first row
  226. int nCol = 0; // first column
  227. String value;
  228. int nRows;
  229. int nFields;
  230. if(conn.State != ConnectionState.Open)
  231. throw new InvalidOperationException(
  232. "ConnnectionState is not Open");
  233. // FIXME: PQexec blocks
  234. // while PQsendQuery is non-blocking
  235. // which is better to use?
  236. // int PQsendQuery(PGconn *conn,
  237. // const char *query);
  238. // execute SQL command
  239. // uses internal property to get the PGConn IntPtr
  240. pgResult = PostgresLibrary.
  241. PQexec (conn.PostgresConnection, sql);
  242. execStatus = PostgresLibrary.
  243. PQresultStatus (pgResult);
  244. if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
  245. nRows = PostgresLibrary.
  246. PQntuples(pgResult);
  247. nFields = PostgresLibrary.
  248. PQnfields(pgResult);
  249. if(nRows > 0 && nFields > 0) {
  250. // get column name
  251. //String fieldName;
  252. //fieldName = PostgresLibrary.
  253. // PQfname(pgResult, nCol);
  254. int oid;
  255. string sType;
  256. DbType dbType;
  257. // get PostgreSQL data type (OID)
  258. oid = PostgresLibrary.
  259. PQftype(pgResult, nCol);
  260. sType = PostgresHelper.
  261. OidToTypname (oid, conn.Types);
  262. dbType = PostgresHelper.
  263. TypnameToSqlDbType(sType);
  264. int definedSize;
  265. // get defined size of column
  266. definedSize = PostgresLibrary.
  267. PQfsize(pgResult, nCol);
  268. // get data value
  269. value = PostgresLibrary.
  270. PQgetvalue(
  271. pgResult,
  272. nRow, nCol);
  273. int columnIsNull;
  274. // is column NULL?
  275. columnIsNull = PostgresLibrary.
  276. PQgetisnull(pgResult,
  277. nRow, nCol);
  278. int actualLength;
  279. // get Actual Length
  280. actualLength = PostgresLibrary.
  281. PQgetlength(pgResult,
  282. nRow, nCol);
  283. obj = PostgresHelper.
  284. ConvertDbTypeToSystem (
  285. dbType,
  286. value);
  287. }
  288. // close result set
  289. PostgresLibrary.PQclear (pgResult);
  290. }
  291. else {
  292. String errorMessage;
  293. errorMessage = PostgresLibrary.
  294. PQresStatus(execStatus);
  295. errorMessage += " " + PostgresLibrary.
  296. PQresultErrorMessage(pgResult);
  297. throw new SqlException(0, 0,
  298. errorMessage, 0, "",
  299. conn.DataSource, "SqlCommand", 0);
  300. }
  301. return obj;
  302. }
  303. [MonoTODO]
  304. public XmlReader ExecuteXmlReader () {
  305. throw new NotImplementedException ();
  306. }
  307. [MonoTODO]
  308. public void Prepare () {
  309. // FIXME: parameters have to be implemented for this
  310. throw new NotImplementedException ();
  311. }
  312. [MonoTODO]
  313. public SqlCommand Clone () {
  314. throw new NotImplementedException ();
  315. }
  316. #endregion // Methods
  317. #region Properties
  318. public string CommandText {
  319. get {
  320. return sql;
  321. }
  322. set {
  323. sql = value;
  324. }
  325. }
  326. public int CommandTimeout {
  327. get {
  328. return timeout;
  329. }
  330. set {
  331. // FIXME: if value < 0, throw
  332. // ArgumentException
  333. // if (value < 0)
  334. // throw ArgumentException;
  335. timeout = value;
  336. }
  337. }
  338. public CommandType CommandType {
  339. get {
  340. return cmdType;
  341. }
  342. set {
  343. cmdType = value;
  344. }
  345. }
  346. // FIXME: for property Connection, is this the correct
  347. // way to handle a return of a stronger type?
  348. IDbConnection IDbCommand.Connection {
  349. get {
  350. return Connection;
  351. }
  352. set {
  353. // FIXME: throw an InvalidOperationException
  354. // if the change was during a
  355. // transaction in progress
  356. // csc
  357. Connection = (SqlConnection) value;
  358. // mcs
  359. // Connection = value;
  360. // FIXME: set Transaction property to null
  361. }
  362. }
  363. public SqlConnection Connection {
  364. get {
  365. // conn defaults to null
  366. return conn;
  367. }
  368. set {
  369. // FIXME: throw an InvalidOperationException
  370. // if the change was during
  371. // a transaction in progress
  372. conn = value;
  373. // FIXME: set Transaction property to null
  374. }
  375. }
  376. public bool DesignTimeVisible {
  377. get {
  378. return designTime;
  379. }
  380. set{
  381. designTime = value;
  382. }
  383. }
  384. // FIXME; for property Parameters, is this the correct
  385. // way to handle a stronger return type?
  386. IDataParameterCollection IDbCommand.Parameters {
  387. get {
  388. return Parameters;
  389. }
  390. }
  391. SqlParameterCollection Parameters {
  392. get {
  393. return parmCollection;
  394. }
  395. }
  396. // FIXME: for property Transaction, is this the correct
  397. // way to handle a return of a stronger type?
  398. IDbTransaction IDbCommand.Transaction {
  399. get {
  400. return Transaction;
  401. }
  402. set {
  403. // FIXME: error handling - do not allow
  404. // setting of transaction if transaction
  405. // has already begun
  406. // csc
  407. Transaction = (SqlTransaction) value;
  408. // mcs
  409. // Transaction = value;
  410. }
  411. }
  412. public SqlTransaction Transaction {
  413. get {
  414. return trans;
  415. }
  416. set {
  417. // FIXME: error handling
  418. trans = value;
  419. }
  420. }
  421. [MonoTODO]
  422. public UpdateRowSource UpdatedRowSource {
  423. // FIXME: do this once DbDataAdaptor
  424. // and DataRow are done
  425. get {
  426. throw new NotImplementedException ();
  427. }
  428. set {
  429. throw new NotImplementedException ();
  430. }
  431. }
  432. #endregion // Properties
  433. #region Destructors
  434. [MonoTODO]
  435. public void Dispose() {
  436. // FIXME: need proper way to release resources
  437. // Dispose(true);
  438. }
  439. [MonoTODO]
  440. ~SqlCommand() {
  441. // FIXME: need proper way to release resources
  442. // Dispose(false);
  443. }
  444. #endregion //Destructors
  445. }
  446. }