SqlConnection.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. //
  2. // System.Data.SqlClient.SqlConnection.cs
  3. //
  4. // Author:
  5. // Rodrigo Moya ([email protected])
  6. // Daniel Morgan ([email protected])
  7. //
  8. // (C) Ximian, Inc 2002
  9. //
  10. // use #define DEBUG_SqlConnection if you want to spew debug messages
  11. // #define DEBUG_SqlConnection
  12. using System;
  13. using System.ComponentModel;
  14. using System.Data;
  15. using System.Data.Common;
  16. using System.Runtime.InteropServices;
  17. using System.Text;
  18. namespace System.Data.SqlClient
  19. {
  20. // using PGconn = IntPtr;
  21. // PGconn is native C library type in libpq for Postgres Connection
  22. // using PGressult = IntPtr;
  23. // PGresult is native C library type in libpq for Postgres Resultset
  24. /// <summary>
  25. /// Represents an open connection to a SQL data source
  26. /// </summary>
  27. //public sealed class SqlConnection : Component, IDbConnection,
  28. // ICloneable
  29. public sealed class SqlConnection : IDbConnection
  30. {
  31. // FIXME: Need to implement class Component,
  32. // and interfaces: ICloneable and IDisposable
  33. #region Fields
  34. private IntPtr pgConn = IntPtr.Zero;
  35. // PGConn (Postgres Connection)
  36. private string connectionString = "";
  37. // OLE DB Connection String
  38. private string pgConnectionString = "";
  39. // PostgreSQL Connection String
  40. private SqlTransaction trans = null;
  41. private int connectionTimeout = 15;
  42. // default for 15 seconds
  43. // connection parameters in connection string
  44. private string host = "";
  45. // Name of host to connect to
  46. private string hostaddr = "";
  47. // IP address of host to connect to
  48. // should be in "n.n.n.n" format
  49. private string port = "";
  50. // Port number to connect to at the server host
  51. private string dbname = ""; // The database name.
  52. private string user = ""; // User name to connect as.
  53. private string password = "";
  54. // Password to be used if the server
  55. // demands password authentication.
  56. private string options = "";
  57. // Trace/debug options to be sent to the server.
  58. private string tty = "";
  59. // A file or tty for optional
  60. // debug output from the backend.
  61. private string requiressl = "";
  62. // Set to 1 to require
  63. // SSL connection to the backend.
  64. // Libpq will then refuse to connect
  65. // if the server does not
  66. // support SSL. Set to 0 (default) to
  67. // negotiate with server.
  68. private ConnectionState conState = ConnectionState.Closed;
  69. private bool dataReaderOpen = false;
  70. // FIXME: if true, throw an exception if SqlConnection
  71. // is used used for anything other than reading
  72. // data using SqlDataReader
  73. #endregion // Fields
  74. #region Constructors
  75. /*
  76. [MonoTODO]
  77. public SqlConnection ()
  78. {
  79. this.ConnectionString = null;
  80. this.ConnectionTimeout = 0;
  81. this.Database = null;
  82. this.State = 0;
  83. }
  84. [MonoTODO]
  85. public SqlConnection (string cs) : SqlConnection ()
  86. {
  87. this.ConnectionString = cs;
  88. }
  89. */
  90. // A lot of the defaults were initialized in the Fields
  91. [MonoTODO]
  92. public SqlConnection ()
  93. {
  94. }
  95. [MonoTODO]
  96. public SqlConnection (String connectionString)
  97. {
  98. SetConnectionString (connectionString);
  99. }
  100. #endregion // Constructors
  101. #region Destructors
  102. [MonoTODO]
  103. public void Dispose () {
  104. // FIXME: release resources properly
  105. Close ();
  106. // Dispose (true);
  107. }
  108. // aka Finalize
  109. // [ClassInterface(ClassInterfaceType.AutoDual)]
  110. [MonoTODO]
  111. ~SqlConnection()
  112. {
  113. // FIXME: this class need
  114. // a destructor to release resources
  115. // Also, take a look at Dispose
  116. // Dispose (false);
  117. }
  118. #endregion // Destructors
  119. #region Public Methods
  120. IDbTransaction IDbConnection.BeginTransaction ()
  121. {
  122. return BeginTransaction ();
  123. }
  124. public SqlTransaction BeginTransaction ()
  125. {
  126. return TransactionBegin (); // call private method
  127. }
  128. IDbTransaction IDbConnection.BeginTransaction (IsolationLevel
  129. il)
  130. {
  131. return BeginTransaction (il);
  132. }
  133. public SqlTransaction BeginTransaction (IsolationLevel il)
  134. {
  135. return TransactionBegin (il); // call private method
  136. }
  137. // PostgreSQL does not support named transactions/savepoint
  138. // nor nested transactions
  139. [Obsolete]
  140. public SqlTransaction BeginTransaction(string transactionName) {
  141. return TransactionBegin (); // call private method
  142. }
  143. [Obsolete]
  144. public SqlTransaction BeginTransaction(IsolationLevel iso,
  145. string transactionName) {
  146. return TransactionBegin (iso); // call private method
  147. }
  148. [MonoTODO]
  149. public void ChangeDatabase (string databaseName)
  150. {
  151. throw new NotImplementedException ();
  152. }
  153. [MonoTODO]
  154. public void Close ()
  155. {
  156. CloseDataSource ();
  157. }
  158. IDbCommand IDbConnection.CreateCommand ()
  159. {
  160. return CreateCommand ();
  161. }
  162. public SqlCommand CreateCommand ()
  163. {
  164. SqlCommand sqlcmd = new SqlCommand ("", this);
  165. return sqlcmd;
  166. }
  167. [MonoTODO]
  168. public void Open ()
  169. {
  170. OpenDataSource ();
  171. }
  172. #endregion // Public Methods
  173. #region Internal Methods
  174. // this is for System.Data.SqlClient classes
  175. // to get the Postgres connection
  176. internal IntPtr PostgresConnection {
  177. get {
  178. return pgConn;
  179. }
  180. }
  181. #endregion // Internal Methods
  182. #region Protected Methods
  183. // FIXME: protected override void Dispose overrides Component
  184. // however, including Component causes other problems
  185. /*
  186. [MonoTODO]
  187. protected override void Dispose (bool disposing)
  188. {
  189. throw new NotImplementedException ();
  190. }
  191. */
  192. #endregion
  193. #region Private Methods
  194. private void OpenDataSource ()
  195. {
  196. if(dbname.Equals(""))
  197. throw new InvalidOperationException(
  198. "dbname missing");
  199. else if(conState == ConnectionState.Open)
  200. throw new InvalidOperationException(
  201. "ConnnectionState is already Open");
  202. ConnStatusType connStatus;
  203. // FIXME: check to make sure we have
  204. // everything to connect,
  205. // otherwise, throw an exception
  206. pgConn = PostgresLibrary.PQconnectdb
  207. (pgConnectionString);
  208. // FIXME: should we use PQconnectStart/PQconnectPoll
  209. // instead of PQconnectdb?
  210. // PQconnectdb blocks
  211. // PQconnectStart/PQconnectPoll is non-blocking
  212. connStatus = PostgresLibrary.PQstatus (pgConn);
  213. if(connStatus == ConnStatusType.CONNECTION_OK)
  214. {
  215. // Successfully Connected
  216. conState = ConnectionState.Open;
  217. }
  218. else
  219. {
  220. String errorMessage = PostgresLibrary.
  221. PQerrorMessage (pgConn);
  222. errorMessage += ": Could not connect to database.";
  223. throw new SqlException(0, 0,
  224. errorMessage, 0, "",
  225. host, "SqlConnection", 0);
  226. }
  227. }
  228. private void CloseDataSource ()
  229. {
  230. // FIXME: just a quick hack
  231. conState = ConnectionState.Closed;
  232. PostgresLibrary.PQfinish (pgConn);
  233. }
  234. private void SetConnectionString (string connectionString)
  235. {
  236. // FIXME: perform error checking on string
  237. // while translating string from
  238. // OLE DB format to PostgreSQL
  239. // connection string format
  240. //
  241. // OLE DB: "host=localhost;dbname=test;user=joe;password=smoe"
  242. // PostgreSQL: "host=localhost dbname=test user=joe password=smoe"
  243. //
  244. // For OLE DB, you would have the additional
  245. // "provider=postgresql"
  246. // OleDbConnection you would be using libgda, maybe
  247. // it would be
  248. // "provider=OAFIID:GNOME_Database_Postgres_Provider"
  249. // instead.
  250. //
  251. // Also, parse the connection string into properties
  252. // FIXME: if connection is open, you can
  253. // not set the connection
  254. // string, throw an exception
  255. this.connectionString = connectionString;
  256. pgConnectionString = ConvertStringToPostgres (
  257. connectionString);
  258. #if DEBUG_SqlConnection
  259. Console.WriteLine(
  260. "OLE-DB Connection String [in]: " +
  261. this.ConnectionString);
  262. Console.WriteLine(
  263. "Postgres Connection String [out]: " +
  264. pgConnectionString);
  265. #endif // DEBUG_SqlConnection
  266. }
  267. private String ConvertStringToPostgres (String
  268. oleDbConnectionString)
  269. {
  270. StringBuilder postgresConnection =
  271. new StringBuilder();
  272. string result;
  273. string[] connectionParameters;
  274. char[] semicolon = new Char[1];
  275. semicolon[0] = ';';
  276. // FIXME: what is the max number of value pairs
  277. // can there be for the OLE DB
  278. // connnection string? what about libgda max?
  279. // what about postgres max?
  280. // FIXME: currently assuming value pairs are like:
  281. // "key1=value1;key2=value2;key3=value3"
  282. // Need to deal with values that have
  283. // single or double quotes. And error
  284. // handling of that too.
  285. // "key1=value1;key2='value2';key=\"value3\""
  286. // FIXME: put the connection parameters
  287. // from the connection
  288. // string into a
  289. // Hashtable (System.Collections)
  290. // instead of using private variables
  291. // to store them
  292. connectionParameters = oleDbConnectionString.
  293. Split (semicolon);
  294. foreach (string sParameter in connectionParameters) {
  295. if(sParameter.Length > 0) {
  296. BreakConnectionParameter (sParameter);
  297. postgresConnection.
  298. Append (sParameter +
  299. " ");
  300. }
  301. }
  302. result = postgresConnection.ToString ();
  303. return result;
  304. }
  305. private bool BreakConnectionParameter (String sParameter)
  306. {
  307. bool addParm = true;
  308. int index;
  309. index = sParameter.IndexOf ("=");
  310. if (index > 0) {
  311. string parmKey, parmValue;
  312. // separate string "key=value" to
  313. // string "key" and "value"
  314. parmKey = sParameter.Substring (0, index);
  315. parmValue = sParameter.Substring (index + 1,
  316. sParameter.Length - index - 1);
  317. switch(parmKey.ToLower()) {
  318. case "hostaddr":
  319. hostaddr = parmValue;
  320. break;
  321. case "port":
  322. port = parmValue;
  323. break;
  324. case "host":
  325. // set DataSource property
  326. host = parmValue;
  327. break;
  328. case "dbname":
  329. // set Database property
  330. dbname = parmValue;
  331. break;
  332. case "user":
  333. user = parmValue;
  334. break;
  335. case "password":
  336. password = parmValue;
  337. // addParm = false;
  338. break;
  339. case "options":
  340. options = parmValue;
  341. break;
  342. case "tty":
  343. tty = parmValue;
  344. break;
  345. case "requiressl":
  346. requiressl = parmValue;
  347. break;
  348. }
  349. }
  350. return addParm;
  351. }
  352. private SqlTransaction TransactionBegin ()
  353. {
  354. // FIXME: need to keep track of
  355. // transaction in-progress
  356. trans = new SqlTransaction ();
  357. // using internal methods of SqlTransaction
  358. trans.SetConnection (this);
  359. trans.Begin();
  360. return trans;
  361. }
  362. private SqlTransaction TransactionBegin (IsolationLevel il)
  363. {
  364. // FIXME: need to keep track of
  365. // transaction in-progress
  366. TransactionBegin();
  367. trans.SetIsolationLevel (il);
  368. return trans;
  369. }
  370. #endregion
  371. #region Properties
  372. [MonoTODO]
  373. public ConnectionState State {
  374. get {
  375. return conState;
  376. }
  377. }
  378. public string ConnectionString {
  379. get {
  380. return connectionString;
  381. }
  382. set {
  383. SetConnectionString (value);
  384. }
  385. }
  386. public int ConnectionTimeout {
  387. get {
  388. return connectionTimeout;
  389. }
  390. }
  391. public string Database {
  392. get {
  393. return dbname;
  394. }
  395. }
  396. public string DataSource {
  397. get {
  398. return host;
  399. }
  400. }
  401. /*
  402. * FIXME: this is here because of Component?
  403. [MonoTODO]
  404. protected bool DesignMode {
  405. get {
  406. throw new NotImplementedException ();
  407. }
  408. }
  409. */
  410. public int PacketSize {
  411. get {
  412. throw new NotImplementedException ();
  413. }
  414. }
  415. public string ServerVersion {
  416. get {
  417. throw new NotImplementedException ();
  418. }
  419. }
  420. internal SqlTransaction Transaction {
  421. get {
  422. return trans;
  423. }
  424. }
  425. #endregion
  426. #region Events and Delegates
  427. // FIXME: the two events belong here
  428. // however, i do not know about the delegates
  429. // also, they are stubs for now
  430. /*
  431. public delegate void
  432. SqlInfoMessageEventHandler (object sender,
  433. SqlInfoMessageEventArgs e);
  434. public event
  435. SqlInfoMessageEventHandler InfoMessage;
  436. public event
  437. StateChangeEventHandler StateChange;
  438. */
  439. #endregion
  440. }
  441. }