New test.
[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 using System.Text;
35
36 namespace Mono.Data.SqliteClient
37 {
38         public class SqliteConnection : IDbConnection
39         {
40
41                 #region Fields
42                 
43                 private string conn_str;
44                 private string db_file;
45                 private int db_mode;
46                 private int db_version;
47                 private IntPtr sqlite_handle;
48                 private ConnectionState state;
49                 private Encoding encoding;
50                 private int busy_timeout;
51
52                 #endregion
53
54                 #region Constructors and destructors
55                 
56                 public SqliteConnection ()
57                 {
58                         db_file = null;
59                         db_mode = 0644;
60                         db_version = 2;
61                         state = ConnectionState.Closed;
62                         sqlite_handle = IntPtr.Zero;
63                         encoding = null;
64                         busy_timeout = 0;
65                 }
66                 
67                 public SqliteConnection (string connstring) : this ()
68                 {
69                         ConnectionString = connstring;
70                 }
71                 
72                 public void Dispose ()
73                 {
74                         Close ();
75                 }
76                                 
77                 #endregion
78
79                 #region Properties
80                 
81                 public string ConnectionString {
82                         get { return conn_str; }
83                         set { SetConnectionString(value); }
84                 }
85                 
86                 public int ConnectionTimeout {
87                         get { return 0; }
88                 }
89                 
90                 public string Database {
91                         get { return db_file; }
92                 }
93                 
94                 public ConnectionState State {
95                         get { return state; }
96                 }
97                 
98                 public Encoding Encoding {
99                         get { return encoding; }
100                 }
101
102                 public int Version {
103                         get { return db_version; }
104                 }
105
106                 internal IntPtr Handle {
107                         get { return sqlite_handle; }
108                 }
109                 
110                 public int LastInsertRowId {
111                         get {
112                                 if (Version == 3)
113                                         return (int)Sqlite.sqlite3_last_insert_rowid (Handle);
114                                 else
115                                         return Sqlite.sqlite_last_insert_rowid (Handle);
116                         }
117                 }
118
119                 public int BusyTimeout {
120                         get {
121                                 return busy_timeout;  
122                         }
123                         set {
124                                 busy_timeout = value < 0 ? 0 : value;
125                         }
126                 }
127                 
128                 #endregion
129
130                 #region Private Methods
131                 
132                 private void SetConnectionString(string connstring)
133                 {
134                         if (connstring == null) {
135                                 Close ();
136                                 conn_str = null;
137                                 return;
138                         }
139                         
140                         if (connstring != conn_str) {
141                                 Close ();
142                                 conn_str = connstring;
143                                 
144                                 db_file = null;
145                                 db_mode = 0644;
146                                 
147                                 string[] conn_pieces = connstring.Split (',');
148                                 for (int i = 0; i < conn_pieces.Length; i++) {
149                                         string piece = conn_pieces [i].Trim ();
150                                         if (piece.Length == 0) { // ignore empty elements
151                                                 continue;
152                                         }
153                                         string[] arg_pieces = piece.Split ('=');
154                                         if (arg_pieces.Length != 2) {
155                                                 throw new InvalidOperationException ("Invalid connection string");
156                                         }
157                                         string token = arg_pieces[0].ToLower (System.Globalization.CultureInfo.InvariantCulture).Trim ();
158                                         string tvalue = arg_pieces[1].Trim ();
159                                         string tvalue_lc = arg_pieces[1].ToLower (System.Globalization.CultureInfo.InvariantCulture).Trim ();
160                                         switch (token) {
161                                         case "uri": 
162                                                 if (tvalue_lc.StartsWith ("file://")) {
163                                                         db_file = tvalue.Substring (7);
164                                                 } else if (tvalue_lc.StartsWith ("file:")) {
165                                                         db_file = tvalue.Substring (5);
166                                                 } else if (tvalue_lc.StartsWith ("/")) {
167                                                         db_file = tvalue;
168                                                 } else {
169                                                         throw new InvalidOperationException ("Invalid connection string: invalid URI");
170                                                 }
171                                                 break;
172
173                                         case "mode": 
174                                                 db_mode = Convert.ToInt32 (tvalue);
175                                                 break;
176
177                                         case "version":
178                                                 db_version = Convert.ToInt32 (tvalue);
179                                                 break;
180
181                                         case "encoding": // only for sqlite2
182                                                 encoding = Encoding.GetEncoding (tvalue);
183                                                 break;
184
185                                         case "busy_timeout":
186                                                 busy_timeout = Convert.ToInt32 (tvalue);
187                                                 break;
188                                         }
189                                 }
190                                 
191                                 if (db_file == null) {
192                                         throw new InvalidOperationException ("Invalid connection string: no URI");
193                                 }
194                         }
195                 }
196                 
197                 #endregion
198
199                 #region Internal Methods
200                 
201                 internal void StartExec ()
202                 {
203                         // use a mutex here
204                         state = ConnectionState.Executing;
205                 }
206                 
207                 internal void EndExec ()
208                 {
209                         state = ConnectionState.Open;
210                 }
211                 
212                 #endregion
213
214                 #region Public Methods
215                 
216                 public IDbTransaction BeginTransaction ()
217                 {
218                         if (state != ConnectionState.Open)
219                                 throw new InvalidOperationException("Invalid operation: The connection is closed");
220                         
221                         SqliteTransaction t = new SqliteTransaction();
222                         t.Connection = this;
223                         SqliteCommand cmd = this.CreateCommand();
224                         cmd.CommandText = "BEGIN";
225                         cmd.ExecuteNonQuery();
226                         return t;
227                 }
228                 
229                 public IDbTransaction BeginTransaction (IsolationLevel il)
230                 {
231                         throw new InvalidOperationException();
232                 }
233                 
234                 public void Close ()
235                 {
236                         if (state != ConnectionState.Open) {
237                                 return;
238                         }
239                         
240                         state = ConnectionState.Closed;
241                 
242                         if (Version == 3)
243                                 Sqlite.sqlite3_close (sqlite_handle);
244                         else 
245                                 Sqlite.sqlite_close(sqlite_handle);
246                         sqlite_handle = IntPtr.Zero;
247                 }
248                 
249                 public void ChangeDatabase (string databaseName)
250                 {
251                         throw new NotImplementedException ();
252                 }
253                 
254                 IDbCommand IDbConnection.CreateCommand ()
255                 {
256                         return CreateCommand ();
257                 }
258                 
259                 public SqliteCommand CreateCommand ()
260                 {
261                         return new SqliteCommand (null, this);
262                 }
263                 
264                 public void Open ()
265                 {
266                         if (conn_str == null) {
267                                 throw new InvalidOperationException ("No database specified");
268                         }
269                         
270                         if (state != ConnectionState.Closed) {
271                                 return;
272                         }
273                         
274                         IntPtr errmsg = IntPtr.Zero;
275
276                         if (Version == 2){
277                                 try {
278                                         sqlite_handle = Sqlite.sqlite_open(db_file, db_mode, out errmsg);
279                                         if (errmsg != IntPtr.Zero) {
280                                                 string msg = Marshal.PtrToStringAnsi (errmsg);
281                                                 Sqlite.sqliteFree (errmsg);
282                                                 throw new ApplicationException (msg);
283                                         }
284                                 } catch (DllNotFoundException dll) {
285                                         db_version = 3;
286                                 }
287                                 if (busy_timeout != 0)
288                                         Sqlite.sqlite_busy_timeout (sqlite_handle, busy_timeout);
289                         }
290                         if (Version == 3) {
291                                 int err = Sqlite.sqlite3_open16(db_file, out sqlite_handle);
292                                 if (err == (int)SqliteError.ERROR)
293                                         throw new ApplicationException (Marshal.PtrToStringUni( Sqlite.sqlite3_errmsg16 (sqlite_handle)));
294                                 if (busy_timeout != 0)
295                                         Sqlite.sqlite3_busy_timeout (sqlite_handle, busy_timeout);
296                         } else {
297                         }
298                         state = ConnectionState.Open;
299                 }
300                 
301                 #endregion
302
303         }
304 }