SqlCommand.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. //
  2. // System.Data.SqlClient.SqlCommand.cs
  3. //
  4. // Author:
  5. // Rodrigo Moya ([email protected])
  6. // Daniel Morgan ([email protected])
  7. // Tim Coleman ([email protected])
  8. //
  9. // (C) Ximian, Inc 2002 http://www.ximian.com/
  10. // (C) Daniel Morgan, 2002
  11. // (C) Copyright 2002 Tim Coleman
  12. //
  13. // Credits:
  14. // SQL and concepts were used from libgda 0.8.190 (GNOME Data Access)
  15. // http://www.gnome-db.org/
  16. // with permission from the authors of the
  17. // PostgreSQL provider in libgda:
  18. // Michael Lausch <[email protected]>
  19. // Rodrigo Moya <[email protected]>
  20. // Vivien Malerba <[email protected]>
  21. // Gonzalo Paniagua Javier <[email protected]>
  22. //
  23. // use #define DEBUG_SqlCommand if you want to spew debug messages
  24. // #define DEBUG_SqlCommand
  25. using System;
  26. using System.Collections;
  27. using System.ComponentModel;
  28. using System.Data;
  29. using System.Data.Common;
  30. using System.Runtime.InteropServices;
  31. using System.Text;
  32. using System.Xml;
  33. namespace System.Data.SqlClient {
  34. /// <summary>
  35. /// Represents a SQL statement that is executed
  36. /// while connected to a SQL database.
  37. /// </summary>
  38. // public sealed class SqlCommand : Component, IDbCommand, ICloneable
  39. public sealed class SqlCommand : IDbCommand {
  40. #region Fields
  41. private string sql = "";
  42. private int timeout = 30;
  43. // default is 30 seconds
  44. // for command execution
  45. private SqlConnection conn = null;
  46. private SqlTransaction trans = null;
  47. private CommandType cmdType = CommandType.Text;
  48. private bool designTime = false;
  49. private SqlParameterCollection parmCollection = new
  50. SqlParameterCollection();
  51. // SqlDataReader state data for ExecuteReader()
  52. private SqlDataReader dataReader = null;
  53. private string[] queries = null;
  54. private int currentQuery = -1;
  55. private CommandBehavior cmdBehavior = CommandBehavior.Default;
  56. private ParmUtil parmUtil = null;
  57. #endregion // Fields
  58. #region Constructors
  59. public SqlCommand() {
  60. sql = "";
  61. }
  62. public SqlCommand (string cmdText) {
  63. sql = cmdText;
  64. }
  65. public SqlCommand (string cmdText, SqlConnection connection) {
  66. sql = cmdText;
  67. conn = connection;
  68. }
  69. public SqlCommand (string cmdText, SqlConnection connection,
  70. SqlTransaction transaction) {
  71. sql = cmdText;
  72. conn = connection;
  73. trans = transaction;
  74. }
  75. #endregion // Constructors
  76. #region Methods
  77. [MonoTODO]
  78. public void Cancel () {
  79. // FIXME: use non-blocking Exec for this
  80. throw new NotImplementedException ();
  81. }
  82. // FIXME: is this the correct way to return a stronger type?
  83. [MonoTODO]
  84. IDbDataParameter IDbCommand.CreateParameter () {
  85. return CreateParameter ();
  86. }
  87. [MonoTODO]
  88. public SqlParameter CreateParameter () {
  89. return new SqlParameter ();
  90. }
  91. public int ExecuteNonQuery () {
  92. IntPtr pgResult; // PGresult
  93. int rowsAffected = -1;
  94. ExecStatusType execStatus;
  95. String rowsAffectedString;
  96. string query;
  97. if(conn.State != ConnectionState.Open)
  98. throw new InvalidOperationException(
  99. "ConnnectionState is not Open");
  100. query = TweakQuery(sql, cmdType);
  101. // FIXME: PQexec blocks
  102. // while PQsendQuery is non-blocking
  103. // which is better to use?
  104. // int PQsendQuery(PGconn *conn,
  105. // const char *query);
  106. // execute SQL command
  107. // uses internal property to get the PGConn IntPtr
  108. pgResult = PostgresLibrary.
  109. PQexec (conn.PostgresConnection, query);
  110. execStatus = PostgresLibrary.
  111. PQresultStatus (pgResult);
  112. if(execStatus == ExecStatusType.PGRES_COMMAND_OK ||
  113. execStatus == ExecStatusType.PGRES_TUPLES_OK ) {
  114. rowsAffectedString = PostgresLibrary.
  115. PQcmdTuples (pgResult);
  116. if(rowsAffectedString != null)
  117. if(rowsAffectedString.Equals("") == false)
  118. rowsAffected = int.Parse(rowsAffectedString);
  119. PostgresLibrary.PQclear (pgResult);
  120. pgResult = IntPtr.Zero;
  121. }
  122. else {
  123. String errorMessage;
  124. errorMessage = PostgresLibrary.
  125. PQresStatus(execStatus);
  126. errorMessage += " " + PostgresLibrary.
  127. PQresultErrorMessage(pgResult);
  128. PostgresLibrary.PQclear (pgResult);
  129. pgResult = IntPtr.Zero;
  130. throw new SqlException(0, 0,
  131. errorMessage, 0, "",
  132. conn.DataSource, "SqlCommand", 0);
  133. }
  134. return rowsAffected;
  135. }
  136. [MonoTODO]
  137. IDataReader IDbCommand.ExecuteReader () {
  138. return ExecuteReader ();
  139. }
  140. [MonoTODO]
  141. public SqlDataReader ExecuteReader () {
  142. return ExecuteReader(CommandBehavior.Default);
  143. }
  144. [MonoTODO]
  145. IDataReader IDbCommand.ExecuteReader (
  146. CommandBehavior behavior) {
  147. return ExecuteReader (behavior);
  148. }
  149. [MonoTODO]
  150. public SqlDataReader ExecuteReader (CommandBehavior behavior)
  151. {
  152. if(conn.State != ConnectionState.Open)
  153. throw new InvalidOperationException(
  154. "ConnectionState is not Open");
  155. cmdBehavior = behavior;
  156. queries = null;
  157. currentQuery = -1;
  158. dataReader = new SqlDataReader(this);
  159. queries = sql.Split(new Char[] {';'});
  160. dataReader.NextResult();
  161. return dataReader;
  162. }
  163. internal SqlResult NextResult()
  164. {
  165. SqlResult res = new SqlResult();
  166. res.Connection = this.Connection;
  167. res.Behavior = cmdBehavior;
  168. string statement;
  169. currentQuery++;
  170. res.CurrentQuery = currentQuery;
  171. if(currentQuery < queries.Length && queries[currentQuery].Equals("") == false) {
  172. res.SQL = queries[currentQuery];
  173. statement = TweakQuery(queries[currentQuery], cmdType);
  174. ExecuteQuery(statement, res);
  175. res.ResultReturned = true;
  176. }
  177. else {
  178. res.ResultReturned = false;
  179. }
  180. return res;
  181. }
  182. private string TweakQuery(string query, CommandType commandType) {
  183. string statement = "";
  184. StringBuilder td;
  185. #if DEBUG_SqlCommand
  186. Console.WriteLine("---------[][] TweakQuery() [][]--------");
  187. Console.WriteLine("CommandType: " + commandType + " CommandBehavior: " + cmdBehavior);
  188. Console.WriteLine("SQL before command type: " + query);
  189. #endif
  190. // finish building SQL based on CommandType
  191. switch(commandType) {
  192. case CommandType.Text:
  193. statement = query;
  194. break;
  195. case CommandType.StoredProcedure:
  196. statement =
  197. "SELECT " + query + "()";
  198. break;
  199. case CommandType.TableDirect:
  200. // NOTE: this is for the PostgreSQL provider
  201. // and for OleDb, according to the docs,
  202. // an exception is thrown if you try to use
  203. // this with SqlCommand
  204. string[] directTables = query.Split(
  205. new Char[] {','});
  206. td = new StringBuilder("SELECT * FROM ");
  207. for(int tab = 0; tab < directTables.Length; tab++) {
  208. if(tab > 0)
  209. td.Append(',');
  210. td.Append(directTables[tab]);
  211. // FIXME: if multipe tables, how do we
  212. // join? based on Primary/Foreign Keys?
  213. // Otherwise, a Cartesian Product happens
  214. }
  215. statement = td.ToString();
  216. break;
  217. default:
  218. // FIXME: throw an exception?
  219. statement = query;
  220. break;
  221. }
  222. #if DEBUG_SqlCommand
  223. Console.WriteLine("SQL after command type: " + statement);
  224. #endif
  225. // TODO: this parameters utility
  226. // currently only support input variables
  227. // need todo output, input/output, and return.
  228. #if DEBUG_SqlCommand
  229. Console.WriteLine("using ParmUtil in TweakQuery()...");
  230. #endif
  231. parmUtil = new ParmUtil(statement, parmCollection);
  232. #if DEBUG_SqlCommand
  233. Console.WriteLine("ReplaceWithParms...");
  234. #endif
  235. statement = parmUtil.ReplaceWithParms();
  236. #if DEBUG_SqlCommand
  237. Console.WriteLine("SQL after ParmUtil: " + statement);
  238. #endif
  239. return statement;
  240. }
  241. private void ExecuteQuery (string query, SqlResult res)
  242. {
  243. IntPtr pgResult;
  244. ExecStatusType execStatus;
  245. if(conn.State != ConnectionState.Open)
  246. throw new InvalidOperationException(
  247. "ConnectionState is not Open");
  248. // FIXME: PQexec blocks
  249. // while PQsendQuery is non-blocking
  250. // which is better to use?
  251. // int PQsendQuery(PGconn *conn,
  252. // const char *query);
  253. // execute SQL command
  254. // uses internal property to get the PGConn IntPtr
  255. pgResult = PostgresLibrary.
  256. PQexec (conn.PostgresConnection, query);
  257. execStatus = PostgresLibrary.
  258. PQresultStatus (pgResult);
  259. res.ExecStatus = execStatus;
  260. if(execStatus == ExecStatusType.PGRES_TUPLES_OK ||
  261. execStatus == ExecStatusType.PGRES_COMMAND_OK) {
  262. res.BuildTableSchema(pgResult);
  263. }
  264. else {
  265. String errorMessage;
  266. errorMessage = PostgresLibrary.
  267. PQresStatus(execStatus);
  268. errorMessage += " " + PostgresLibrary.
  269. PQresultErrorMessage(pgResult);
  270. PostgresLibrary.PQclear (pgResult);
  271. pgResult = IntPtr.Zero;
  272. throw new SqlException(0, 0,
  273. errorMessage, 0, "",
  274. conn.DataSource, "SqlCommand", 0);
  275. }
  276. }
  277. // since SqlCommand has resources so SqlDataReader
  278. // can do Read() and NextResult(), need to free
  279. // those resources. Also, need to allow this SqlCommand
  280. // and this SqlConnection to do things again.
  281. internal void CloseReader() {
  282. dataReader = null;
  283. queries = null;
  284. if((cmdBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) {
  285. conn.CloseReader(true);
  286. }
  287. else {
  288. conn.CloseReader(false);
  289. }
  290. }
  291. // only meant to be used between SqlConnectioin,
  292. // SqlCommand, and SqlDataReader
  293. internal void OpenReader(SqlDataReader reader) {
  294. conn.OpenReader(reader);
  295. }
  296. /// <summary>
  297. /// ExecuteScalar is used to retrieve one object
  298. /// from one result set
  299. /// that has one row and one column.
  300. /// It is lightweight compared to ExecuteReader.
  301. /// </summary>
  302. [MonoTODO]
  303. public object ExecuteScalar () {
  304. IntPtr pgResult; // PGresult
  305. ExecStatusType execStatus;
  306. object obj = null; // return
  307. int nRow = 0; // first row
  308. int nCol = 0; // first column
  309. String value;
  310. int nRows;
  311. int nFields;
  312. string query;
  313. if(conn.State != ConnectionState.Open)
  314. throw new InvalidOperationException(
  315. "ConnnectionState is not Open");
  316. query = TweakQuery(sql, cmdType);
  317. // FIXME: PQexec blocks
  318. // while PQsendQuery is non-blocking
  319. // which is better to use?
  320. // int PQsendQuery(PGconn *conn,
  321. // const char *query);
  322. // execute SQL command
  323. // uses internal property to get the PGConn IntPtr
  324. pgResult = PostgresLibrary.
  325. PQexec (conn.PostgresConnection, query);
  326. execStatus = PostgresLibrary.
  327. PQresultStatus (pgResult);
  328. if(execStatus == ExecStatusType.PGRES_COMMAND_OK) {
  329. // result was a SQL Command
  330. // close result set
  331. PostgresLibrary.PQclear (pgResult);
  332. pgResult = IntPtr.Zero;
  333. return null; // return null reference
  334. }
  335. else if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
  336. // result was a SQL Query
  337. nRows = PostgresLibrary.
  338. PQntuples(pgResult);
  339. nFields = PostgresLibrary.
  340. PQnfields(pgResult);
  341. if(nRows > 0 && nFields > 0) {
  342. // get column name
  343. //String fieldName;
  344. //fieldName = PostgresLibrary.
  345. // PQfname(pgResult, nCol);
  346. int oid;
  347. string sType;
  348. DbType dbType;
  349. // get PostgreSQL data type (OID)
  350. oid = PostgresLibrary.
  351. PQftype(pgResult, nCol);
  352. sType = PostgresHelper.
  353. OidToTypname (oid, conn.Types);
  354. dbType = PostgresHelper.
  355. TypnameToSqlDbType(sType);
  356. int definedSize;
  357. // get defined size of column
  358. definedSize = PostgresLibrary.
  359. PQfsize(pgResult, nCol);
  360. // get data value
  361. value = PostgresLibrary.
  362. PQgetvalue(
  363. pgResult,
  364. nRow, nCol);
  365. int columnIsNull;
  366. // is column NULL?
  367. columnIsNull = PostgresLibrary.
  368. PQgetisnull(pgResult,
  369. nRow, nCol);
  370. int actualLength;
  371. // get Actual Length
  372. actualLength = PostgresLibrary.
  373. PQgetlength(pgResult,
  374. nRow, nCol);
  375. obj = PostgresHelper.
  376. ConvertDbTypeToSystem (
  377. dbType,
  378. value);
  379. }
  380. // close result set
  381. PostgresLibrary.PQclear (pgResult);
  382. pgResult = IntPtr.Zero;
  383. }
  384. else {
  385. String errorMessage;
  386. errorMessage = PostgresLibrary.
  387. PQresStatus(execStatus);
  388. errorMessage += " " + PostgresLibrary.
  389. PQresultErrorMessage(pgResult);
  390. PostgresLibrary.PQclear (pgResult);
  391. pgResult = IntPtr.Zero;
  392. throw new SqlException(0, 0,
  393. errorMessage, 0, "",
  394. conn.DataSource, "SqlCommand", 0);
  395. }
  396. return obj;
  397. }
  398. [MonoTODO]
  399. public XmlReader ExecuteXmlReader () {
  400. throw new NotImplementedException ();
  401. }
  402. [MonoTODO]
  403. public void Prepare () {
  404. // FIXME: parameters have to be implemented for this
  405. throw new NotImplementedException ();
  406. }
  407. [MonoTODO]
  408. public SqlCommand Clone () {
  409. throw new NotImplementedException ();
  410. }
  411. #endregion // Methods
  412. #region Properties
  413. public string CommandText {
  414. get {
  415. return sql;
  416. }
  417. set {
  418. sql = value;
  419. }
  420. }
  421. public int CommandTimeout {
  422. get {
  423. return timeout;
  424. }
  425. set {
  426. // FIXME: if value < 0, throw
  427. // ArgumentException
  428. // if (value < 0)
  429. // throw ArgumentException;
  430. timeout = value;
  431. }
  432. }
  433. public CommandType CommandType {
  434. get {
  435. return cmdType;
  436. }
  437. set {
  438. cmdType = value;
  439. }
  440. }
  441. // FIXME: for property Connection, is this the correct
  442. // way to handle a return of a stronger type?
  443. IDbConnection IDbCommand.Connection {
  444. get {
  445. return Connection;
  446. }
  447. set {
  448. // FIXME: throw an InvalidOperationException
  449. // if the change was during a
  450. // transaction in progress
  451. // csc
  452. Connection = (SqlConnection) value;
  453. // mcs
  454. // Connection = value;
  455. // FIXME: set Transaction property to null
  456. }
  457. }
  458. public SqlConnection Connection {
  459. get {
  460. // conn defaults to null
  461. return conn;
  462. }
  463. set {
  464. // FIXME: throw an InvalidOperationException
  465. // if the change was during
  466. // a transaction in progress
  467. conn = value;
  468. // FIXME: set Transaction property to null
  469. }
  470. }
  471. public bool DesignTimeVisible {
  472. get {
  473. return designTime;
  474. }
  475. set{
  476. designTime = value;
  477. }
  478. }
  479. // FIXME; for property Parameters, is this the correct
  480. // way to handle a stronger return type?
  481. IDataParameterCollection IDbCommand.Parameters {
  482. get {
  483. return Parameters;
  484. }
  485. }
  486. public SqlParameterCollection Parameters {
  487. get {
  488. return parmCollection;
  489. }
  490. }
  491. // FIXME: for property Transaction, is this the correct
  492. // way to handle a return of a stronger type?
  493. IDbTransaction IDbCommand.Transaction {
  494. get {
  495. return Transaction;
  496. }
  497. set {
  498. // FIXME: error handling - do not allow
  499. // setting of transaction if transaction
  500. // has already begun
  501. // csc
  502. Transaction = (SqlTransaction) value;
  503. // mcs
  504. // Transaction = value;
  505. }
  506. }
  507. public SqlTransaction Transaction {
  508. get {
  509. return trans;
  510. }
  511. set {
  512. // FIXME: error handling
  513. trans = value;
  514. }
  515. }
  516. [MonoTODO]
  517. public UpdateRowSource UpdatedRowSource {
  518. // FIXME: do this once DbDataAdaptor
  519. // and DataRow are done
  520. get {
  521. throw new NotImplementedException ();
  522. }
  523. set {
  524. throw new NotImplementedException ();
  525. }
  526. }
  527. #endregion // Properties
  528. #region Inner Classes
  529. #endregion // Inner Classes
  530. #region Destructors
  531. [MonoTODO]
  532. public void Dispose() {
  533. // FIXME: need proper way to release resources
  534. // Dispose(true);
  535. }
  536. [MonoTODO]
  537. ~SqlCommand() {
  538. // FIXME: need proper way to release resources
  539. // Dispose(false);
  540. }
  541. #endregion //Destructors
  542. }
  543. // SqlResult is used for passing Result Set data
  544. // from SqlCommand to SqlDataReader
  545. internal class SqlResult {
  546. private DataTable dataTableSchema = null; // only will contain the schema
  547. private IntPtr pg_result = IntPtr.Zero; // native PostgreSQL PGresult
  548. private int rowCount = 0;
  549. private int fieldCount = 0;
  550. private string[] pgtypes = null; // PostgreSQL types (typname)
  551. private bool resultReturned = false;
  552. private SqlConnection con = null;
  553. private int rowsAffected = -1;
  554. private ExecStatusType execStatus = ExecStatusType.PGRES_FATAL_ERROR;
  555. private int currentQuery = -1;
  556. private string sql = "";
  557. private CommandBehavior cmdBehavior = CommandBehavior.Default;
  558. internal CommandBehavior Behavior {
  559. get {
  560. return cmdBehavior;
  561. }
  562. set {
  563. cmdBehavior = value;
  564. }
  565. }
  566. internal string SQL {
  567. get {
  568. return sql;
  569. }
  570. set {
  571. sql = value;
  572. }
  573. }
  574. internal ExecStatusType ExecStatus {
  575. get {
  576. return execStatus;
  577. }
  578. set {
  579. execStatus = value;
  580. }
  581. }
  582. internal int CurrentQuery {
  583. get {
  584. return currentQuery;
  585. }
  586. set {
  587. currentQuery = value;
  588. }
  589. }
  590. internal SqlConnection Connection {
  591. get {
  592. return con;
  593. }
  594. set {
  595. con = value;
  596. }
  597. }
  598. internal int RecordsAffected {
  599. get {
  600. return rowsAffected;
  601. }
  602. }
  603. internal bool ResultReturned {
  604. get {
  605. return resultReturned;
  606. }
  607. set {
  608. resultReturned = value;
  609. }
  610. }
  611. internal DataTable Table {
  612. get {
  613. return dataTableSchema;
  614. }
  615. }
  616. internal IntPtr PgResult {
  617. get {
  618. return pg_result;
  619. }
  620. }
  621. internal int RowCount {
  622. get {
  623. return rowCount;
  624. }
  625. }
  626. internal int FieldCount {
  627. get {
  628. return fieldCount;
  629. }
  630. }
  631. internal string[] PgTypes {
  632. get {
  633. return pgtypes;
  634. }
  635. }
  636. internal void BuildTableSchema (IntPtr pgResult) {
  637. pg_result = pgResult;
  638. // need to set IDataReader.RecordsAffected property
  639. string rowsAffectedString;
  640. rowsAffectedString = PostgresLibrary.
  641. PQcmdTuples (pgResult);
  642. if(rowsAffectedString != null)
  643. if(rowsAffectedString.Equals("") == false)
  644. rowsAffected = int.Parse(rowsAffectedString);
  645. // Only Results from SQL SELECT Queries
  646. // get a DataTable for schema of the result
  647. // otherwise, DataTable is null reference
  648. if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
  649. dataTableSchema = new DataTable ();
  650. dataTableSchema.Columns.Add ("ColumnName", typeof (string));
  651. dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
  652. dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
  653. dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
  654. dataTableSchema.Columns.Add ("NumericScale", typeof (int));
  655. dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
  656. dataTableSchema.Columns.Add ("IsKey", typeof (bool));
  657. DataColumn dc = dataTableSchema.Columns["IsKey"];
  658. dc.AllowDBNull = true; // IsKey can have a DBNull
  659. dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
  660. dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
  661. dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
  662. dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
  663. dataTableSchema.Columns.Add ("DataType", typeof(string));
  664. dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
  665. dataTableSchema.Columns.Add ("ProviderType", typeof (int));
  666. dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
  667. dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
  668. dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
  669. dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
  670. dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
  671. dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
  672. dataTableSchema.Columns.Add ("IsLong", typeof (bool));
  673. dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
  674. fieldCount = PostgresLibrary.PQnfields (pgResult);
  675. rowCount = PostgresLibrary.PQntuples(pgResult);
  676. pgtypes = new string[fieldCount];
  677. // TODO: for CommandBehavior.SingleRow
  678. // use IRow, otherwise, IRowset
  679. if(fieldCount > 0)
  680. if((cmdBehavior & CommandBehavior.SingleRow) == CommandBehavior.SingleRow)
  681. fieldCount = 1;
  682. // TODO: for CommandBehavior.SchemaInfo
  683. if((cmdBehavior & CommandBehavior.SchemaOnly) == CommandBehavior.SchemaOnly)
  684. fieldCount = 0;
  685. // TODO: for CommandBehavior.SingleResult
  686. if((cmdBehavior & CommandBehavior.SingleResult) == CommandBehavior.SingleResult)
  687. if(currentQuery > 0)
  688. fieldCount = 0;
  689. // TODO: for CommandBehavior.SequentialAccess - used for reading Large OBjects
  690. //if((cmdBehavior & CommandBehavior.SequentialAccess) == CommandBehavior.SequentialAccess) {
  691. //}
  692. DataRow schemaRow;
  693. int oid;
  694. DbType dbType;
  695. Type typ;
  696. for (int i = 0; i < fieldCount; i += 1 ) {
  697. schemaRow = dataTableSchema.NewRow ();
  698. string columnName = PostgresLibrary.PQfname (pgResult, i);
  699. schemaRow["ColumnName"] = columnName;
  700. schemaRow["ColumnOrdinal"] = i+1;
  701. schemaRow["ColumnSize"] = PostgresLibrary.PQfsize (pgResult, i);
  702. schemaRow["NumericPrecision"] = 0;
  703. schemaRow["NumericScale"] = 0;
  704. // TODO: need to get KeyInfo
  705. if((cmdBehavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) {
  706. bool IsUnique, IsKey;
  707. GetKeyInfo(columnName, out IsUnique, out IsKey);
  708. }
  709. else {
  710. schemaRow["IsUnique"] = false;
  711. schemaRow["IsKey"] = DBNull.Value;
  712. }
  713. schemaRow["BaseCatalogName"] = "";
  714. schemaRow["BaseColumnName"] = columnName;
  715. schemaRow["BaseSchemaName"] = "";
  716. schemaRow["BaseTableName"] = "";
  717. // PostgreSQL type to .NET type stuff
  718. oid = PostgresLibrary.PQftype (pgResult, i);
  719. pgtypes[i] = PostgresHelper.OidToTypname (oid, con.Types);
  720. dbType = PostgresHelper.TypnameToSqlDbType (pgtypes[i]);
  721. typ = PostgresHelper.DbTypeToSystemType (dbType);
  722. string st = typ.ToString();
  723. schemaRow["DataType"] = st;
  724. schemaRow["AllowDBNull"] = false;
  725. schemaRow["ProviderType"] = oid;
  726. schemaRow["IsAliased"] = false;
  727. schemaRow["IsExpression"] = false;
  728. schemaRow["IsIdentity"] = false;
  729. schemaRow["IsAutoIncrement"] = false;
  730. schemaRow["IsRowVersion"] = false;
  731. schemaRow["IsHidden"] = false;
  732. schemaRow["IsLong"] = false;
  733. schemaRow["IsReadOnly"] = false;
  734. schemaRow.AcceptChanges();
  735. dataTableSchema.Rows.Add (schemaRow);
  736. }
  737. #if DEBUG_SqlCommand
  738. Console.WriteLine("********** DEBUG Table Schema BEGIN ************");
  739. foreach (DataRow myRow in dataTableSchema.Rows) {
  740. foreach (DataColumn myCol in dataTableSchema.Columns)
  741. Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);
  742. Console.WriteLine();
  743. }
  744. Console.WriteLine("********** DEBUG Table Schema END ************");
  745. #endif // DEBUG_SqlCommand
  746. }
  747. }
  748. // TODO: how do we get the key info if
  749. // we don't have the tableName?
  750. private void GetKeyInfo(string columnName, out bool isUnique, out bool isKey) {
  751. isUnique = false;
  752. isKey = false;
  753. string sql;
  754. sql =
  755. "SELECT i.indkey, i.indisprimary, i.indisunique " +
  756. "FROM pg_class c, pg_class c2, pg_index i " +
  757. "WHERE c.relname = ':tableName' AND c.oid = i.indrelid " +
  758. "AND i.indexrelid = c2.oid ";
  759. }
  760. }
  761. }