SqliteCommand reorganization and other changes.
[mono.git] / mcs / class / Mono.Data.SqliteClient / Mono.Data.SqliteClient / SqliteConnection.cs
1 //
2 // Mono.Data.SqliteClient.SqliteConnection.cs
3 //
4 // Represents an open connection to a Sqlite database file.
5 //
6 // Author(s): Vladimir Vukicevic  <vladimir@pobox.com>
7 //            Everaldo Canuto  <everaldo_canuto@yahoo.com.br>
8 //
9 // Copyright (C) 2002  Vladimir Vukicevic
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Runtime.InteropServices;
33 using System.Data;
34
35 namespace Mono.Data.SqliteClient
36 {
37         public class SqliteConnection : IDbConnection
38         {
39
40                 #region Fields
41                 
42                 private string conn_str;
43                 private string db_file;
44                 private int db_mode;
45                 private int db_version;
46                 private IntPtr sqlite_handle;
47                 private ConnectionState state;
48                 
49                 #endregion
50
51                 #region Constructors and destructors
52                 
53                 public SqliteConnection ()
54                 {
55                         db_file = null;
56                         db_mode = 0644;
57                         db_version = 2;
58                         state = ConnectionState.Closed;
59                         sqlite_handle = IntPtr.Zero;
60                 }
61                 
62                 public SqliteConnection (string connstring) : this ()
63                 {
64                         ConnectionString = connstring;
65                 }
66                 
67                 public void Dispose ()
68                 {
69                         Close ();
70                 }
71                                 
72                 #endregion
73
74                 #region Properties
75                 
76                 public string ConnectionString {
77                         get { return conn_str; }
78                         set { SetConnectionString(value); }
79                 }
80                 
81                 public int ConnectionTimeout {
82                         get { return 0; }
83                 }
84                 
85                 public string Database {
86                         get { return db_file; }
87                 }
88                 
89                 public ConnectionState State {
90                         get { return state; }
91                 }
92                 
93                 internal int Version {
94                         get { return db_version; }
95                 }
96
97                 internal IntPtr Handle {
98                         get { return sqlite_handle; }
99                 }
100                 
101                 public int LastInsertRowId {
102                         get {
103                                 if (Version == 3)
104                                         return Sqlite.sqlite3_last_insert_rowid (Handle);
105                                 else
106                                         return Sqlite.sqlite_last_insert_rowid (Handle);
107                         }
108                 }
109                 
110                 #endregion
111
112                 #region Private Methods
113                 
114                 private void SetConnectionString(string connstring)
115                 {
116                         if (connstring == null) {
117                                 Close ();
118                                 conn_str = null;
119                                 return;
120                         }
121                         
122                         if (connstring != conn_str) {
123                                 Close ();
124                                 conn_str = connstring;
125                                 
126                                 db_file = null;
127                                 db_mode = 0644;
128                                 
129                                 string[] conn_pieces = connstring.Split (',');
130                                 for (int i = 0; i < conn_pieces.Length; i++) {
131                                         string piece = conn_pieces [i].Trim ();
132                                         if (piece.Length == 0) { // ignore empty elements
133                                                 continue;
134                                         }
135                                         string[] arg_pieces = piece.Split ('=');
136                                         if (arg_pieces.Length != 2) {
137                                                 throw new InvalidOperationException ("Invalid connection string");
138                                         }
139                                         string token = arg_pieces[0].ToLower ().Trim ();
140                                         string tvalue = arg_pieces[1].Trim ();
141                                         string tvalue_lc = arg_pieces[1].ToLower ().Trim ();
142                                         if (token == "uri") {
143                                                 if (tvalue_lc.StartsWith ("file://")) {
144                                                         db_file = tvalue.Substring (7);
145                                                 } else if (tvalue_lc.StartsWith ("file:")) {
146                                                         db_file = tvalue.Substring (5);
147                                                 } else if (tvalue_lc.StartsWith ("/")) {
148                                                         db_file = tvalue;
149                                                 } else {
150                                                         throw new InvalidOperationException ("Invalid connection string: invalid URI");
151                                                 }
152                                         } else if (token == "mode") {
153                                                 db_mode = Convert.ToInt32 (tvalue);
154                                         } else if (token == "version") {
155                                                 db_version = Convert.ToInt32 (tvalue);
156                                         }
157                                 }
158                                 
159                                 if (db_file == null) {
160                                         throw new InvalidOperationException ("Invalid connection string: no URI");
161                                 }
162                         }
163                 }
164                 
165                 #endregion
166
167                 #region Internal Methods
168                 
169                 internal void StartExec ()
170                 {
171                         // use a mutex here
172                         state = ConnectionState.Executing;
173                 }
174                 
175                 internal void EndExec ()
176                 {
177                         state = ConnectionState.Open;
178                 }
179                 
180                 #endregion
181
182                 #region Public Methods
183                 
184                 public IDbTransaction BeginTransaction ()
185                 {
186                         if (state != ConnectionState.Open)
187                                 throw new InvalidOperationException("Invalid operation: The connection is closed");
188                         
189                         SqliteTransaction t = new SqliteTransaction();
190                         t.Connection = this;
191                         SqliteCommand cmd = this.CreateCommand();
192                         cmd.CommandText = "BEGIN";
193                         cmd.ExecuteNonQuery();
194                         return t;
195                 }
196                 
197                 public IDbTransaction BeginTransaction (IsolationLevel il)
198                 {
199                         throw new InvalidOperationException();
200                 }
201                 
202                 public void Close ()
203                 {
204                         if (state != ConnectionState.Open) {
205                                 return;
206                         }
207                         
208                         state = ConnectionState.Closed;
209                 
210                         if (Version == 3)
211                                 Sqlite.sqlite3_close (sqlite_handle);
212                         else 
213                                 Sqlite.sqlite_close(sqlite_handle);
214                         sqlite_handle = IntPtr.Zero;
215                 }
216                 
217                 public void ChangeDatabase (string databaseName)
218                 {
219                         throw new NotImplementedException ();
220                 }
221                 
222                 IDbCommand IDbConnection.CreateCommand ()
223                 {
224                         return CreateCommand ();
225                 }
226                 
227                 public SqliteCommand CreateCommand ()
228                 {
229                         return new SqliteCommand (null, this);
230                 }
231                 
232                 public void Open ()
233                 {
234                         if (conn_str == null) {
235                                 throw new InvalidOperationException ("No database specified");
236                         }
237                         
238                         if (state != ConnectionState.Closed) {
239                                 return;
240                         }
241                         
242                         IntPtr errmsg = IntPtr.Zero;
243
244                         if (Version == 2){
245                                 try {
246                                         sqlite_handle = Sqlite.sqlite_open(db_file, db_mode, out errmsg);
247                                         if (errmsg != IntPtr.Zero) {
248                                                 string msg = Marshal.PtrToStringAnsi (errmsg);
249                                                 Sqlite.sqliteFree (errmsg);
250                                                 throw new ApplicationException (msg);
251                                         }
252                                 } catch (DllNotFoundException dll) {
253                                         db_version = 3;
254                                 }
255                         }
256                         if (Version == 3) {
257                                 int err = Sqlite.sqlite3_open(db_file, out sqlite_handle);
258                                 if (err == (int)SqliteError.ERROR)
259                                         throw new ApplicationException (Marshal.PtrToStringAnsi( Sqlite.sqlite3_errmsg (sqlite_handle)));
260                         } else {
261                         }
262                         state = ConnectionState.Open;
263                 }
264                 
265                 #endregion
266
267         }
268 }