SqlConnection.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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. 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(
  194. "dbname missing");
  195. else if(conState == ConnectionState.Open)
  196. throw new InvalidOperationException(
  197. "ConnnectionState is already Open");
  198. ConnStatusType connStatus;
  199. // FIXME: check to make sure we have
  200. // everything to connect,
  201. // otherwise, throw an exception
  202. pgConn = PostgresLibrary.PQconnectdb
  203. (pgConnectionString);
  204. // FIXME: should we use PQconnectStart/PQconnectPoll
  205. // instead of PQconnectdb?
  206. // PQconnectdb blocks
  207. // PQconnectStart/PQconnectPoll is non-blocking
  208. connStatus = PostgresLibrary.PQstatus (pgConn);
  209. if(connStatus == ConnStatusType.CONNECTION_OK)
  210. {
  211. // Successfully Connected
  212. conState = ConnectionState.Open;
  213. }
  214. else
  215. {
  216. String errorMessage = PostgresLibrary.
  217. PQerrorMessage (pgConn);
  218. errorMessage += ": Could not connect to database.";
  219. throw new SqlException(0, 0,
  220. errorMessage, 0, "",
  221. host, "SqlConnection", 0);
  222. }
  223. }
  224. private void CloseDataSource ()
  225. {
  226. // FIXME: just a quick hack
  227. conState = ConnectionState.Closed;
  228. PostgresLibrary.PQfinish (pgConn);
  229. }
  230. private void SetConnectionString (string connectionString)
  231. {
  232. // FIXME: perform error checking on string
  233. // while translating string from
  234. // OLE DB format to PostgreSQL
  235. // connection string format
  236. //
  237. // OLE DB: "host=localhost;dbname=test;user=joe;password=smoe"
  238. // PostgreSQL: "host=localhost dbname=test user=joe password=smoe"
  239. //
  240. // For OLE DB, you would have the additional
  241. // "provider=postgresql"
  242. // OleDbConnection you would be using libgda, maybe
  243. // it would be
  244. // "provider=OAFIID:GNOME_Database_Postgres_Provider"
  245. // instead.
  246. //
  247. // Also, parse the connection string into properties
  248. // FIXME: if connection is open, you can
  249. // not set the connection
  250. // string, throw an exception
  251. this.connectionString = connectionString;
  252. pgConnectionString = ConvertStringToPostgres (
  253. connectionString);
  254. #if DEBUG_SqlConnection
  255. Console.WriteLine(
  256. "OLE-DB Connection String [in]: " +
  257. this.ConnectionString);
  258. Console.WriteLine(
  259. "Postgres Connection String [out]: " +
  260. pgConnectionString);
  261. #endif // DEBUG_SqlConnection
  262. }
  263. private String ConvertStringToPostgres (String
  264. oleDbConnectionString)
  265. {
  266. StringBuilder postgresConnection =
  267. new StringBuilder();
  268. string result;
  269. string[] connectionParameters;
  270. char[] semicolon = new Char[1];
  271. semicolon[0] = ';';
  272. // FIXME: what is the max number of value pairs
  273. // can there be for the OLE DB
  274. // connnection string? what about libgda max?
  275. // what about postgres max?
  276. // FIXME: currently assuming value pairs are like:
  277. // "key1=value1;key2=value2;key3=value3"
  278. // Need to deal with values that have
  279. // single or double quotes. And error
  280. // handling of that too.
  281. // "key1=value1;key2='value2';key=\"value3\""
  282. // FIXME: put the connection parameters
  283. // from the connection
  284. // string into a
  285. // Hashtable (System.Collections)
  286. // instead of using private variables
  287. // to store them
  288. connectionParameters = oleDbConnectionString.
  289. Split (semicolon);
  290. foreach (string sParameter in connectionParameters) {
  291. if(sParameter.Length > 0) {
  292. BreakConnectionParameter (sParameter);
  293. postgresConnection.
  294. Append (sParameter +
  295. " ");
  296. }
  297. }
  298. result = postgresConnection.ToString ();
  299. return result;
  300. }
  301. private bool BreakConnectionParameter (String sParameter)
  302. {
  303. bool addParm = true;
  304. int index;
  305. index = sParameter.IndexOf ("=");
  306. if (index > 0) {
  307. string parmKey, parmValue;
  308. // separate string "key=value" to
  309. // string "key" and "value"
  310. parmKey = sParameter.Substring (0, index);
  311. parmValue = sParameter.Substring (index + 1,
  312. sParameter.Length - index - 1);
  313. switch(parmKey.ToLower()) {
  314. case "hostaddr":
  315. hostaddr = parmValue;
  316. break;
  317. case "port":
  318. port = parmValue;
  319. break;
  320. case "host":
  321. // set DataSource property
  322. host = parmValue;
  323. break;
  324. case "dbname":
  325. // set Database property
  326. dbname = parmValue;
  327. break;
  328. case "user":
  329. user = parmValue;
  330. break;
  331. case "password":
  332. password = parmValue;
  333. // addParm = false;
  334. break;
  335. case "options":
  336. options = parmValue;
  337. break;
  338. case "tty":
  339. tty = parmValue;
  340. break;
  341. case "requiressl":
  342. requiressl = parmValue;
  343. break;
  344. }
  345. }
  346. return addParm;
  347. }
  348. private SqlTransaction TransactionBegin ()
  349. {
  350. // FIXME: need to keep track of
  351. // transaction in-progress
  352. trans = new SqlTransaction ();
  353. // using internal methods of SqlTransaction
  354. trans.SetConnection (this);
  355. trans.Begin();
  356. return trans;
  357. }
  358. private SqlTransaction TransactionBegin (IsolationLevel il)
  359. {
  360. // FIXME: need to keep track of
  361. // transaction in-progress
  362. TransactionBegin();
  363. trans.SetIsolationLevel (il);
  364. return trans;
  365. }
  366. #endregion
  367. #region Properties
  368. [MonoTODO]
  369. public ConnectionState State {
  370. get {
  371. return conState;
  372. }
  373. }
  374. public string ConnectionString {
  375. get {
  376. return connectionString;
  377. }
  378. set {
  379. SetConnectionString (value);
  380. }
  381. }
  382. public int ConnectionTimeout {
  383. get {
  384. return connectionTimeout;
  385. }
  386. }
  387. public string Database {
  388. get {
  389. return dbname;
  390. }
  391. }
  392. public string DataSource {
  393. get {
  394. return host;
  395. }
  396. }
  397. /*
  398. * FIXME: this is here because of Component?
  399. [MonoTODO]
  400. protected bool DesignMode {
  401. get {
  402. throw new NotImplementedException ();
  403. }
  404. }
  405. */
  406. public int PacketSize {
  407. get {
  408. throw new NotImplementedException ();
  409. }
  410. }
  411. public string ServerVersion {
  412. get {
  413. throw new NotImplementedException ();
  414. }
  415. }
  416. internal SqlTransaction Transaction {
  417. get {
  418. return trans;
  419. }
  420. }
  421. #endregion
  422. #region Events and Delegates
  423. // FIXME: the two events belong here
  424. // however, i do not know about the delegates
  425. // also, they are stubs for now
  426. /*
  427. public delegate void
  428. SqlInfoMessageEventHandler (object sender,
  429. SqlInfoMessageEventArgs e);
  430. public event
  431. SqlInfoMessageEventHandler InfoMessage;
  432. public event
  433. StateChangeEventHandler StateChange;
  434. */
  435. #endregion
  436. }
  437. }