SqlConnection.cs 11 KB

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