| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702 |
- //
- // System.Data.SqlClient.SqlConnection.cs
- //
- // Author:
- // Rodrigo Moya ([email protected])
- // Daniel Morgan ([email protected])
- //
- // (C) Ximian, Inc 2002
- // (C) Daniel Morgan 2002
- //
- // Credits:
- // SQL and concepts were used from libgda 0.8.190 (GNOME Data Access)
- // http://www.gnome-db.org/
- // with permission from the authors of the
- // PostgreSQL provider in libgda:
- // Michael Lausch <[email protected]>
- // Rodrigo Moya <[email protected]>
- // Vivien Malerba <[email protected]>
- // Gonzalo Paniagua Javier <[email protected]>
- //
- // use #define DEBUG_SqlConnection if you want to spew debug messages
- // #define DEBUG_SqlConnection
- using System;
- using System.Collections;
- using System.ComponentModel;
- using System.Data;
- using System.Data.Common;
- using System.Runtime.InteropServices;
- using System.Text;
- namespace System.Data.SqlClient {
- // using PGconn = IntPtr;
- // PGconn is native C library type in libpq for Postgres Connection
- // using PGressult = IntPtr;
- // PGresult is native C library type in libpq for Postgres Resultset
- /// <summary>
- /// Represents an open connection to a SQL data source
- /// </summary>
- //public sealed class SqlConnection : Component, IDbConnection,
- // ICloneable
- public sealed class SqlConnection : IDbConnection, IDisposable
- {
- // FIXME: Need to implement class Component,
- // and interfaces: ICloneable and IDisposable
- #region Fields
- private PostgresTypes types;
- private IntPtr pgConn = IntPtr.Zero;
- // PGConn (Postgres Connection)
- private string connectionString = "";
- // OLE DB Connection String
- private string pgConnectionString = "";
- // PostgreSQL Connection String
- private SqlTransaction trans = null;
- private int connectionTimeout = 15;
- // default for 15 seconds
-
- // connection parameters in connection string
- private string host = "";
- // Name of host to connect to
- private string hostaddr = "";
- // IP address of host to connect to
- // should be in "n.n.n.n" format
- private string port = "";
- // Port number to connect to at the server host
- private string dbname = ""; // The database name.
- private string user = ""; // User name to connect as.
- private string password = "";
- // Password to be used if the server
- // demands password authentication.
- private string options = "";
- // Trace/debug options to be sent to the server.
- private string tty = "";
- // A file or tty for optional
- // debug output from the backend.
- private string requiressl = "";
- // Set to 1 to require
- // SSL connection to the backend.
- // Libpq will then refuse to connect
- // if the server does not
- // support SSL. Set to 0 (default) to
- // negotiate with server.
- private ConnectionState conState = ConnectionState.Closed;
- private bool dataReaderOpen = false;
- // FIXME: if true, throw an exception if SqlConnection
- // is used for anything other than reading
- // data using SqlDataReader
-
- private string versionString = "Unknown";
- #endregion // Fields
- #region Constructors
- /*
- [MonoTODO]
- public SqlConnection ()
- {
- this.ConnectionString = null;
- this.ConnectionTimeout = 0;
- this.Database = null;
- this.State = 0;
- }
-
- [MonoTODO]
- public SqlConnection (string cs) : SqlConnection ()
- {
- this.ConnectionString = cs;
- }
-
- */
- // A lot of the defaults were initialized in the Fields
- [MonoTODO]
- public SqlConnection () {
- }
-
- [MonoTODO]
- public SqlConnection (String connectionString) {
- SetConnectionString (connectionString);
- }
- #endregion // Constructors
- #region Destructors
- [MonoTODO]
- public void Dispose () {
- // FIXME: release resources properly
- Close ();
- // Dispose (true);
- }
-
- // aka Finalize
- // [ClassInterface(ClassInterfaceType.AutoDual)]
- [MonoTODO]
- ~SqlConnection() {
- // FIXME: this class need
- // a destructor to release resources
- // Also, take a look at Dispose
- // Dispose (false);
- }
-
- #endregion // Destructors
- #region Public Methods
- IDbTransaction IDbConnection.BeginTransaction () {
- return BeginTransaction ();
- }
- public SqlTransaction BeginTransaction () {
- return TransactionBegin (); // call private method
- }
- IDbTransaction IDbConnection.BeginTransaction (IsolationLevel
- il) {
- return BeginTransaction (il);
- }
- public SqlTransaction BeginTransaction (IsolationLevel il) {
- return TransactionBegin (il); // call private method
- }
- // PostgreSQL does not support named transactions/savepoint
- // nor nested transactions
- [Obsolete]
- public SqlTransaction BeginTransaction(string transactionName) {
- return TransactionBegin (); // call private method
- }
- [Obsolete]
- public SqlTransaction BeginTransaction(IsolationLevel iso,
- string transactionName) {
- return TransactionBegin (iso); // call private method
- }
- [MonoTODO]
- public void ChangeDatabase (string databaseName) {
- throw new NotImplementedException ();
- }
-
- [MonoTODO]
- public void Close () {
- CloseDataSource ();
- }
- IDbCommand IDbConnection.CreateCommand () {
- return CreateCommand ();
- }
- public SqlCommand CreateCommand () {
- SqlCommand sqlcmd = new SqlCommand ("", this);
- return sqlcmd;
- }
- [MonoTODO]
- public void Open () {
- if(dbname.Equals(""))
- throw new InvalidOperationException(
- "dbname missing");
- else if(conState == ConnectionState.Open)
- throw new InvalidOperationException(
- "ConnnectionState is already Open");
- ConnStatusType connStatus;
- // FIXME: check to make sure we have
- // everything to connect,
- // otherwise, throw an exception
- pgConn = PostgresLibrary.PQconnectdb
- (pgConnectionString);
- // FIXME: should we use PQconnectStart/PQconnectPoll
- // instead of PQconnectdb?
- // PQconnectdb blocks
- // PQconnectStart/PQconnectPoll is non-blocking
-
- connStatus = PostgresLibrary.PQstatus (pgConn);
- if(connStatus == ConnStatusType.CONNECTION_OK) {
- // Successfully Connected
- SetupConnection();
- }
- else {
- String errorMessage = PostgresLibrary.
- PQerrorMessage (pgConn);
- errorMessage += ": Could not connect to database.";
- throw new SqlException(0, 0,
- errorMessage, 0, "",
- host, "SqlConnection", 0);
- }
- }
- #endregion // Public Methods
- #region Protected Methods
- // FIXME: protected override void Dispose overrides Component
- // however, including Component causes other problems
- /*
- [MonoTODO]
- protected override void Dispose (bool disposing)
- {
- throw new NotImplementedException ();
- }
- */
- #endregion
- #region Private Methods
- private void SetupConnection() {
- conState = ConnectionState.Open;
- // FIXME: load types into hashtable
- types = new PostgresTypes(this);
- types.Load();
- versionString = GetDatabaseServerVersion();
- // set DATE style to YYYY/MM/DD
- IntPtr pgResult = IntPtr.Zero;
- pgResult = PostgresLibrary.PQexec (pgConn, "SET DATESTYLE TO 'ISO'");
- PostgresLibrary.PQclear (pgResult);
- pgResult = IntPtr.Zero;
- }
- private string GetDatabaseServerVersion()
- {
- SqlCommand cmd = new SqlCommand("select version()",this);
- return (string) cmd.ExecuteScalar();
- }
- private void CloseDataSource () {
- // FIXME: just a quick hack
- conState = ConnectionState.Closed;
- PostgresLibrary.PQfinish (pgConn);
- pgConn = IntPtr.Zero;
- }
- private void SetConnectionString (string connectionString) {
- // FIXME: perform error checking on string
- // while translating string from
- // OLE DB format to PostgreSQL
- // connection string format
- //
- // OLE DB: "host=localhost;dbname=test;user=joe;password=smoe"
- // PostgreSQL: "host=localhost dbname=test user=joe password=smoe"
- //
- // For OLE DB, you would have the additional
- // "provider=postgresql"
- // OleDbConnection you would be using libgda, maybe
- // it would be
- // "provider=OAFIID:GNOME_Database_Postgres_Provider"
- // instead.
- //
- // Also, parse the connection string into properties
- // FIXME: if connection is open, you can
- // not set the connection
- // string, throw an exception
- this.connectionString = connectionString;
- pgConnectionString = ConvertStringToPostgres (
- connectionString);
- #if DEBUG_SqlConnection
- Console.WriteLine(
- "OLE-DB Connection String [in]: " +
- this.ConnectionString);
- Console.WriteLine(
- "Postgres Connection String [out]: " +
- pgConnectionString);
- #endif // DEBUG_SqlConnection
- }
- private String ConvertStringToPostgres (String
- oleDbConnectionString) {
- StringBuilder postgresConnection =
- new StringBuilder();
- string result;
- string[] connectionParameters;
- char[] semicolon = new Char[1];
- semicolon[0] = ';';
-
- // FIXME: what is the max number of value pairs
- // can there be for the OLE DB
- // connnection string? what about libgda max?
- // what about postgres max?
- // FIXME: currently assuming value pairs are like:
- // "key1=value1;key2=value2;key3=value3"
- // Need to deal with values that have
- // single or double quotes. And error
- // handling of that too.
- // "key1=value1;key2='value2';key=\"value3\""
- // FIXME: put the connection parameters
- // from the connection
- // string into a
- // Hashtable (System.Collections)
- // instead of using private variables
- // to store them
- connectionParameters = oleDbConnectionString.
- Split (semicolon);
- foreach (string sParameter in connectionParameters) {
- if(sParameter.Length > 0) {
- BreakConnectionParameter (sParameter);
- postgresConnection.
- Append (sParameter +
- " ");
- }
- }
- result = postgresConnection.ToString ();
- return result;
- }
- private bool BreakConnectionParameter (String sParameter) {
- bool addParm = true;
- int index;
- index = sParameter.IndexOf ("=");
- if (index > 0) {
- string parmKey, parmValue;
- // separate string "key=value" to
- // string "key" and "value"
- parmKey = sParameter.Substring (0, index);
- parmValue = sParameter.Substring (index + 1,
- sParameter.Length - index - 1);
- switch(parmKey.ToLower()) {
- case "hostaddr":
- hostaddr = parmValue;
- break;
- case "port":
- port = parmValue;
- break;
- case "host":
- // set DataSource property
- host = parmValue;
- break;
- case "dbname":
- // set Database property
- dbname = parmValue;
- break;
- case "user":
- user = parmValue;
- break;
- case "password":
- password = parmValue;
- // addParm = false;
- break;
- case "options":
- options = parmValue;
- break;
- case "tty":
- tty = parmValue;
- break;
-
- case "requiressl":
- requiressl = parmValue;
- break;
- }
- }
- return addParm;
- }
- private SqlTransaction TransactionBegin () {
- // FIXME: need to keep track of
- // transaction in-progress
- trans = new SqlTransaction ();
- // using internal methods of SqlTransaction
- trans.SetConnection (this);
- trans.Begin();
- return trans;
- }
- private SqlTransaction TransactionBegin (IsolationLevel il) {
- // FIXME: need to keep track of
- // transaction in-progress
- TransactionBegin();
- trans.SetIsolationLevel (il);
-
- return trans;
- }
- #endregion
- #region Public Properties
- [MonoTODO]
- public ConnectionState State {
- get {
- return conState;
- }
- }
- public string ConnectionString {
- get {
- return connectionString;
- }
- set {
- SetConnectionString (value);
- }
- }
-
- public int ConnectionTimeout {
- get {
- return connectionTimeout;
- }
- }
- public string Database {
- get {
- return dbname;
- }
- }
- public string DataSource {
- get {
- return host;
- }
- }
- /*
- * FIXME: this is here because of Component?
- [MonoTODO]
- protected bool DesignMode {
- get {
- throw new NotImplementedException ();
- }
- }
- */
- public int PacketSize {
- get {
- throw new NotImplementedException ();
- }
- }
- public string ServerVersion {
- get {
- return versionString;
- }
- }
- #endregion // Public Properties
- #region Internal Properties
- // For System.Data.SqlClient classes
- // to get the current transaction
- // in progress - if any
- internal SqlTransaction Transaction {
- get {
- return trans;
- }
- }
- // For System.Data.SqlClient classes
- // to get the unmanaged PostgreSQL connection
- internal IntPtr PostgresConnection {
- get {
- return pgConn;
- }
- }
- // For System.Data.SqlClient classes
- // to get the list PostgreSQL types
- // so can look up based on OID to
- // get the .NET System type.
- internal ArrayList Types {
- get {
- return types.List;
- }
- }
- // Used to prevent SqlConnection
- // from doing anything while
- // SqlDataReader is open
- internal bool OpenReader {
- get {
- return dataReaderOpen;
- }
- set {
- dataReaderOpen = value;
- }
- }
- #endregion // Internal Properties
- #region Events
-
- public event
- SqlInfoMessageEventHandler InfoMessage;
- public event
- StateChangeEventHandler StateChange;
-
- #endregion
- #region Inner Classes
- private class PostgresTypes {
- // TODO: create hashtable for
- // PostgreSQL types to .NET types
- // containing: oid, typname, SqlDbType
- private Hashtable hashTypes;
- private ArrayList pgTypes;
- private SqlConnection con;
- // Got this SQL with the permission from
- // the authors of libgda
- private const string SEL_SQL_GetTypes =
- "SELECT oid, typname FROM pg_type " +
- "WHERE typrelid = 0 AND typname !~ '^_' " +
- " AND typname not in ('SET', 'cid', " +
- "'int2vector', 'oidvector', 'regproc', " +
- "'smgr', 'tid', 'unknown', 'xid') " +
- "ORDER BY typname";
- internal PostgresTypes(SqlConnection sqlcon) {
-
- con = sqlcon;
- hashTypes = new Hashtable();
- }
- private void AddPgType(Hashtable types,
- string typname, DbType dbType) {
- PostgresType pgType = new PostgresType();
-
- pgType.typname = typname;
- pgType.dbType = dbType;
- types.Add(pgType.typname, pgType);
- }
- private void BuildTypes(IntPtr pgResult,
- int nRows, int nFields) {
- String value;
- int r;
- for(r = 0; r < nRows; r++) {
- PostgresType pgType =
- new PostgresType();
- // get data value (oid)
- value = PostgresLibrary.
- PQgetvalue(
- pgResult,
- r, 0);
-
- pgType.oid = Int32.Parse(value);
- // get data value (typname)
- value = PostgresLibrary.
- PQgetvalue(
- pgResult,
- r, 1);
- pgType.typname = String.Copy(value);
- pgType.dbType = PostgresHelper.
- TypnameToSqlDbType(
- pgType.typname);
- pgTypes.Add(pgType);
- }
- pgTypes = ArrayList.ReadOnly(pgTypes);
- }
- internal void Load() {
- pgTypes = new ArrayList();
- IntPtr pgResult = IntPtr.Zero; // PGresult
-
- if(con.State != ConnectionState.Open)
- throw new InvalidOperationException(
- "ConnnectionState is not Open");
- // FIXME: PQexec blocks
- // while PQsendQuery is non-blocking
- // which is better to use?
- // int PQsendQuery(PGconn *conn,
- // const char *query);
- // execute SQL command
- // uses internal property to get the PGConn IntPtr
- pgResult = PostgresLibrary.
- PQexec (con.PostgresConnection, SEL_SQL_GetTypes);
- if(pgResult.Equals(IntPtr.Zero)) {
- throw new SqlException(0, 0,
- "No Resultset from PostgreSQL", 0, "",
- con.DataSource, "SqlConnection", 0);
- }
- else {
- ExecStatusType execStatus;
- execStatus = PostgresLibrary.
- PQresultStatus (pgResult);
-
- if(execStatus == ExecStatusType.PGRES_TUPLES_OK) {
- int nRows;
- int nFields;
- nRows = PostgresLibrary.
- PQntuples(pgResult);
- nFields = PostgresLibrary.
- PQnfields(pgResult);
- BuildTypes (pgResult, nRows, nFields);
- }
- else {
- String errorMessage;
-
- errorMessage = PostgresLibrary.
- PQresStatus(execStatus);
- errorMessage += " " + PostgresLibrary.
- PQresultErrorMessage(pgResult);
-
- throw new SqlException(0, 0,
- errorMessage, 0, "",
- con.DataSource, "SqlConnection", 0);
- }
- // close result set
- PostgresLibrary.PQclear (pgResult);
- pgResult = IntPtr.Zero;
- }
- }
- public ArrayList List {
- get {
- return pgTypes;
- }
- }
- }
- #endregion
- }
- }
|