Imported version 1.0.61.0 of SQLite.NET
[mono.git] / mcs / class / Mono.Data.Sqlite / Mono.Data.Sqlite_2.0 / SQLiteConnection.cs
1 /********************************************************\r
2  * ADO.NET 2.0 Data Provider for SQLite Version 3.X\r
3  * Written by Robert Simpson (robert@blackcastlesoft.com)\r
4  * \r
5  * Released to the public domain, use at your own risk!\r
6  ********************************************************/\r
7 \r
8 namespace System.Data.SQLite\r
9 {\r
10   using System;\r
11   using System.Data;\r
12   using System.Data.Common;\r
13   using System.Collections.Generic;\r
14   using System.Globalization;\r
15   using System.ComponentModel;\r
16   using System.Text;\r
17   using System.Runtime.InteropServices;\r
18   using System.IO;\r
19 \r
20   /// <summary>\r
21   /// SQLite implentation of DbConnection.\r
22   /// </summary>\r
23   /// <remarks>\r
24   /// The <see cref="ConnectionString">ConnectionString</see> property of the SQLiteConnection class can contain the following parameter(s), delimited with a semi-colon:\r
25   /// <list type="table">\r
26   /// <listheader>\r
27   /// <term>Parameter</term>\r
28   /// <term>Values</term>\r
29   /// <term>Required</term>\r
30   /// <term>Default</term>\r
31   /// </listheader>\r
32   /// <item>\r
33   /// <description>Data Source</description>\r
34   /// <description>{filename}</description>\r
35   /// <description>Y</description>\r
36   /// <description></description>\r
37   /// </item>\r
38   /// <item>\r
39   /// <description>Version</description>\r
40   /// <description>3</description>\r
41   /// <description>N</description>\r
42   /// <description>3</description>\r
43   /// </item>\r
44   /// <item>\r
45   /// <description>UseUTF16Encoding</description>\r
46   /// <description><b>True</b><br/><b>False</b></description>\r
47   /// <description>N</description>\r
48   /// <description>False</description>\r
49   /// </item>\r
50   /// <item>\r
51   /// <description>DateTimeFormat</description>\r
52   /// <description><b>Ticks</b> - Use DateTime.Ticks<br/><b>ISO8601</b> - Use ISO8601 DateTime format</description>\r
53   /// <description>N</description>\r
54   /// <description>ISO8601</description>\r
55   /// </item>\r
56   /// <item>\r
57   /// <description>BinaryGUID</description>\r
58   /// <description><b>True</b> - Store GUID columns in binary form<br/><b>False</b> - Store GUID columns as text</description>\r
59   /// <description>N</description>\r
60   /// <description>True</description>\r
61   /// </item>\r
62   /// <item>\r
63   /// <description>Cache Size</description>\r
64   /// <description>{size in bytes}</description>\r
65   /// <description>N</description>\r
66   /// <description>2000</description>\r
67   /// </item>\r
68   /// <item>\r
69   /// <description>Synchronous</description>\r
70   /// <description><b>Normal</b> - Normal file flushing behavior<br/><b>Full</b> - Full flushing after all writes<br/><b>Off</b> - Underlying OS flushes I/O's</description>\r
71   /// <description>N</description>\r
72   /// <description>Normal</description>\r
73   /// </item>\r
74   /// <item>\r
75   /// <description>Page Size</description>\r
76   /// <description>{size in bytes}</description>\r
77   /// <description>N</description>\r
78   /// <description>1024</description>\r
79   /// </item>\r
80   /// <item>\r
81   /// <description>Password</description>\r
82   /// <description>{password}</description>\r
83   /// <description>N</description>\r
84   /// <description></description>\r
85   /// </item>\r
86   /// <item>\r
87   /// <description>Enlist</description>\r
88   /// <description><b>Y</b> - Automatically enlist in distributed transactions<br/><b>N</b> - No automatic enlistment</description>\r
89   /// <description>N</description>\r
90   /// <description>Y</description>\r
91   /// </item>\r
92   /// <item>\r
93   /// <description>Pooling</description>\r
94   /// <description><b>True</b> - Use connection pooling<br/><b>False</b> - Do not use connection pooling</description>\r
95   /// <description>N</description>\r
96   /// <description>False</description>\r
97   /// </item>\r
98   /// <item>\r
99   /// <description>FailIfMissing</description>\r
100   /// <description><b>True</b> - Don't create the database if it does not exist, throw an error instead<br/><b>False</b> - Automatically create the database if it does not exist</description>\r
101   /// <description>N</description>\r
102   /// <description>False</description>\r
103   /// </item>\r
104   /// <item>\r
105   /// <description>Max Page Count</description>\r
106   /// <description>{size in pages} - Limits the maximum number of pages (limits the size) of the database</description>\r
107   /// <description>N</description>\r
108   /// <description>0</description>\r
109   /// </item>\r
110   /// <item>\r
111   /// <description>Legacy Format</description>\r
112   /// <description><b>True</b> - Use the more compatible legacy 3.x database format<br/><b>False</b> - Use the newer 3.3x database format which compresses numbers more effectively</description>\r
113   /// <description>N</description>\r
114   /// <description>False</description>\r
115   /// </item>\r
116   /// <item>\r
117   /// <description>Default Timeout</description>\r
118   /// <description>{time in seconds}<br/>The default command timeout</description>\r
119   /// <description>N</description>\r
120   /// <description>30</description>\r
121   /// </item>\r
122   /// <item>\r
123   /// <description>Journal Mode</description>\r
124   /// <description><b>Delete</b> - Delete the journal file after a commit<br/><b>Persist</b> - Zero out and leave the journal file on disk after a commit<br/><b>Off</b> - Disable the rollback journal entirely</description>\r
125   /// <description>N</description>\r
126   /// <description>Delete</description>\r
127   /// </item>\r
128   /// <item>\r
129   /// <description>Read Only</description>\r
130   /// <description><b>True</b> - Open the database for read only access<br/><b>False</b> - Open the database for normal read/write access</description>\r
131   /// <description>N</description>\r
132   /// <description>False</description>\r
133   /// </item>\r
134   /// <item>\r
135   /// <description>Max Pool Size</description>\r
136   /// <description>The maximum number of connections for the given connection string that can be in the connection pool</description>\r
137   /// <description>N</description>\r
138   /// <description>100</description>\r
139   /// </item>\r
140   /// <item>\r
141   /// <description>Default IsolationLevel</description>\r
142   /// <description>The default transaciton isolation level</description>\r
143   /// <description>N</description>\r
144   /// <description>Serializable</description>\r
145   /// </item>\r
146   /// </list>\r
147   /// </remarks>\r
148   public sealed partial class SQLiteConnection : DbConnection, ICloneable\r
149   {\r
150     private const string _dataDirectory = "|DataDirectory|";\r
151     private const string _masterdb = "sqlite_master";\r
152     private const string _tempmasterdb = "sqlite_temp_master";\r
153 \r
154     /// <summary>\r
155     /// State of the current connection\r
156     /// </summary>\r
157     private ConnectionState _connectionState;\r
158     /// <summary>\r
159     /// The connection string\r
160     /// </summary>\r
161     private string _connectionString;\r
162     /// <summary>\r
163     /// Nesting level of the transactions open on the connection\r
164     /// </summary>\r
165     internal int _transactionLevel;\r
166 \r
167     /// <summary>\r
168     /// The default isolation level for new transactions\r
169     /// </summary>\r
170     private IsolationLevel _defaultIsolation;\r
171 \r
172 #if !PLATFORM_COMPACTFRAMEWORK\r
173     /// <summary>\r
174     /// Whether or not the connection is enlisted in a distrubuted transaction\r
175     /// </summary>\r
176     internal SQLiteEnlistment _enlistment;\r
177 #endif\r
178     /// <summary>\r
179     /// The base SQLite object to interop with\r
180     /// </summary>\r
181     internal SQLiteBase _sql;\r
182     /// <summary>\r
183     /// The database filename minus path and extension\r
184     /// </summary>\r
185     private string _dataSource;\r
186     /// <summary>\r
187     /// Temporary password storage, emptied after the database has been opened\r
188     /// </summary>\r
189     private byte[] _password;\r
190 \r
191     /// <summary>\r
192     /// Default command timeout\r
193     /// </summary>\r
194     private int _defaultTimeout = 30;\r
195 \r
196     internal bool _binaryGuid;\r
197 \r
198     internal long _version;\r
199 \r
200     private event SQLiteUpdateEventHandler _updateHandler;\r
201     private event SQLiteCommitHandler _commitHandler;\r
202     private event EventHandler _rollbackHandler;\r
203 \r
204     private SQLiteUpdateCallback _updateCallback;\r
205     private SQLiteCommitCallback _commitCallback;\r
206     private SQLiteRollbackCallback _rollbackCallback;\r
207 \r
208     /// <summary>\r
209     /// This event is raised whenever the database is opened or closed.\r
210     /// </summary>\r
211     public override event StateChangeEventHandler StateChange;\r
212 \r
213     ///<overloads>\r
214     /// Constructs a new SQLiteConnection object\r
215     /// </overloads>\r
216     /// <summary>\r
217     /// Default constructor\r
218     /// </summary>\r
219     public SQLiteConnection()\r
220       : this("")\r
221     {\r
222     }\r
223 \r
224     /// <summary>\r
225     /// Initializes the connection with the specified connection string\r
226     /// </summary>\r
227     /// <param name="connectionString">The connection string to use on the connection</param>\r
228     public SQLiteConnection(string connectionString)\r
229     {\r
230       _sql = null;\r
231       _connectionState = ConnectionState.Closed;\r
232       _connectionString = "";\r
233       _transactionLevel = 0;\r
234       _version = 0;\r
235       //_commandList = new List<WeakReference>();\r
236 \r
237       if (connectionString != null)\r
238         ConnectionString = connectionString;\r
239     }\r
240 \r
241     /// <summary>\r
242     /// Clones the settings and connection string from an existing connection.  If the existing connection is already open, this\r
243     /// function will open its own connection, enumerate any attached databases of the original connection, and automatically\r
244     /// attach to them.\r
245     /// </summary>\r
246     /// <param name="connection"></param>\r
247     public SQLiteConnection(SQLiteConnection connection)\r
248       : this(connection.ConnectionString)\r
249     {\r
250       string str;\r
251 \r
252       if (connection.State == ConnectionState.Open)\r
253       {\r
254         Open();\r
255 \r
256         // Reattach all attached databases from the existing connection\r
257         using (DataTable tbl = connection.GetSchema("Catalogs"))\r
258         {\r
259           foreach (DataRow row in tbl.Rows)\r
260           {\r
261             str = row[0].ToString();\r
262             if (String.Compare(str, "main", true, CultureInfo.InvariantCulture) != 0\r
263               && String.Compare(str, "temp", true, CultureInfo.InvariantCulture) != 0)\r
264             {\r
265               using (SQLiteCommand cmd = CreateCommand())\r
266               {\r
267                 cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "ATTACH DATABASE '{0}' AS [{1}]", row[1], row[0]);\r
268                 cmd.ExecuteNonQuery();\r
269               }\r
270             }\r
271           }\r
272         }\r
273       }\r
274     }\r
275 \r
276 #if PLATFORM_COMPACTFRAMEWORK\r
277     /// <summary>\r
278     /// Obsolete\r
279     /// </summary>\r
280     public override int ConnectionTimeout\r
281     {\r
282       get\r
283       {\r
284         return 30;\r
285       }\r
286     }\r
287 #endif\r
288 \r
289     /// <summary>\r
290     /// Creates a clone of the connection.  All attached databases and user-defined functions are cloned.  If the existing connection is open, the cloned connection \r
291     /// will also be opened.\r
292     /// </summary>\r
293     /// <returns></returns>\r
294     public object Clone()\r
295     {\r
296       return new SQLiteConnection(this);\r
297     }\r
298 \r
299     /// <summary>\r
300     /// Disposes of the SQLiteConnection, closing it if it is active.\r
301     /// </summary>\r
302     /// <param name="disposing">True if the connection is being explicitly closed.</param>\r
303     protected override void Dispose(bool disposing)\r
304     {\r
305       base.Dispose(disposing);\r
306 \r
307       if (disposing)\r
308         Close();\r
309     }\r
310 \r
311     /// <summary>\r
312     /// Creates a database file.  This just creates a zero-byte file which SQLite\r
313     /// will turn into a database when the file is opened properly.\r
314     /// </summary>\r
315     /// <param name="databaseFileName">The file to create</param>\r
316     static public void CreateFile(string databaseFileName)\r
317     {\r
318       FileStream fs = File.Create(databaseFileName);\r
319       fs.Close();\r
320     }\r
321 \r
322 #if !SQLITE_STANDARD\r
323     /// <summary>\r
324     /// On NTFS volumes, this function turns on the compression attribute for the given file.\r
325     /// It must not be open or referenced at the time of the function call.\r
326     /// </summary>\r
327     /// <param name="databaseFileName">The file to compress</param>\r
328     [Obsolete("This functionality is being removed from a future version of the SQLite provider")]\r
329     static public void CompressFile(string databaseFileName)\r
330     {\r
331       UnsafeNativeMethods.sqlite3_compressfile(databaseFileName);\r
332     }\r
333 #endif\r
334 \r
335 #if !SQLITE_STANDARD\r
336     /// <summary>\r
337     /// On NTFS volumes, this function removes the compression attribute for the given file.\r
338     /// It must not be open or referenced at the time of the function call.\r
339     /// </summary>\r
340     /// <param name="databaseFileName">The file to decompress</param>\r
341     [Obsolete("This functionality is being removed from a future version of the SQLite provider")]\r
342     static public void DecompressFile(string databaseFileName)\r
343     {\r
344       UnsafeNativeMethods.sqlite3_decompressfile(databaseFileName);\r
345     }\r
346 #endif\r
347 \r
348     /// <summary>\r
349     /// Raises the state change event when the state of the connection changes\r
350     /// </summary>\r
351     /// <param name="newState">The new state.  If it is different from the previous state, an event is raised.</param>\r
352     internal void OnStateChange(ConnectionState newState)\r
353     {\r
354       ConnectionState oldState = _connectionState;\r
355       _connectionState = newState;\r
356 \r
357       if (StateChange != null && oldState != newState)\r
358       {\r
359         StateChangeEventArgs e = new StateChangeEventArgs(oldState, newState);\r
360         StateChange(this, e);\r
361       }\r
362     }\r
363 \r
364     /// <summary>\r
365     /// OBSOLETE.  Creates a new SQLiteTransaction if one isn't already active on the connection.\r
366     /// </summary>\r
367     /// <param name="isolationLevel">This parameter is ignored.</param>\r
368     /// <param name="deferredLock">When TRUE, SQLite defers obtaining a write lock until a write operation is requested.\r
369     /// When FALSE, a writelock is obtained immediately.  The default is TRUE, but in a multi-threaded multi-writer \r
370     /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.</param>\r
371     /// <returns>Returns a SQLiteTransaction object.</returns>\r
372     [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]\r
373     public SQLiteTransaction BeginTransaction(IsolationLevel isolationLevel, bool deferredLock)\r
374     {\r
375       return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted);\r
376     }\r
377 \r
378     /// <summary>\r
379     /// OBSOLETE.  Creates a new SQLiteTransaction if one isn't already active on the connection.\r
380     /// </summary>\r
381     /// <param name="deferredLock">When TRUE, SQLite defers obtaining a write lock until a write operation is requested.\r
382     /// When FALSE, a writelock is obtained immediately.  The default is false, but in a multi-threaded multi-writer \r
383     /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.</param>\r
384     /// <returns>Returns a SQLiteTransaction object.</returns>\r
385     [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]\r
386     public SQLiteTransaction BeginTransaction(bool deferredLock)\r
387     {\r
388       return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted);\r
389     }\r
390 \r
391     /// <summary>\r
392     /// Creates a new SQLiteTransaction if one isn't already active on the connection.\r
393     /// </summary>\r
394     /// <param name="isolationLevel">Supported isolation levels are Serializable, ReadCommitted and Unspecified.</param>\r
395     /// <remarks>\r
396     /// Unspecified will use the default isolation level specified in the connection string.  If no isolation level is specified in the \r
397     /// connection string, Serializable is used.\r
398     /// Serializable transactions are the default.  In this mode, the engine gets an immediate lock on the database, and no other threads\r
399     /// may begin a transaction.  Other threads may read from the database, but not write.\r
400     /// With a ReadCommitted isolation level, locks are deferred and elevated as needed.  It is possible for multiple threads to start\r
401     /// a transaction in ReadCommitted mode, but if a thread attempts to commit a transaction while another thread\r
402     /// has a ReadCommitted lock, it may timeout or cause a deadlock on both threads until both threads' CommandTimeout's are reached.\r
403     /// </remarks>\r
404     /// <returns>Returns a SQLiteTransaction object.</returns>\r
405     public new SQLiteTransaction BeginTransaction(IsolationLevel isolationLevel)\r
406     {\r
407       return (SQLiteTransaction)BeginDbTransaction(isolationLevel);\r
408     }\r
409 \r
410     /// <summary>\r
411     /// Creates a new SQLiteTransaction if one isn't already active on the connection.\r
412     /// </summary>\r
413     /// <returns>Returns a SQLiteTransaction object.</returns>\r
414     public new SQLiteTransaction BeginTransaction()\r
415     {\r
416       return (SQLiteTransaction)BeginDbTransaction(_defaultIsolation);\r
417     }\r
418 \r
419     /// <summary>\r
420     /// Forwards to the local BeginTransaction() function\r
421     /// </summary>\r
422     /// <param name="isolationLevel">Supported isolation levels are Unspecified, Serializable, and ReadCommitted</param>\r
423     /// <returns></returns>\r
424     protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)\r
425     {\r
426       if (_connectionState != ConnectionState.Open)\r
427         throw new InvalidOperationException();\r
428 \r
429       if (isolationLevel == IsolationLevel.Unspecified) isolationLevel = _defaultIsolation;\r
430 \r
431       if (isolationLevel != IsolationLevel.Serializable && isolationLevel != IsolationLevel.ReadCommitted)\r
432         throw new ArgumentException("isolationLevel");\r
433 \r
434       return new SQLiteTransaction(this, isolationLevel != IsolationLevel.Serializable);\r
435     }\r
436 \r
437     /// <summary>\r
438     /// Not implemented\r
439     /// </summary>\r
440     /// <param name="databaseName"></param>\r
441     public override void ChangeDatabase(string databaseName)\r
442     {\r
443       throw new NotImplementedException();\r
444     }\r
445 \r
446     /// <summary>\r
447     /// When the database connection is closed, all commands linked to this connection are automatically reset.\r
448     /// </summary>\r
449     public override void Close()\r
450     {\r
451       if (_sql != null)\r
452       {\r
453 #if !PLATFORM_COMPACTFRAMEWORK\r
454         if (_enlistment != null)\r
455         {\r
456           // If the connection is enlisted in a transaction scope and the scope is still active,\r
457           // we cannot truly shut down this connection until the scope has completed.  Therefore make a \r
458           // hidden connection temporarily to hold open the connection until the scope has completed.\r
459           SQLiteConnection cnn = new SQLiteConnection();\r
460           cnn._sql = _sql;\r
461           cnn._transactionLevel = _transactionLevel;\r
462           cnn._enlistment = _enlistment;\r
463           cnn._connectionState = _connectionState;\r
464           cnn._version = _version;\r
465 \r
466           cnn._enlistment._transaction._cnn = cnn;\r
467           cnn._enlistment._disposeConnection = true;\r
468           _sql = null;\r
469           _enlistment = null;\r
470         }\r
471 #endif\r
472         if (_sql != null)\r
473         {\r
474           _sql.Close();\r
475         }\r
476         _sql = null;\r
477         _transactionLevel = 0;\r
478       }\r
479       OnStateChange(ConnectionState.Closed);\r
480     }\r
481 \r
482     /// <summary>\r
483     /// Clears the connection pool associated with the connection.  Any other active connections using the same database file\r
484     /// will be discarded instead of returned to the pool when they are closed.\r
485     /// </summary>\r
486     /// <param name="connection"></param>\r
487     public static void ClearPool(SQLiteConnection connection)\r
488     {\r
489       if (connection._sql == null) return;\r
490       connection._sql.ClearPool();\r
491     }\r
492 \r
493     /// <summary>\r
494     /// Clears all connection pools.  Any active connections will be discarded instead of sent to the pool when they are closed.\r
495     /// </summary>\r
496     public static void ClearAllPools()\r
497     {\r
498       SQLiteConnectionPool.ClearAllPools();\r
499     }\r
500 \r
501     /// <summary>\r
502     /// The connection string containing the parameters for the connection\r
503     /// </summary>\r
504     /// <remarks>\r
505     /// <list type="table">\r
506     /// <listheader>\r
507     /// <term>Parameter</term>\r
508     /// <term>Values</term>\r
509     /// <term>Required</term>\r
510     /// <term>Default</term>\r
511     /// </listheader>\r
512     /// <item>\r
513     /// <description>Data Source</description>\r
514     /// <description>{filename}</description>\r
515     /// <description>Y</description>\r
516     /// <description></description>\r
517     /// </item>\r
518     /// <item>\r
519     /// <description>Version</description>\r
520     /// <description>3</description>\r
521     /// <description>N</description>\r
522     /// <description>3</description>\r
523     /// </item>\r
524     /// <item>\r
525     /// <description>UseUTF16Encoding</description>\r
526     /// <description><b>True</b><br/><b>False</b></description>\r
527     /// <description>N</description>\r
528     /// <description>False</description>\r
529     /// </item>\r
530     /// <item>\r
531     /// <description>DateTimeFormat</description>\r
532     /// <description><b>Ticks</b> - Use DateTime.Ticks<br/><b>ISO8601</b> - Use ISO8601 DateTime format<br/><b>JulianDay</b> - Use JulianDay format</description>\r
533     /// <description>N</description>\r
534     /// <description>ISO8601</description>\r
535     /// </item>\r
536     /// <item>\r
537     /// <description>BinaryGUID</description>\r
538     /// <description><b>Yes/On/1</b> - Store GUID columns in binary form<br/><b>No/Off/0</b> - Store GUID columns as text</description>\r
539     /// <description>N</description>\r
540     /// <description>On</description>\r
541     /// </item>\r
542     /// <item>\r
543     /// <description>Cache Size</description>\r
544     /// <description>{size in bytes}</description>\r
545     /// <description>N</description>\r
546     /// <description>2000</description>\r
547     /// </item>\r
548     /// <item>\r
549     /// <description>Synchronous</description>\r
550     /// <description><b>Normal</b> - Normal file flushing behavior<br/><b>Full</b> - Full flushing after all writes<br/><b>Off</b> - Underlying OS flushes I/O's</description>\r
551     /// <description>N</description>\r
552     /// <description>Normal</description>\r
553     /// </item>\r
554     /// <item>\r
555     /// <description>Page Size</description>\r
556     /// <description>{size in bytes}</description>\r
557     /// <description>N</description>\r
558     /// <description>1024</description>\r
559     /// </item>\r
560     /// <item>\r
561     /// <description>Password</description>\r
562     /// <description>{password}</description>\r
563     /// <description>N</description>\r
564     /// <description></description>\r
565     /// </item>\r
566     /// <item>\r
567     /// <description>Enlist</description>\r
568     /// <description><B>Y</B> - Automatically enlist in distributed transactions<br/><b>N</b> - No automatic enlistment</description>\r
569     /// <description>N</description>\r
570     /// <description>Y</description>\r
571     /// </item>\r
572     /// <item>\r
573     /// <description>Pooling</description>\r
574     /// <description><b>True</b> - Use connection pooling<br/><b>False</b> - Do not use connection pooling</description>\r
575     /// <description>N</description>\r
576     /// <description>False</description>\r
577     /// </item>\r
578     /// <item>\r
579     /// <description>FailIfMissing</description>\r
580     /// <description><b>True</b> - Don't create the database if it does not exist, throw an error instead<br/><b>False</b> - Automatically create the database if it does not exist</description>\r
581     /// <description>N</description>\r
582     /// <description>False</description>\r
583     /// </item>\r
584     /// <item>\r
585     /// <description>Max Page Count</description>\r
586     /// <description>{size in pages} - Limits the maximum number of pages (limits the size) of the database</description>\r
587     /// <description>N</description>\r
588     /// <description>0</description>\r
589     /// </item>\r
590     /// <item>\r
591     /// <description>Legacy Format</description>\r
592     /// <description><b>True</b> - Use the more compatible legacy 3.x database format<br/><b>False</b> - Use the newer 3.3x database format which compresses numbers more effectively</description>\r
593     /// <description>N</description>\r
594     /// <description>False</description>\r
595     /// </item>\r
596     /// <item>\r
597     /// <description>Default Timeout</description>\r
598     /// <description>{time in seconds}<br/>The default command timeout</description>\r
599     /// <description>N</description>\r
600     /// <description>30</description>\r
601     /// </item>\r
602     /// <item>\r
603     /// <description>Journal Mode</description>\r
604     /// <description><b>Delete</b> - Delete the journal file after a commit<br/><b>Persist</b> - Zero out and leave the journal file on disk after a commit<br/><b>Off</b> - Disable the rollback journal entirely</description>\r
605     /// <description>N</description>\r
606     /// <description>Delete</description>\r
607     /// </item>\r
608     /// <item>\r
609     /// <description>Read Only</description>\r
610     /// <description><b>True</b> - Open the database for read only access<br/><b>False</b> - Open the database for normal read/write access</description>\r
611     /// <description>N</description>\r
612     /// <description>False</description>\r
613     /// </item>\r
614     /// <item>\r
615     /// <description>Max Pool Size</description>\r
616     /// <description>The maximum number of connections for the given connection string that can be in the connection pool</description>\r
617     /// <description>N</description>\r
618     /// <description>100</description>\r
619     /// </item>\r
620     /// <item>\r
621     /// <description>Default IsolationLevel</description>\r
622     /// <description>The default transaciton isolation level</description>\r
623     /// <description>N</description>\r
624     /// <description>Serializable</description>\r
625     /// </item>\r
626     /// </list>\r
627     /// </remarks>\r
628 #if !PLATFORM_COMPACTFRAMEWORK\r
629     [RefreshProperties(RefreshProperties.All), DefaultValue("")]\r
630     [Editor("SQLite.Designer.SQLiteConnectionStringEditor, SQLite.Designer, Version=1.0.36.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]\r
631 #endif\r
632     public override string ConnectionString\r
633     {\r
634       get\r
635       {\r
636         return _connectionString;\r
637       }\r
638       set\r
639       {\r
640         if (value == null)\r
641           throw new ArgumentNullException();\r
642 \r
643         else if (_connectionState != ConnectionState.Closed)\r
644           throw new InvalidOperationException();\r
645 \r
646         _connectionString = value;\r
647       }\r
648     }\r
649 \r
650     /// <summary>\r
651     /// Create a new SQLiteCommand and associate it with this connection.\r
652     /// </summary>\r
653     /// <returns>Returns an instantiated SQLiteCommand object already assigned to this connection.</returns>\r
654     public new SQLiteCommand CreateCommand()\r
655     {\r
656       return new SQLiteCommand(this);\r
657     }\r
658 \r
659     /// <summary>\r
660     /// Forwards to the local CreateCommand() function\r
661     /// </summary>\r
662     /// <returns></returns>\r
663     protected override DbCommand CreateDbCommand()\r
664     {\r
665       return CreateCommand();\r
666     }\r
667 \r
668     /// <summary>\r
669     /// Returns the filename without extension or path\r
670     /// </summary>\r
671 #if !PLATFORM_COMPACTFRAMEWORK\r
672     [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]\r
673 #endif\r
674     public override string DataSource\r
675     {\r
676       get\r
677       {\r
678         return _dataSource;\r
679       }\r
680     }\r
681 \r
682     /// <summary>\r
683     /// Returns an empty string\r
684     /// </summary>\r
685 #if !PLATFORM_COMPACTFRAMEWORK\r
686     [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]\r
687 #endif\r
688     public override string Database\r
689     {\r
690       get\r
691       {\r
692         return "main";\r
693       }\r
694     }\r
695 \r
696     internal static string MapUriPath(string path)\r
697     {\r
698             if (path.StartsWith ("file://"))\r
699                     return path.Substring (7);\r
700       else if (path.StartsWith ("file:"))\r
701                     return path.Substring (5);\r
702       else if (path.StartsWith ("/"))\r
703                     return path;\r
704       else\r
705                     throw new InvalidOperationException ("Invalid connection string: invalid URI");\r
706     }\r
707     \r
708     /// <summary>\r
709     /// Parses the connection string into component parts\r
710     /// </summary>\r
711     /// <param name="connectionString">The connection string to parse</param>\r
712     /// <returns>An array of key-value pairs representing each parameter of the connection string</returns>\r
713     internal static SortedList<string, string> ParseConnectionString(string connectionString)\r
714     {\r
715       string s = connectionString;\r
716       int n;\r
717       SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase);\r
718 \r
719       // First split into semi-colon delimited values.  The Split() function of SQLiteBase accounts for and properly\r
720       // skips semi-colons in quoted strings\r
721       string[] arParts = SQLiteConvert.Split(s, ';');\r
722       string[] arPiece;\r
723 \r
724       int x = arParts.Length;\r
725       // For each semi-colon piece, split into key and value pairs by the presence of the = sign\r
726       for (n = 0; n < x; n++)\r
727       {\r
728         arPiece = SQLiteConvert.Split(arParts[n], '=');\r
729         if (arPiece.Length == 2)\r
730         {\r
731           ls.Add(arPiece[0], arPiece[1]);\r
732         }\r
733         else throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Invalid ConnectionString format for parameter \"{0}\"", (arPiece.Length > 0) ? arPiece[0] : "null"));\r
734       }\r
735       return ls;\r
736     }\r
737 \r
738 #if !PLATFORM_COMPACTFRAMEWORK\r
739     /// <summary>\r
740     /// Manual distributed transaction enlistment support\r
741     /// </summary>\r
742     /// <param name="transaction">The distributed transaction to enlist in</param>\r
743     public override void EnlistTransaction(System.Transactions.Transaction transaction)\r
744     {\r
745       if (_transactionLevel > 0 && transaction != null)\r
746         throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");\r
747 \r
748       if (_enlistment != null && transaction != _enlistment._scope)\r
749         throw new ArgumentException("Already enlisted in a transaction");\r
750 \r
751       _enlistment = new SQLiteEnlistment(this, transaction);\r
752     }\r
753 #endif\r
754 \r
755     /// <summary>\r
756     /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value\r
757     /// </summary>\r
758     /// <param name="items">The list to look in</param>\r
759     /// <param name="key">The key to find</param>\r
760     /// <param name="defValue">The default value to return if the key is not found</param>\r
761     /// <returns>The value corresponding to the specified key, or the default value if not found.</returns>\r
762     static internal string FindKey(SortedList<string, string> items, string key, string defValue)\r
763     {\r
764       string ret;\r
765 \r
766       if (items.TryGetValue(key, out ret)) return ret;\r
767 \r
768       return defValue;\r
769     }\r
770 \r
771     /// <summary>\r
772     /// Opens the connection using the parameters found in the <see cref="ConnectionString">ConnectionString</see>\r
773     /// </summary>\r
774     public override void Open()\r
775     {\r
776       if (_connectionState != ConnectionState.Closed)\r
777         throw new InvalidOperationException();\r
778 \r
779       Close();\r
780 \r
781       SortedList<string, string> opts = ParseConnectionString(_connectionString);\r
782       string fileName;\r
783 \r
784       if (Convert.ToInt32(FindKey(opts, "Version", "3"), CultureInfo.InvariantCulture) != 3)\r
785         throw new NotSupportedException("Only SQLite Version 3 is supported at this time");\r
786 \r
787       fileName = FindKey(opts, "Data Source", "");\r
788 \r
789       if (String.IsNullOrEmpty(fileName))\r
790       {\r
791         fileName = FindKey(opts, "Uri", "");\r
792         if (String.IsNullOrEmpty(fileName))\r
793           throw new ArgumentException("Data Source cannot be empty.  Use :memory: to open an in-memory database");\r
794         else\r
795           fileName = MapUriPath(fileName);\r
796       }\r
797 \r
798       if (String.Compare(fileName, ":MEMORY:", true, CultureInfo.InvariantCulture) == 0)\r
799         fileName = ":memory:";\r
800       else\r
801       {\r
802 #if PLATFORM_COMPACTFRAMEWORK\r
803        if (fileName.StartsWith(".\\"))\r
804          fileName = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase) + fileName.Substring(1);\r
805 #endif\r
806        fileName = ExpandFileName(fileName);\r
807       }\r
808       try\r
809       {\r
810         bool usePooling = (SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", Boolean.FalseString)) == true);\r
811         bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true);\r
812         int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", "100"));\r
813 \r
814         _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", "30"), CultureInfo.CurrentCulture);\r
815 \r
816         _defaultIsolation = (IsolationLevel)Enum.Parse(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", "Serializable"), true);\r
817         if (_defaultIsolation != IsolationLevel.Serializable && _defaultIsolation != IsolationLevel.ReadCommitted)\r
818           throw new NotSupportedException("Invalid Default IsolationLevel specified");\r
819 \r
820         SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat", "ISO8601"), true);\r
821         //string temp = FindKey(opts, "DateTimeFormat", "ISO8601");\r
822         //if (String.Compare(temp, "ticks", true, CultureInfo.InvariantCulture) == 0) dateFormat = SQLiteDateFormats.Ticks;\r
823         //else if (String.Compare(temp, "julianday", true, CultureInfo.InvariantCulture) == 0) dateFormat = SQLiteDateFormats.JulianDay;\r
824 \r
825         if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16()\r
826           _sql = new SQLite3_UTF16(dateFormat);\r
827         else\r
828           _sql = new SQLite3(dateFormat);\r
829 \r
830         SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None;\r
831 \r
832         if (SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", Boolean.FalseString)) == false)\r
833           flags |= SQLiteOpenFlagsEnum.Create;\r
834 \r
835         if (SQLiteConvert.ToBoolean(FindKey(opts, "Read Only", Boolean.FalseString)) == true)\r
836           flags |= SQLiteOpenFlagsEnum.ReadOnly;\r
837         else\r
838           flags |= SQLiteOpenFlagsEnum.ReadWrite;\r
839 \r
840         _sql.Open(fileName, flags, maxPoolSize, usePooling);\r
841 \r
842         _binaryGuid = (SQLiteConvert.ToBoolean(FindKey(opts, "BinaryGUID", Boolean.TrueString)) == true);\r
843 \r
844         string password = FindKey(opts, "Password", null);\r
845 \r
846         if (String.IsNullOrEmpty(password) == false)\r
847           _sql.SetPassword(System.Text.UTF8Encoding.UTF8.GetBytes(password));\r
848         else if (_password != null)\r
849           _sql.SetPassword(_password);\r
850         _password = null;\r
851 \r
852         _dataSource = Path.GetFileNameWithoutExtension(fileName);\r
853 \r
854         OnStateChange(ConnectionState.Open);\r
855         _version++;\r
856 \r
857         using (SQLiteCommand cmd = CreateCommand())\r
858         {\r
859           string defValue;\r
860 \r
861           if (fileName != ":memory:")\r
862           {\r
863             defValue = FindKey(opts, "Page Size", "1024");\r
864             if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 1024)\r
865             {\r
866               cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA page_size={0}", defValue);\r
867               cmd.ExecuteNonQuery();\r
868             }\r
869           }\r
870 \r
871           defValue = FindKey(opts, "Max Page Count", "0");\r
872           if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 0)\r
873           {\r
874             cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA max_page_count={0}", defValue);\r
875             cmd.ExecuteNonQuery();\r
876           }\r
877 \r
878           defValue = FindKey(opts, "Legacy Format", Boolean.FalseString);\r
879           cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA legacy_file_format={0}", SQLiteConvert.ToBoolean(defValue) == true ? "ON" : "OFF");\r
880           cmd.ExecuteNonQuery();\r
881 \r
882           defValue = FindKey(opts, "Synchronous", "Normal");\r
883           if (String.Compare(defValue, "Full", StringComparison.OrdinalIgnoreCase) != 0)\r
884           {\r
885             cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA synchronous={0}", defValue);\r
886             cmd.ExecuteNonQuery();\r
887           }\r
888 \r
889           defValue = FindKey(opts, "Cache Size", "2000");\r
890           if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 2000)\r
891           {\r
892             cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA cache_size={0}", defValue);\r
893             cmd.ExecuteNonQuery();\r
894           }\r
895 \r
896           defValue = FindKey(opts, "Journal Mode", "Delete");\r
897           if (String.Compare(defValue, "Default", StringComparison.OrdinalIgnoreCase) != 0)\r
898           {\r
899             cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA journal_mode={0}", defValue);\r
900             cmd.ExecuteNonQuery();\r
901           }\r
902         }\r
903 \r
904         if (_commitHandler != null)\r
905           _sql.SetCommitHook(_commitCallback);\r
906 \r
907         if (_updateHandler != null)\r
908           _sql.SetUpdateHook(_updateCallback);\r
909 \r
910         if (_rollbackHandler != null)\r
911           _sql.SetRollbackHook(_rollbackCallback);\r
912 \r
913 #if !PLATFORM_COMPACTFRAMEWORK\r
914         if (Transactions.Transaction.Current != null && SQLiteConvert.ToBoolean(FindKey(opts, "Enlist", Boolean.TrueString)) == true)\r
915           EnlistTransaction(Transactions.Transaction.Current);\r
916 #endif\r
917       }\r
918       catch (SQLiteException)\r
919       {\r
920         Close();\r
921         throw;\r
922       }\r
923     }\r
924 \r
925     /// <summary>\r
926     /// Gets/sets the default command timeout for newly-created commands.  This is especially useful for \r
927     /// commands used internally such as inside a SQLiteTransaction, where setting the timeout is not possible.\r
928     /// This can also be set in the ConnectionString with "Default Timeout"\r
929     /// </summary>\r
930     public int DefaultTimeout\r
931     {\r
932       get { return _defaultTimeout; }\r
933       set { _defaultTimeout = value; }\r
934     }\r
935 \r
936     /// <summary>\r
937     /// Returns the version of the underlying SQLite database engine\r
938     /// </summary>\r
939 #if !PLATFORM_COMPACTFRAMEWORK\r
940     [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]\r
941 #endif\r
942     public override string ServerVersion\r
943     {\r
944       get\r
945       {\r
946         if (_connectionState != ConnectionState.Open)\r
947           throw new InvalidOperationException();\r
948 \r
949         return _sql.Version;\r
950       }\r
951     }\r
952 \r
953     /// <summary>\r
954     /// Returns the version of the underlying SQLite database engine\r
955     /// </summary>\r
956     public static string SQLiteVersion\r
957     {\r
958       get { return SQLite3.SQLiteVersion; }\r
959     }\r
960 \r
961     /// <summary>\r
962     /// Returns the state of the connection.\r
963     /// </summary>\r
964 #if !PLATFORM_COMPACTFRAMEWORK\r
965     [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]\r
966 #endif\r
967     public override ConnectionState State\r
968     {\r
969       get\r
970       {\r
971         return _connectionState;\r
972       }\r
973     }\r
974 \r
975     /// <summary>\r
976     /// Change the password (or assign a password) to an open database.\r
977     /// </summary>\r
978     /// <remarks>\r
979     /// No readers or writers may be active for this process.  The database must already be open\r
980     /// and if it already was password protected, the existing password must already have been supplied.\r
981     /// </remarks>\r
982     /// <param name="newPassword">The new password to assign to the database</param>\r
983     public void ChangePassword(string newPassword)\r
984     {\r
985       ChangePassword(String.IsNullOrEmpty(newPassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(newPassword));\r
986     }\r
987 \r
988     /// <summary>\r
989     /// Change the password (or assign a password) to an open database.\r
990     /// </summary>\r
991     /// <remarks>\r
992     /// No readers or writers may be active for this process.  The database must already be open\r
993     /// and if it already was password protected, the existing password must already have been supplied.\r
994     /// </remarks>\r
995     /// <param name="newPassword">The new password to assign to the database</param>\r
996     public void ChangePassword(byte[] newPassword)\r
997     {\r
998       if (_connectionState != ConnectionState.Open)\r
999         throw new InvalidOperationException("Database must be opened before changing the password.");\r
1000 \r
1001       _sql.ChangePassword(newPassword);\r
1002     }\r
1003 \r
1004     /// <summary>\r
1005     /// Sets the password for a password-protected database.  A password-protected database is\r
1006     /// unusable for any operation until the password has been set.\r
1007     /// </summary>\r
1008     /// <param name="databasePassword">The password for the database</param>\r
1009     public void SetPassword(string databasePassword)\r
1010     {\r
1011       SetPassword(String.IsNullOrEmpty(databasePassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(databasePassword));\r
1012     }\r
1013 \r
1014     /// <summary>\r
1015     /// Sets the password for a password-protected database.  A password-protected database is\r
1016     /// unusable for any operation until the password has been set.\r
1017     /// </summary>\r
1018     /// <param name="databasePassword">The password for the database</param>\r
1019     public void SetPassword(byte[] databasePassword)\r
1020     {\r
1021       if (_connectionState != ConnectionState.Closed)\r
1022         throw new InvalidOperationException("Password can only be set before the database is opened.");\r
1023 \r
1024       if (databasePassword != null)\r
1025         if (databasePassword.Length == 0) databasePassword = null;\r
1026 \r
1027       _password = databasePassword;\r
1028     }\r
1029 \r
1030     /// <summary>\r
1031     /// Expand the filename of the data source, resolving the |DataDirectory| macro as appropriate.\r
1032     /// </summary>\r
1033     /// <param name="sourceFile">The database filename to expand</param>\r
1034     /// <returns>The expanded path and filename of the filename</returns>\r
1035     private string ExpandFileName(string sourceFile)\r
1036     {\r
1037       if (String.IsNullOrEmpty(sourceFile)) return sourceFile;\r
1038 \r
1039       if (sourceFile.StartsWith(_dataDirectory, StringComparison.OrdinalIgnoreCase))\r
1040       {\r
1041         string dataDirectory;\r
1042 \r
1043 #if PLATFORM_COMPACTFRAMEWORK\r
1044         dataDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase);\r
1045 #else\r
1046         dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory") as string;\r
1047         if (String.IsNullOrEmpty(dataDirectory))\r
1048           dataDirectory = AppDomain.CurrentDomain.BaseDirectory;\r
1049 #endif\r
1050 \r
1051         if (sourceFile.Length > _dataDirectory.Length)\r
1052         {\r
1053           if (sourceFile[_dataDirectory.Length] == Path.DirectorySeparatorChar ||\r
1054               sourceFile[_dataDirectory.Length] == Path.AltDirectorySeparatorChar)\r
1055             sourceFile = sourceFile.Remove(_dataDirectory.Length, 1);\r
1056         }\r
1057         sourceFile = Path.Combine(dataDirectory, sourceFile.Substring(_dataDirectory.Length));\r
1058       }\r
1059 \r
1060 #if !PLATFORM_COMPACTFRAMEWORK\r
1061       sourceFile = Path.GetFullPath(sourceFile);\r
1062 #endif\r
1063 \r
1064       return sourceFile;\r
1065     }\r
1066 \r
1067     ///<overloads>\r
1068     /// The following commands are used to extract schema information out of the database.  Valid schema types are:\r
1069     /// <list type="bullet">\r
1070     /// <item>\r
1071     /// <description>MetaDataCollections</description>\r
1072     /// </item>\r
1073     /// <item>\r
1074     /// <description>DataSourceInformation</description>\r
1075     /// </item>\r
1076     /// <item>\r
1077     /// <description>Catalogs</description>\r
1078     /// </item>\r
1079     /// <item>\r
1080     /// <description>Columns</description>\r
1081     /// </item>\r
1082     /// <item>\r
1083     /// <description>ForeignKeys</description>\r
1084     /// </item>\r
1085     /// <item>\r
1086     /// <description>Indexes</description>\r
1087     /// </item>\r
1088     /// <item>\r
1089     /// <description>IndexColumns</description>\r
1090     /// </item>\r
1091     /// <item>\r
1092     /// <description>Tables</description>\r
1093     /// </item>\r
1094     /// <item>\r
1095     /// <description>Views</description>\r
1096     /// </item>\r
1097     /// <item>\r
1098     /// <description>ViewColumns</description>\r
1099     /// </item>\r
1100     /// </list>\r
1101     /// </overloads>\r
1102     /// <summary>\r
1103     /// Returns the MetaDataCollections schema\r
1104     /// </summary>\r
1105     /// <returns>A DataTable of the MetaDataCollections schema</returns>\r
1106     public override DataTable GetSchema()\r
1107     {\r
1108       return GetSchema("MetaDataCollections", null);\r
1109     }\r
1110 \r
1111     /// <summary>\r
1112     /// Returns schema information of the specified collection\r
1113     /// </summary>\r
1114     /// <param name="collectionName">The schema collection to retrieve</param>\r
1115     /// <returns>A DataTable of the specified collection</returns>\r
1116     public override DataTable GetSchema(string collectionName)\r
1117     {\r
1118       return GetSchema(collectionName, new string[0]);\r
1119     }\r
1120 \r
1121     /// <summary>\r
1122     /// Retrieves schema information using the specified constraint(s) for the specified collection\r
1123     /// </summary>\r
1124     /// <param name="collectionName">The collection to retrieve</param>\r
1125     /// <param name="restrictionValues">The restrictions to impose</param>\r
1126     /// <returns>A DataTable of the specified collection</returns>\r
1127     public override DataTable GetSchema(string collectionName, string[] restrictionValues)\r
1128     {\r
1129       if (_connectionState != ConnectionState.Open)\r
1130         throw new InvalidOperationException();\r
1131 \r
1132       string[] parms = new string[5];\r
1133 \r
1134       if (restrictionValues == null) restrictionValues = new string[0];\r
1135       restrictionValues.CopyTo(parms, 0);\r
1136 \r
1137       switch (collectionName.ToUpper(CultureInfo.InvariantCulture))\r
1138       {\r
1139         case "METADATACOLLECTIONS":\r
1140           return Schema_MetaDataCollections();\r
1141         case "DATASOURCEINFORMATION":\r
1142           return Schema_DataSourceInformation();\r
1143         case "DATATYPES":\r
1144           return Schema_DataTypes();\r
1145         case "COLUMNS":\r
1146         case "TABLECOLUMNS":\r
1147           return Schema_Columns(parms[0], parms[2], parms[3]);\r
1148         case "INDEXES":\r
1149           return Schema_Indexes(parms[0], parms[2], parms[3]);\r
1150         case "TRIGGERS":\r
1151           return Schema_Triggers(parms[0], parms[2], parms[3]);\r
1152         case "INDEXCOLUMNS":\r
1153           return Schema_IndexColumns(parms[0], parms[2], parms[3], parms[4]);\r
1154         case "TABLES":\r
1155           return Schema_Tables(parms[0], parms[2], parms[3]);\r
1156         case "VIEWS":\r
1157           return Schema_Views(parms[0], parms[2]);\r
1158         case "VIEWCOLUMNS":\r
1159           return Schema_ViewColumns(parms[0], parms[2], parms[3]);\r
1160         case "FOREIGNKEYS":\r
1161           return Schema_ForeignKeys(parms[0], parms[2], parms[3]);\r
1162         case "CATALOGS":\r
1163           return Schema_Catalogs(parms[0]);\r
1164         case "RESERVEDWORDS":\r
1165           return Schema_ReservedWords();\r
1166       }\r
1167       throw new NotSupportedException();\r
1168     }\r
1169 \r
1170     private static DataTable Schema_ReservedWords()\r
1171     {\r
1172       DataTable tbl = new DataTable("MetaDataCollections");\r
1173 \r
1174       tbl.Locale = CultureInfo.InvariantCulture;\r
1175       tbl.Columns.Add("ReservedWord", typeof(string));\r
1176       tbl.Columns.Add("MaximumVersion", typeof(string));\r
1177       tbl.Columns.Add("MinimumVersion", typeof(string));\r
1178 \r
1179       tbl.BeginLoadData();\r
1180       DataRow row;\r
1181       foreach (string word in SR.Keywords.Split(new char[] { ',' }))\r
1182       {\r
1183         row = tbl.NewRow();\r
1184         row[0] = word;\r
1185         tbl.Rows.Add(row);\r
1186       }\r
1187 \r
1188       tbl.AcceptChanges();\r
1189       tbl.EndLoadData();\r
1190 \r
1191       return tbl;\r
1192     }\r
1193 \r
1194     /// <summary>\r
1195     /// Builds a MetaDataCollections schema datatable\r
1196     /// </summary>\r
1197     /// <returns>DataTable</returns>\r
1198     private static DataTable Schema_MetaDataCollections()\r
1199     {\r
1200       DataTable tbl = new DataTable("MetaDataCollections");\r
1201 \r
1202       tbl.Locale = CultureInfo.InvariantCulture;\r
1203       tbl.Columns.Add("CollectionName", typeof(string));\r
1204       tbl.Columns.Add("NumberOfRestrictions", typeof(int));\r
1205       tbl.Columns.Add("NumberOfIdentifierParts", typeof(int));\r
1206 \r
1207       tbl.BeginLoadData();\r
1208 \r
1209       StringReader reader = new StringReader(SR.MetaDataCollections);\r
1210       tbl.ReadXml(reader);\r
1211       reader.Close();\r
1212 \r
1213       tbl.AcceptChanges();\r
1214       tbl.EndLoadData();\r
1215 \r
1216       return tbl;\r
1217     }\r
1218 \r
1219     /// <summary>\r
1220     /// Builds a DataSourceInformation datatable\r
1221     /// </summary>\r
1222     /// <returns>DataTable</returns>\r
1223     private DataTable Schema_DataSourceInformation()\r
1224     {\r
1225       DataTable tbl = new DataTable("DataSourceInformation");\r
1226       DataRow row;\r
1227 \r
1228       tbl.Locale = CultureInfo.InvariantCulture;\r
1229       tbl.Columns.Add(DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern, typeof(string));\r
1230       tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductName, typeof(string));\r
1231       tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductVersion, typeof(string));\r
1232       tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductVersionNormalized, typeof(string));\r
1233       tbl.Columns.Add(DbMetaDataColumnNames.GroupByBehavior, typeof(int));\r
1234       tbl.Columns.Add(DbMetaDataColumnNames.IdentifierPattern, typeof(string));\r
1235       tbl.Columns.Add(DbMetaDataColumnNames.IdentifierCase, typeof(int));\r
1236       tbl.Columns.Add(DbMetaDataColumnNames.OrderByColumnsInSelect, typeof(bool));\r
1237       tbl.Columns.Add(DbMetaDataColumnNames.ParameterMarkerFormat, typeof(string));\r
1238       tbl.Columns.Add(DbMetaDataColumnNames.ParameterMarkerPattern, typeof(string));\r
1239       tbl.Columns.Add(DbMetaDataColumnNames.ParameterNameMaxLength, typeof(int));\r
1240       tbl.Columns.Add(DbMetaDataColumnNames.ParameterNamePattern, typeof(string));\r
1241       tbl.Columns.Add(DbMetaDataColumnNames.QuotedIdentifierPattern, typeof(string));\r
1242       tbl.Columns.Add(DbMetaDataColumnNames.QuotedIdentifierCase, typeof(int));\r
1243       tbl.Columns.Add(DbMetaDataColumnNames.StatementSeparatorPattern, typeof(string));\r
1244       tbl.Columns.Add(DbMetaDataColumnNames.StringLiteralPattern, typeof(string));\r
1245       tbl.Columns.Add(DbMetaDataColumnNames.SupportedJoinOperators, typeof(int));\r
1246 \r
1247       tbl.BeginLoadData();\r
1248 \r
1249       row = tbl.NewRow();\r
1250       row.ItemArray = new object[] {\r
1251         null,\r
1252         "SQLite",\r
1253         _sql.Version,\r
1254         _sql.Version,\r
1255         3,\r
1256         @"(^\[\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\[[^\]\0]|\]\]+\]$)|(^\""[^\""\0]|\""\""+\""$)",\r
1257         1,\r
1258         false,\r
1259         "{0}",\r
1260         @"@[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)",\r
1261         255,\r
1262         @"^[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)",\r
1263         @"(([^\[]|\]\])*)",\r
1264         1,\r
1265         ";",\r
1266         @"'(([^']|'')*)'",\r
1267         15\r
1268       };\r
1269       tbl.Rows.Add(row);\r
1270 \r
1271       tbl.AcceptChanges();\r
1272       tbl.EndLoadData();\r
1273 \r
1274       return tbl;\r
1275     }\r
1276 \r
1277     /// <summary>\r
1278     /// Build a Columns schema\r
1279     /// </summary>\r
1280     /// <param name="strCatalog">The catalog (attached database) to query, can be null</param>\r
1281     /// <param name="strTable">The table to retrieve schema information for, must not be null</param>\r
1282     /// <param name="strColumn">The column to retrieve schema information for, can be null</param>\r
1283     /// <returns>DataTable</returns>\r
1284     private DataTable Schema_Columns(string strCatalog, string strTable, string strColumn)\r
1285     {\r
1286       DataTable tbl = new DataTable("Columns");\r
1287       DataRow row;\r
1288 \r
1289       tbl.Locale = CultureInfo.InvariantCulture;\r
1290       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
1291       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
1292       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
1293       tbl.Columns.Add("COLUMN_NAME", typeof(string));\r
1294       tbl.Columns.Add("COLUMN_GUID", typeof(Guid));\r
1295       tbl.Columns.Add("COLUMN_PROPID", typeof(long));\r
1296       tbl.Columns.Add("ORDINAL_POSITION", typeof(int));\r
1297       tbl.Columns.Add("COLUMN_HASDEFAULT", typeof(bool));\r
1298       tbl.Columns.Add("COLUMN_DEFAULT", typeof(string));\r
1299       tbl.Columns.Add("COLUMN_FLAGS", typeof(long));\r
1300       tbl.Columns.Add("IS_NULLABLE", typeof(bool));\r
1301       tbl.Columns.Add("DATA_TYPE", typeof(string));\r
1302       tbl.Columns.Add("TYPE_GUID", typeof(Guid));\r
1303       tbl.Columns.Add("CHARACTER_MAXIMUM_LENGTH", typeof(int));\r
1304       tbl.Columns.Add("CHARACTER_OCTET_LENGTH", typeof(int));\r
1305       tbl.Columns.Add("NUMERIC_PRECISION", typeof(int));\r
1306       tbl.Columns.Add("NUMERIC_SCALE", typeof(int));\r
1307       tbl.Columns.Add("DATETIME_PRECISION", typeof(long));\r
1308       tbl.Columns.Add("CHARACTER_SET_CATALOG", typeof(string));\r
1309       tbl.Columns.Add("CHARACTER_SET_SCHEMA", typeof(string));\r
1310       tbl.Columns.Add("CHARACTER_SET_NAME", typeof(string));\r
1311       tbl.Columns.Add("COLLATION_CATALOG", typeof(string));\r
1312       tbl.Columns.Add("COLLATION_SCHEMA", typeof(string));\r
1313       tbl.Columns.Add("COLLATION_NAME", typeof(string));\r
1314       tbl.Columns.Add("DOMAIN_CATALOG", typeof(string));\r
1315       tbl.Columns.Add("DOMAIN_NAME", typeof(string));\r
1316       tbl.Columns.Add("DESCRIPTION", typeof(string));\r
1317       tbl.Columns.Add("PRIMARY_KEY", typeof(bool));\r
1318       tbl.Columns.Add("EDM_TYPE", typeof(string));\r
1319       tbl.Columns.Add("AUTOINCREMENT", typeof(bool));\r
1320       tbl.Columns.Add("UNIQUE", typeof(bool));\r
1321 \r
1322       tbl.BeginLoadData();\r
1323 \r
1324       if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main";\r
1325 \r
1326       string master = (String.Compare(strCatalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
1327 \r
1328       using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table' OR [type] LIKE 'view'", strCatalog, master), this))\r
1329       using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())\r
1330       {\r
1331         while (rdTables.Read())\r
1332         {\r
1333           if (String.IsNullOrEmpty(strTable) || String.Compare(strTable, rdTables.GetString(2), true, CultureInfo.InvariantCulture) == 0)\r
1334           {\r
1335             try\r
1336             {\r
1337               using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdTables.GetString(2)), this))\r
1338               using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader(CommandBehavior.SchemaOnly))\r
1339               using (DataTable tblSchema = rd.GetSchemaTable(true, true))\r
1340               {\r
1341                 foreach (DataRow schemaRow in tblSchema.Rows)\r
1342                 {\r
1343                   if (String.Compare(schemaRow[SchemaTableColumn.ColumnName].ToString(), strColumn, true, CultureInfo.InvariantCulture) == 0\r
1344                     || strColumn == null)\r
1345                   {\r
1346                     row = tbl.NewRow();\r
1347 \r
1348                     row["NUMERIC_PRECISION"] = schemaRow[SchemaTableColumn.NumericPrecision];\r
1349                     row["NUMERIC_SCALE"] = schemaRow[SchemaTableColumn.NumericScale];\r
1350                     row["TABLE_NAME"] = rdTables.GetString(2);\r
1351                     row["COLUMN_NAME"] = schemaRow[SchemaTableColumn.ColumnName];\r
1352                     row["TABLE_CATALOG"] = strCatalog;\r
1353                     row["ORDINAL_POSITION"] = schemaRow[SchemaTableColumn.ColumnOrdinal];\r
1354                     row["COLUMN_HASDEFAULT"] = (schemaRow[SchemaTableOptionalColumn.DefaultValue] != DBNull.Value);\r
1355                     row["COLUMN_DEFAULT"] = schemaRow[SchemaTableOptionalColumn.DefaultValue];\r
1356                     row["IS_NULLABLE"] = schemaRow[SchemaTableColumn.AllowDBNull];\r
1357                     row["DATA_TYPE"] = schemaRow["DataTypeName"].ToString().ToLower(CultureInfo.InvariantCulture);\r
1358                     row["EDM_TYPE"] = SQLiteConvert.DbTypeToTypeName((DbType)schemaRow[SchemaTableColumn.ProviderType]).ToString().ToLower(CultureInfo.InvariantCulture);\r
1359                     row["CHARACTER_MAXIMUM_LENGTH"] = schemaRow[SchemaTableColumn.ColumnSize];\r
1360                     row["TABLE_SCHEMA"] = schemaRow[SchemaTableColumn.BaseSchemaName];\r
1361                     row["PRIMARY_KEY"] = schemaRow[SchemaTableColumn.IsKey];\r
1362                     row["AUTOINCREMENT"] = schemaRow[SchemaTableOptionalColumn.IsAutoIncrement];\r
1363                     row["COLLATION_NAME"] = schemaRow["CollationType"];\r
1364                     row["UNIQUE"] = schemaRow[SchemaTableColumn.IsUnique];\r
1365                     tbl.Rows.Add(row);\r
1366                   }\r
1367                 }\r
1368               }\r
1369             }\r
1370             catch(SQLiteException)\r
1371             {\r
1372             }\r
1373           }\r
1374         }\r
1375       }\r
1376 \r
1377       tbl.AcceptChanges();\r
1378       tbl.EndLoadData();\r
1379 \r
1380       return tbl;\r
1381     }\r
1382 \r
1383     /// <summary>\r
1384     /// Returns index information for the given database and catalog\r
1385     /// </summary>\r
1386     /// <param name="strCatalog">The catalog (attached database) to query, can be null</param>\r
1387     /// <param name="strIndex">The name of the index to retrieve information for, can be null</param>\r
1388     /// <param name="strTable">The table to retrieve index information for, can be null</param>\r
1389     /// <returns>DataTable</returns>\r
1390     private DataTable Schema_Indexes(string strCatalog, string strTable, string strIndex)\r
1391     {\r
1392       DataTable tbl = new DataTable("Indexes");\r
1393       DataRow row;\r
1394       List<int> primaryKeys = new List<int>();\r
1395       bool maybeRowId;\r
1396 \r
1397       tbl.Locale = CultureInfo.InvariantCulture;\r
1398       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
1399       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
1400       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
1401       tbl.Columns.Add("INDEX_CATALOG", typeof(string));\r
1402       tbl.Columns.Add("INDEX_SCHEMA", typeof(string));\r
1403       tbl.Columns.Add("INDEX_NAME", typeof(string));\r
1404       tbl.Columns.Add("PRIMARY_KEY", typeof(bool));\r
1405       tbl.Columns.Add("UNIQUE", typeof(bool));\r
1406       tbl.Columns.Add("CLUSTERED", typeof(bool));\r
1407       tbl.Columns.Add("TYPE", typeof(int));\r
1408       tbl.Columns.Add("FILL_FACTOR", typeof(int));\r
1409       tbl.Columns.Add("INITIAL_SIZE", typeof(int));\r
1410       tbl.Columns.Add("NULLS", typeof(int));\r
1411       tbl.Columns.Add("SORT_BOOKMARKS", typeof(bool));\r
1412       tbl.Columns.Add("AUTO_UPDATE", typeof(bool));\r
1413       tbl.Columns.Add("NULL_COLLATION", typeof(int));\r
1414       tbl.Columns.Add("ORDINAL_POSITION", typeof(int));\r
1415       tbl.Columns.Add("COLUMN_NAME", typeof(string));\r
1416       tbl.Columns.Add("COLUMN_GUID", typeof(Guid));\r
1417       tbl.Columns.Add("COLUMN_PROPID", typeof(long));\r
1418       tbl.Columns.Add("COLLATION", typeof(short));\r
1419       tbl.Columns.Add("CARDINALITY", typeof(Decimal));\r
1420       tbl.Columns.Add("PAGES", typeof(int));\r
1421       tbl.Columns.Add("FILTER_CONDITION", typeof(string));\r
1422       tbl.Columns.Add("INTEGRATED", typeof(bool));\r
1423       tbl.Columns.Add("INDEX_DEFINITION", typeof(string));\r
1424 \r
1425       tbl.BeginLoadData();\r
1426 \r
1427       if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main";\r
1428 \r
1429       string master = (String.Compare(strCatalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
1430       \r
1431       using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))\r
1432       using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())\r
1433       {\r
1434         while (rdTables.Read())\r
1435         {\r
1436           maybeRowId = false;\r
1437           primaryKeys.Clear();\r
1438           if (String.IsNullOrEmpty(strTable) || String.Compare(rdTables.GetString(2), strTable, true, CultureInfo.InvariantCulture) == 0)\r
1439           {\r
1440             // First, look for any rowid indexes -- which sqlite defines are INTEGER PRIMARY KEY columns.\r
1441             // Such indexes are not listed in the indexes list but count as indexes just the same.\r
1442             try\r
1443             {\r
1444               using (SQLiteCommand cmdTable = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].table_info([{1}])", strCatalog, rdTables.GetString(2)), this))\r
1445               using (SQLiteDataReader rdTable = cmdTable.ExecuteReader())\r
1446               {\r
1447                 while (rdTable.Read())\r
1448                 {\r
1449                   if (rdTable.GetInt32(5) == 1)\r
1450                   {\r
1451                     primaryKeys.Add(rdTable.GetInt32(0));\r
1452 \r
1453                     // If the primary key is of type INTEGER, then its a rowid and we need to make a fake index entry for it.\r
1454                     if (String.Compare(rdTable.GetString(2), "INTEGER", true, CultureInfo.InvariantCulture) == 0)\r
1455                       maybeRowId = true;\r
1456                   }\r
1457                 }\r
1458               }\r
1459             }\r
1460             catch (SQLiteException)\r
1461             {\r
1462             }\r
1463             if (primaryKeys.Count == 1 && maybeRowId == true)\r
1464             {\r
1465               row = tbl.NewRow();\r
1466 \r
1467               row["TABLE_CATALOG"] = strCatalog;\r
1468               row["TABLE_NAME"] = rdTables.GetString(2);\r
1469               row["INDEX_CATALOG"] = strCatalog;\r
1470               row["PRIMARY_KEY"] = true;\r
1471               row["INDEX_NAME"] = String.Format(CultureInfo.InvariantCulture, "{1}_PK_{0}", rdTables.GetString(2), master);\r
1472               row["UNIQUE"] = true;\r
1473 \r
1474               if (String.Compare((string)row["INDEX_NAME"], strIndex, true, CultureInfo.InvariantCulture) == 0\r
1475               || strIndex == null)\r
1476               {\r
1477                 tbl.Rows.Add(row);\r
1478               }\r
1479 \r
1480               primaryKeys.Clear();\r
1481             }\r
1482 \r
1483             // Now fetch all the rest of the indexes.\r
1484             try\r
1485             {\r
1486               using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_list([{1}])", strCatalog, rdTables.GetString(2)), this))\r
1487               using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())\r
1488               {\r
1489                 while (rd.Read())\r
1490                 {\r
1491                   if (String.Compare(rd.GetString(1), strIndex, true, CultureInfo.InvariantCulture) == 0\r
1492                   || strIndex == null)\r
1493                   {\r
1494                     row = tbl.NewRow();\r
1495 \r
1496                     row["TABLE_CATALOG"] = strCatalog;\r
1497                     row["TABLE_NAME"] = rdTables.GetString(2);\r
1498                     row["INDEX_CATALOG"] = strCatalog;\r
1499                     row["INDEX_NAME"] = rd.GetString(1);\r
1500                     row["UNIQUE"] = rd.GetBoolean(2);\r
1501                     row["PRIMARY_KEY"] = false;\r
1502 \r
1503                     // get the index definition\r
1504                     using (SQLiteCommand cmdIndexes = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{2}] WHERE [type] LIKE 'index' AND [name] LIKE '{1}'", strCatalog, rd.GetString(1).Replace("'", "''"), master), this))\r
1505                     using (SQLiteDataReader rdIndexes = cmdIndexes.ExecuteReader())\r
1506                     {\r
1507                       while (rdIndexes.Read())\r
1508                       {\r
1509                         if (rdIndexes.IsDBNull(4) == false)\r
1510                           row["INDEX_DEFINITION"] = rdIndexes.GetString(4);\r
1511                         break;\r
1512                       }\r
1513                     }\r
1514 \r
1515                     // Now for the really hard work.  Figure out which index is the primary key index.\r
1516                     // The only way to figure it out is to check if the index was an autoindex and if we have a non-rowid\r
1517                     // primary key, and all the columns in the given index match the primary key columns\r
1518                     if (primaryKeys.Count > 0 && rd.GetString(1).StartsWith("sqlite_autoindex_" + rdTables.GetString(2), StringComparison.InvariantCultureIgnoreCase) == true)\r
1519                     {\r
1520                       using (SQLiteCommand cmdDetails = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_info([{1}])", strCatalog, rd.GetString(1)), this))\r
1521                       using (SQLiteDataReader rdDetails = cmdDetails.ExecuteReader())\r
1522                       {\r
1523                         int nMatches = 0;\r
1524                         while (rdDetails.Read())\r
1525                         {\r
1526                           if (primaryKeys.Contains(rdDetails.GetInt32(1)) == false)\r
1527                           {\r
1528                             nMatches = 0;\r
1529                             break;\r
1530                           }\r
1531                           nMatches++;\r
1532                         }\r
1533                         if (nMatches == primaryKeys.Count)\r
1534                         {\r
1535                           row["PRIMARY_KEY"] = true;\r
1536                           primaryKeys.Clear();\r
1537                         }\r
1538                       }\r
1539                     }\r
1540 \r
1541                     tbl.Rows.Add(row);\r
1542                   }\r
1543                 }\r
1544               }\r
1545             }\r
1546             catch (SQLiteException)\r
1547             {\r
1548             }\r
1549           }\r
1550         }\r
1551       }\r
1552 \r
1553       tbl.AcceptChanges();\r
1554       tbl.EndLoadData();\r
1555 \r
1556       return tbl;\r
1557     }\r
1558 \r
1559     private DataTable Schema_Triggers(string catalog, string table, string triggerName)\r
1560     {\r
1561       DataTable tbl = new DataTable("Triggers");\r
1562       DataRow row;\r
1563 \r
1564       tbl.Locale = CultureInfo.InvariantCulture;\r
1565       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
1566       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
1567       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
1568       tbl.Columns.Add("TRIGGER_NAME", typeof(string));\r
1569       tbl.Columns.Add("TRIGGER_DEFINITION", typeof(string));\r
1570 \r
1571       tbl.BeginLoadData();\r
1572 \r
1573       if (String.IsNullOrEmpty(table)) table = null;\r
1574       if (String.IsNullOrEmpty(catalog)) catalog = "main";\r
1575       string master = (String.Compare(catalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
1576 \r
1577       using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT [type], [name], [tbl_name], [rootpage], [sql], [rowid] FROM [{0}].[{1}] WHERE [type] LIKE 'trigger'", catalog, master), this))\r
1578       using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())\r
1579       {\r
1580         while (rd.Read())\r
1581         {\r
1582           if (String.Compare(rd.GetString(1), triggerName, true, CultureInfo.InvariantCulture) == 0\r
1583             || triggerName == null)\r
1584           {\r
1585             if (table == null || String.Compare(table, rd.GetString(2), true, CultureInfo.InvariantCulture) == 0)\r
1586             {\r
1587               row = tbl.NewRow();\r
1588 \r
1589               row["TABLE_CATALOG"] = catalog;\r
1590               row["TABLE_NAME"] = rd.GetString(2);\r
1591               row["TRIGGER_NAME"] = rd.GetString(1);\r
1592               row["TRIGGER_DEFINITION"] = rd.GetString(4);\r
1593 \r
1594               tbl.Rows.Add(row);\r
1595             }\r
1596           }\r
1597         }\r
1598       }\r
1599       tbl.AcceptChanges();\r
1600       tbl.EndLoadData();\r
1601 \r
1602       return tbl;\r
1603     }\r
1604 \r
1605     /// <summary>\r
1606     /// Retrieves table schema information for the database and catalog\r
1607     /// </summary>\r
1608     /// <param name="strCatalog">The catalog (attached database) to retrieve tables on</param>\r
1609     /// <param name="strTable">The table to retrieve, can be null</param>\r
1610     /// <param name="strType">The table type, can be null</param>\r
1611     /// <returns>DataTable</returns>\r
1612     private DataTable Schema_Tables(string strCatalog, string strTable, string strType)\r
1613     {\r
1614       DataTable tbl = new DataTable("Tables");\r
1615       DataRow row;\r
1616       string strItem;\r
1617 \r
1618       tbl.Locale = CultureInfo.InvariantCulture;\r
1619       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
1620       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
1621       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
1622       tbl.Columns.Add("TABLE_TYPE", typeof(string));\r
1623       tbl.Columns.Add("TABLE_ID", typeof(long));\r
1624       tbl.Columns.Add("TABLE_ROOTPAGE", typeof(int));\r
1625       tbl.Columns.Add("TABLE_DEFINITION", typeof(string));\r
1626       tbl.BeginLoadData();\r
1627 \r
1628       if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main";\r
1629 \r
1630       string master = (String.Compare(strCatalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
1631 \r
1632       using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT [type], [name], [tbl_name], [rootpage], [sql], [rowid] FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))\r
1633       using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())\r
1634       {\r
1635         while (rd.Read())\r
1636         {\r
1637           strItem = rd.GetString(0);\r
1638           if (String.Compare(rd.GetString(2), 0, "SQLITE_", 0, 7, true, CultureInfo.InvariantCulture) == 0)\r
1639             strItem = "SYSTEM_TABLE";\r
1640 \r
1641           if (String.Compare(strType, strItem, true, CultureInfo.InvariantCulture) == 0\r
1642             || strType == null)\r
1643           {\r
1644             if (String.Compare(rd.GetString(2), strTable, true, CultureInfo.InvariantCulture) == 0\r
1645               || strTable == null)\r
1646             {\r
1647               row = tbl.NewRow();\r
1648 \r
1649               row["TABLE_CATALOG"] = strCatalog;\r
1650               row["TABLE_NAME"] = rd.GetString(2);\r
1651               row["TABLE_TYPE"] = strItem;\r
1652               row["TABLE_ID"] = rd.GetInt64(5);\r
1653               row["TABLE_ROOTPAGE"] = rd.GetInt32(3);\r
1654               row["TABLE_DEFINITION"] = rd.GetString(4);\r
1655 \r
1656               tbl.Rows.Add(row);\r
1657             }\r
1658           }\r
1659         }\r
1660       }\r
1661 \r
1662       tbl.AcceptChanges();\r
1663       tbl.EndLoadData();\r
1664 \r
1665       return tbl;\r
1666     }\r
1667 \r
1668     /// <summary>\r
1669     /// Retrieves view schema information for the database\r
1670     /// </summary>\r
1671     /// <param name="strCatalog">The catalog (attached database) to retrieve views on</param>\r
1672     /// <param name="strView">The view name, can be null</param>\r
1673     /// <returns>DataTable</returns>\r
1674     private DataTable Schema_Views(string strCatalog, string strView)\r
1675     {\r
1676       DataTable tbl = new DataTable("Views");\r
1677       DataRow row;\r
1678       string strItem;\r
1679       int nPos;\r
1680 \r
1681       tbl.Locale = CultureInfo.InvariantCulture;\r
1682       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
1683       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
1684       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
1685       tbl.Columns.Add("VIEW_DEFINITION", typeof(string));\r
1686       tbl.Columns.Add("CHECK_OPTION", typeof(bool));\r
1687       tbl.Columns.Add("IS_UPDATABLE", typeof(bool));\r
1688       tbl.Columns.Add("DESCRIPTION", typeof(string));\r
1689       tbl.Columns.Add("DATE_CREATED", typeof(DateTime));\r
1690       tbl.Columns.Add("DATE_MODIFIED", typeof(DateTime));\r
1691 \r
1692       tbl.BeginLoadData();\r
1693 \r
1694       if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main";\r
1695 \r
1696       string master = (String.Compare(strCatalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
1697 \r
1698       using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'view'", strCatalog, master), this))\r
1699       using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())\r
1700       {\r
1701         while (rd.Read())\r
1702         {\r
1703           if (String.Compare(rd.GetString(1), strView, true, CultureInfo.InvariantCulture) == 0\r
1704             || String.IsNullOrEmpty(strView))\r
1705           {\r
1706             strItem = rd.GetString(4).Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ');\r
1707             nPos = CultureInfo.InvariantCulture.CompareInfo.IndexOf(strItem, " AS ", CompareOptions.IgnoreCase);\r
1708             if (nPos > -1)\r
1709             {\r
1710               strItem = strItem.Substring(nPos + 4).Trim();\r
1711               row = tbl.NewRow();\r
1712 \r
1713               row["TABLE_CATALOG"] = strCatalog;\r
1714               row["TABLE_NAME"] = rd.GetString(2);\r
1715               row["IS_UPDATABLE"] = false;\r
1716               row["VIEW_DEFINITION"] = strItem;\r
1717 \r
1718               tbl.Rows.Add(row);\r
1719             }\r
1720           }\r
1721         }\r
1722       }\r
1723 \r
1724       tbl.AcceptChanges();\r
1725       tbl.EndLoadData();\r
1726 \r
1727       return tbl;\r
1728     }\r
1729 \r
1730     /// <summary>\r
1731     /// Retrieves catalog (attached databases) schema information for the database\r
1732     /// </summary>\r
1733     /// <param name="strCatalog">The catalog to retrieve, can be null</param>\r
1734     /// <returns>DataTable</returns>\r
1735     private DataTable Schema_Catalogs(string strCatalog)\r
1736     {\r
1737       DataTable tbl = new DataTable("Catalogs");\r
1738       DataRow row;\r
1739 \r
1740       tbl.Locale = CultureInfo.InvariantCulture;\r
1741       tbl.Columns.Add("CATALOG_NAME", typeof(string));\r
1742       tbl.Columns.Add("DESCRIPTION", typeof(string));\r
1743       tbl.Columns.Add("ID", typeof(long));\r
1744 \r
1745       tbl.BeginLoadData();\r
1746 \r
1747       using (SQLiteCommand cmd = new SQLiteCommand("PRAGMA database_list", this))\r
1748       using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())\r
1749       {\r
1750         while (rd.Read())\r
1751         {\r
1752           if (String.Compare(rd.GetString(1), strCatalog, true, CultureInfo.InvariantCulture) == 0\r
1753             || strCatalog == null)\r
1754           {\r
1755             row = tbl.NewRow();\r
1756 \r
1757             row["CATALOG_NAME"] = rd.GetString(1);\r
1758             row["DESCRIPTION"] = rd.GetString(2);\r
1759             row["ID"] = rd.GetInt64(0);\r
1760 \r
1761             tbl.Rows.Add(row);\r
1762           }\r
1763         }\r
1764       }\r
1765 \r
1766       tbl.AcceptChanges();\r
1767       tbl.EndLoadData();\r
1768 \r
1769       return tbl;\r
1770     }\r
1771 \r
1772     private DataTable Schema_DataTypes()\r
1773     {\r
1774       DataTable tbl = new DataTable("DataTypes");\r
1775 \r
1776       tbl.Locale = CultureInfo.InvariantCulture;\r
1777       tbl.Columns.Add("TypeName", typeof(String));\r
1778       tbl.Columns.Add("ProviderDbType", typeof(int));\r
1779       tbl.Columns.Add("ColumnSize", typeof(long));\r
1780       tbl.Columns.Add("CreateFormat", typeof(String));\r
1781       tbl.Columns.Add("CreateParameters", typeof(String));\r
1782       tbl.Columns.Add("DataType", typeof(String));\r
1783       tbl.Columns.Add("IsAutoIncrementable", typeof(bool));\r
1784       tbl.Columns.Add("IsBestMatch", typeof(bool));\r
1785       tbl.Columns.Add("IsCaseSensitive", typeof(bool));\r
1786       tbl.Columns.Add("IsFixedLength", typeof(bool));\r
1787       tbl.Columns.Add("IsFixedPrecisionScale", typeof(bool));\r
1788       tbl.Columns.Add("IsLong", typeof(bool));\r
1789       tbl.Columns.Add("IsNullable", typeof(bool));\r
1790       tbl.Columns.Add("IsSearchable", typeof(bool));\r
1791       tbl.Columns.Add("IsSearchableWithLike", typeof(bool));\r
1792       tbl.Columns.Add("IsLiteralSupported", typeof(bool));\r
1793       tbl.Columns.Add("LiteralPrefix", typeof(String));\r
1794       tbl.Columns.Add("LiteralSuffix", typeof(String));\r
1795       tbl.Columns.Add("IsUnsigned", typeof(bool));\r
1796       tbl.Columns.Add("MaximumScale", typeof(short));\r
1797       tbl.Columns.Add("MinimumScale", typeof(short));\r
1798       tbl.Columns.Add("IsConcurrencyType", typeof(bool));\r
1799 \r
1800       tbl.BeginLoadData();\r
1801 \r
1802       StringReader reader = new StringReader(SR.DataTypes);\r
1803       tbl.ReadXml(reader);\r
1804       reader.Close();\r
1805 \r
1806       tbl.AcceptChanges();\r
1807       tbl.EndLoadData();\r
1808 \r
1809       return tbl;\r
1810     }\r
1811 \r
1812     /// <summary>\r
1813     /// Returns the base column information for indexes in a database\r
1814     /// </summary>\r
1815     /// <param name="strCatalog">The catalog to retrieve indexes for (can be null)</param>\r
1816     /// <param name="strTable">The table to restrict index information by (can be null)</param>\r
1817     /// <param name="strIndex">The index to restrict index information by (can be null)</param>\r
1818     /// <param name="strColumn">The source column to restrict index information by (can be null)</param>\r
1819     /// <returns>A DataTable containing the results</returns>\r
1820     private DataTable Schema_IndexColumns(string strCatalog, string strTable, string strIndex, string strColumn)\r
1821     {\r
1822       DataTable tbl = new DataTable("IndexColumns");\r
1823       DataRow row;\r
1824       List<KeyValuePair<int, string>> primaryKeys = new List<KeyValuePair<int, string>>();\r
1825       bool maybeRowId;\r
1826 \r
1827       tbl.Locale = CultureInfo.InvariantCulture;\r
1828       tbl.Columns.Add("CONSTRAINT_CATALOG", typeof(string));\r
1829       tbl.Columns.Add("CONSTRAINT_SCHEMA", typeof(string));\r
1830       tbl.Columns.Add("CONSTRAINT_NAME", typeof(string));\r
1831       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
1832       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
1833       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
1834       tbl.Columns.Add("COLUMN_NAME", typeof(string));\r
1835       tbl.Columns.Add("ORDINAL_POSITION", typeof(int));\r
1836       tbl.Columns.Add("INDEX_NAME", typeof(string));\r
1837       tbl.Columns.Add("COLLATION_NAME", typeof(string));\r
1838       tbl.Columns.Add("SORT_MODE", typeof(string));\r
1839       tbl.Columns.Add("CONFLICT_OPTION", typeof(int));\r
1840 \r
1841       if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main";\r
1842 \r
1843       string master = (String.Compare(strCatalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
1844 \r
1845       tbl.BeginLoadData();\r
1846 \r
1847       using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))\r
1848       using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())\r
1849       {\r
1850         while (rdTables.Read())\r
1851         {\r
1852           maybeRowId = false;\r
1853           primaryKeys.Clear();\r
1854           if (String.IsNullOrEmpty(strTable) || String.Compare(rdTables.GetString(2), strTable, true, CultureInfo.InvariantCulture) == 0)\r
1855           {\r
1856             try\r
1857             {\r
1858               using (SQLiteCommand cmdTable = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].table_info([{1}])", strCatalog, rdTables.GetString(2)), this))\r
1859               using (SQLiteDataReader rdTable = cmdTable.ExecuteReader())\r
1860               {\r
1861                 while (rdTable.Read())\r
1862                 {\r
1863                   if (rdTable.GetInt32(5) == 1) // is a primary key\r
1864                   {\r
1865                     primaryKeys.Add(new KeyValuePair<int, string>(rdTable.GetInt32(0), rdTable.GetString(1)));\r
1866                     // Is an integer -- could be a rowid if no other primary keys exist in the table\r
1867                     if (String.Compare(rdTable.GetString(2), "INTEGER", true, CultureInfo.InvariantCulture) == 0)\r
1868                       maybeRowId = true;\r
1869                   }\r
1870                 }\r
1871               }\r
1872             }\r
1873             catch (SQLiteException)\r
1874             {\r
1875             }\r
1876             // This is a rowid row\r
1877             if (primaryKeys.Count == 1 && maybeRowId == true)\r
1878             {\r
1879               row = tbl.NewRow();\r
1880               row["CONSTRAINT_CATALOG"] = strCatalog;\r
1881               row["CONSTRAINT_NAME"] = String.Format(CultureInfo.InvariantCulture, "{1}_PK_{0}", rdTables.GetString(2), master);\r
1882               row["TABLE_CATALOG"] = strCatalog;\r
1883               row["TABLE_NAME"] = rdTables.GetString(2);\r
1884               row["COLUMN_NAME"] = primaryKeys[0].Value;\r
1885               row["INDEX_NAME"] = row["CONSTRAINT_NAME"];\r
1886               row["ORDINAL_POSITION"] = 0; // primaryKeys[0].Key;\r
1887               row["COLLATION_NAME"] = "BINARY";\r
1888               row["SORT_MODE"] = "ASC";\r
1889               row["CONFLICT_OPTION"] = 2;\r
1890 \r
1891               if (String.IsNullOrEmpty(strIndex) || String.Compare(strIndex, (string)row["INDEX_NAME"], true, CultureInfo.InvariantCulture) == 0)\r
1892                 tbl.Rows.Add(row);\r
1893             }\r
1894 \r
1895             using (SQLiteCommand cmdIndexes = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{2}] WHERE [type] LIKE 'index' AND [tbl_name] LIKE '{1}'", strCatalog, rdTables.GetString(2).Replace("'", "''"), master), this))\r
1896             using (SQLiteDataReader rdIndexes = cmdIndexes.ExecuteReader())\r
1897             {\r
1898               while (rdIndexes.Read())\r
1899               {\r
1900                 int ordinal = 0;\r
1901                 if (String.IsNullOrEmpty(strIndex) || String.Compare(strIndex, rdIndexes.GetString(1), true, CultureInfo.InvariantCulture) == 0)\r
1902                 {\r
1903                   try\r
1904                   {\r
1905                     using (SQLiteCommand cmdIndex = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_info([{1}])", strCatalog, rdIndexes.GetString(1)), this))\r
1906                     using (SQLiteDataReader rdIndex = cmdIndex.ExecuteReader())\r
1907                     {\r
1908                       while (rdIndex.Read())\r
1909                       {\r
1910                         row = tbl.NewRow();\r
1911                         row["CONSTRAINT_CATALOG"] = strCatalog;\r
1912                         row["CONSTRAINT_NAME"] = rdIndexes.GetString(1);\r
1913                         row["TABLE_CATALOG"] = strCatalog;\r
1914                         row["TABLE_NAME"] = rdIndexes.GetString(2);\r
1915                         row["COLUMN_NAME"] = rdIndex.GetString(2);\r
1916                         row["INDEX_NAME"] = rdIndexes.GetString(1);\r
1917                         row["ORDINAL_POSITION"] = ordinal; // rdIndex.GetInt32(1);\r
1918 \r
1919                         string collationSequence;\r
1920                         int sortMode;\r
1921                         int onError;\r
1922                         _sql.GetIndexColumnExtendedInfo(strCatalog, rdIndexes.GetString(1), rdIndex.GetString(2), out sortMode, out onError, out collationSequence);\r
1923 \r
1924                         if (String.IsNullOrEmpty(collationSequence) == false)\r
1925                           row["COLLATION_NAME"] = collationSequence;\r
1926 \r
1927                         row["SORT_MODE"] = (sortMode == 0) ? "ASC" : "DESC";\r
1928                         row["CONFLICT_OPTION"] = onError;\r
1929 \r
1930                         ordinal++;\r
1931 \r
1932                         if (String.IsNullOrEmpty(strColumn) || String.Compare(strColumn, row["COLUMN_NAME"].ToString(), true, CultureInfo.InvariantCulture) == 0)\r
1933                           tbl.Rows.Add(row);\r
1934                       }\r
1935                     }\r
1936                   }\r
1937                   catch (SQLiteException)\r
1938                   {\r
1939                   }\r
1940                 }\r
1941               }\r
1942             }\r
1943           }\r
1944         }\r
1945       }\r
1946 \r
1947       tbl.EndLoadData();\r
1948       tbl.AcceptChanges();\r
1949 \r
1950       return tbl;\r
1951     }\r
1952 \r
1953     /// <summary>\r
1954     /// Returns detailed column information for a specified view\r
1955     /// </summary>\r
1956     /// <param name="strCatalog">The catalog to retrieve columns for (can be null)</param>\r
1957     /// <param name="strView">The view to restrict column information by (can be null)</param>\r
1958     /// <param name="strColumn">The source column to restrict column information by (can be null)</param>\r
1959     /// <returns>A DataTable containing the results</returns>\r
1960     private DataTable Schema_ViewColumns(string strCatalog, string strView, string strColumn)\r
1961     {\r
1962       DataTable tbl = new DataTable("ViewColumns");\r
1963       DataRow row;\r
1964       string strSql;\r
1965       int n;\r
1966       DataRow schemaRow;\r
1967       DataRow viewRow;\r
1968 \r
1969       tbl.Locale = CultureInfo.InvariantCulture;\r
1970       tbl.Columns.Add("VIEW_CATALOG", typeof(string));\r
1971       tbl.Columns.Add("VIEW_SCHEMA", typeof(string));\r
1972       tbl.Columns.Add("VIEW_NAME", typeof(string));\r
1973       tbl.Columns.Add("VIEW_COLUMN_NAME", typeof(String));\r
1974       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
1975       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
1976       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
1977       tbl.Columns.Add("COLUMN_NAME", typeof(string));\r
1978       tbl.Columns.Add("ORDINAL_POSITION", typeof(int));\r
1979       tbl.Columns.Add("COLUMN_HASDEFAULT", typeof(bool));\r
1980       tbl.Columns.Add("COLUMN_DEFAULT", typeof(string));\r
1981       tbl.Columns.Add("COLUMN_FLAGS", typeof(long));\r
1982       tbl.Columns.Add("IS_NULLABLE", typeof(bool));\r
1983       tbl.Columns.Add("DATA_TYPE", typeof(string));\r
1984       tbl.Columns.Add("CHARACTER_MAXIMUM_LENGTH", typeof(int));\r
1985       tbl.Columns.Add("NUMERIC_PRECISION", typeof(int));\r
1986       tbl.Columns.Add("NUMERIC_SCALE", typeof(int));\r
1987       tbl.Columns.Add("DATETIME_PRECISION", typeof(long));\r
1988       tbl.Columns.Add("CHARACTER_SET_CATALOG", typeof(string));\r
1989       tbl.Columns.Add("CHARACTER_SET_SCHEMA", typeof(string));\r
1990       tbl.Columns.Add("CHARACTER_SET_NAME", typeof(string));\r
1991       tbl.Columns.Add("COLLATION_CATALOG", typeof(string));\r
1992       tbl.Columns.Add("COLLATION_SCHEMA", typeof(string));\r
1993       tbl.Columns.Add("COLLATION_NAME", typeof(string));\r
1994       tbl.Columns.Add("PRIMARY_KEY", typeof(bool));\r
1995       tbl.Columns.Add("EDM_TYPE", typeof(string));\r
1996       tbl.Columns.Add("AUTOINCREMENT", typeof(bool));\r
1997       tbl.Columns.Add("UNIQUE", typeof(bool));\r
1998 \r
1999       if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main";\r
2000 \r
2001       string master = (String.Compare(strCatalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
2002       \r
2003       tbl.BeginLoadData();\r
2004 \r
2005       using (SQLiteCommand cmdViews = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'view'", strCatalog, master), this))\r
2006       using (SQLiteDataReader rdViews = cmdViews.ExecuteReader())\r
2007       {\r
2008         while (rdViews.Read())\r
2009         {\r
2010           if (String.IsNullOrEmpty(strView) || String.Compare(strView, rdViews.GetString(2), true, CultureInfo.InvariantCulture) == 0)\r
2011           {\r
2012             using (SQLiteCommand cmdViewSelect = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdViews.GetString(2)), this))\r
2013             {\r
2014               strSql = rdViews.GetString(4).Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ');\r
2015               n = CultureInfo.InvariantCulture.CompareInfo.IndexOf(strSql, " AS ", CompareOptions.IgnoreCase);\r
2016               if (n < 0)\r
2017                 continue;\r
2018 \r
2019               strSql = strSql.Substring(n + 4);\r
2020 \r
2021               using (SQLiteCommand cmd = new SQLiteCommand(strSql, this))\r
2022               using (SQLiteDataReader rdViewSelect = cmdViewSelect.ExecuteReader(CommandBehavior.SchemaOnly))\r
2023               using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader(CommandBehavior.SchemaOnly))\r
2024               using (DataTable tblSchemaView = rdViewSelect.GetSchemaTable(false, false))\r
2025               using (DataTable tblSchema = rd.GetSchemaTable(false, false))\r
2026               {\r
2027                 for (n = 0; n < tblSchema.Rows.Count; n++)\r
2028                 {\r
2029                   viewRow = tblSchemaView.Rows[n];\r
2030                   schemaRow = tblSchema.Rows[n];\r
2031 \r
2032                   if (String.Compare(viewRow[SchemaTableColumn.ColumnName].ToString(), strColumn, true, CultureInfo.InvariantCulture) == 0\r
2033                     || strColumn == null)\r
2034                   {\r
2035                     row = tbl.NewRow();\r
2036 \r
2037                     row["VIEW_CATALOG"] = strCatalog;\r
2038                     row["VIEW_NAME"] = rdViews.GetString(2);\r
2039                     row["TABLE_CATALOG"] = strCatalog;\r
2040                     row["TABLE_SCHEMA"] = schemaRow[SchemaTableColumn.BaseSchemaName];\r
2041                     row["TABLE_NAME"] = schemaRow[SchemaTableColumn.BaseTableName];\r
2042                     row["COLUMN_NAME"] = schemaRow[SchemaTableColumn.BaseColumnName];\r
2043                     row["VIEW_COLUMN_NAME"] = viewRow[SchemaTableColumn.ColumnName];\r
2044                     row["COLUMN_HASDEFAULT"] = (viewRow[SchemaTableOptionalColumn.DefaultValue] != DBNull.Value);\r
2045                     row["COLUMN_DEFAULT"] = viewRow[SchemaTableOptionalColumn.DefaultValue];\r
2046                     row["ORDINAL_POSITION"] = viewRow[SchemaTableColumn.ColumnOrdinal];\r
2047                     row["IS_NULLABLE"] = viewRow[SchemaTableColumn.AllowDBNull];\r
2048                     row["DATA_TYPE"] = viewRow["DataTypeName"]; // SQLiteConvert.DbTypeToType((DbType)viewRow[SchemaTableColumn.ProviderType]).ToString();\r
2049                     row["EDM_TYPE"] = SQLiteConvert.DbTypeToTypeName((DbType)viewRow[SchemaTableColumn.ProviderType]).ToString().ToLower(CultureInfo.InvariantCulture);\r
2050                     row["CHARACTER_MAXIMUM_LENGTH"] = viewRow[SchemaTableColumn.ColumnSize];\r
2051                     row["TABLE_SCHEMA"] = viewRow[SchemaTableColumn.BaseSchemaName];\r
2052                     row["PRIMARY_KEY"] = viewRow[SchemaTableColumn.IsKey];\r
2053                     row["AUTOINCREMENT"] = viewRow[SchemaTableOptionalColumn.IsAutoIncrement];\r
2054                     row["COLLATION_NAME"] = viewRow["CollationType"];\r
2055                     row["UNIQUE"] = viewRow[SchemaTableColumn.IsUnique];\r
2056                     tbl.Rows.Add(row);\r
2057                   }\r
2058                 }\r
2059               }\r
2060             }\r
2061           }\r
2062         }\r
2063       }\r
2064 \r
2065       tbl.EndLoadData();\r
2066       tbl.AcceptChanges();\r
2067 \r
2068       return tbl;\r
2069     }\r
2070 \r
2071     /// <summary>\r
2072     /// Retrieves foreign key information from the specified set of filters\r
2073     /// </summary>\r
2074     /// <param name="strCatalog">An optional catalog to restrict results on</param>\r
2075     /// <param name="strTable">An optional table to restrict results on</param>\r
2076     /// <param name="strKeyName">An optional foreign key name to restrict results on</param>\r
2077     /// <returns>A DataTable with the results of the query</returns>\r
2078     private DataTable Schema_ForeignKeys(string strCatalog, string strTable, string strKeyName)\r
2079     {\r
2080       DataTable tbl = new DataTable("ForeignKeys");\r
2081       DataRow row;\r
2082 \r
2083       tbl.Locale = CultureInfo.InvariantCulture;\r
2084       tbl.Columns.Add("CONSTRAINT_CATALOG", typeof(string));\r
2085       tbl.Columns.Add("CONSTRAINT_SCHEMA", typeof(string));\r
2086       tbl.Columns.Add("CONSTRAINT_NAME", typeof(string));\r
2087       tbl.Columns.Add("TABLE_CATALOG", typeof(string));\r
2088       tbl.Columns.Add("TABLE_SCHEMA", typeof(string));\r
2089       tbl.Columns.Add("TABLE_NAME", typeof(string));\r
2090       tbl.Columns.Add("CONSTRAINT_TYPE", typeof(string));\r
2091       tbl.Columns.Add("IS_DEFERRABLE", typeof(bool));\r
2092       tbl.Columns.Add("INITIALLY_DEFERRED", typeof(bool));\r
2093       tbl.Columns.Add("FKEY_FROM_COLUMN", typeof(string));\r
2094       tbl.Columns.Add("FKEY_FROM_ORDINAL_POSITION", typeof(int));\r
2095       tbl.Columns.Add("FKEY_TO_CATALOG", typeof(string));\r
2096       tbl.Columns.Add("FKEY_TO_SCHEMA", typeof(string));\r
2097       tbl.Columns.Add("FKEY_TO_TABLE", typeof(string));\r
2098       tbl.Columns.Add("FKEY_TO_COLUMN", typeof(string));\r
2099 \r
2100       if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main";\r
2101 \r
2102       string master = (String.Compare(strCatalog, "temp", true, CultureInfo.InvariantCulture) == 0) ? _tempmasterdb : _masterdb;\r
2103 \r
2104       tbl.BeginLoadData();\r
2105 \r
2106       using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))\r
2107       using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())\r
2108       {\r
2109         while (rdTables.Read())\r
2110         {\r
2111           if (String.IsNullOrEmpty(strTable) || String.Compare(strTable, rdTables.GetString(2), true, CultureInfo.InvariantCulture) == 0)\r
2112           {\r
2113             try\r
2114             {\r
2115               using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder())\r
2116               //using (SQLiteCommand cmdTable = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdTables.GetString(2)), this))\r
2117               //using (SQLiteDataReader rdTable = cmdTable.ExecuteReader(CommandBehavior.SchemaOnly))\r
2118               using (SQLiteCommand cmdKey = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].foreign_key_list([{1}])", strCatalog, rdTables.GetString(2)), this))\r
2119               using (SQLiteDataReader rdKey = cmdKey.ExecuteReader())\r
2120               {\r
2121                 while (rdKey.Read())\r
2122                 {\r
2123                   row = tbl.NewRow();\r
2124                   row["CONSTRAINT_CATALOG"] = strCatalog;\r
2125                   row["CONSTRAINT_NAME"] = String.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}", rdTables[2], rdKey.GetInt32(0));\r
2126                   row["TABLE_CATALOG"] = strCatalog;\r
2127                   row["TABLE_NAME"] = builder.UnquoteIdentifier(rdTables.GetString(2));\r
2128                   row["CONSTRAINT_TYPE"] = "FOREIGN KEY";\r
2129                   row["IS_DEFERRABLE"] = false;\r
2130                   row["INITIALLY_DEFERRED"] = false;\r
2131                   row["FKEY_FROM_COLUMN"] = builder.UnquoteIdentifier(rdKey[3].ToString());\r
2132                   row["FKEY_TO_CATALOG"] = strCatalog;\r
2133                   row["FKEY_TO_TABLE"] = builder.UnquoteIdentifier(rdKey[2].ToString());\r
2134                   row["FKEY_TO_COLUMN"] = builder.UnquoteIdentifier(rdKey[4].ToString());\r
2135                   row["FKEY_FROM_ORDINAL_POSITION"] = rdKey[1];\r
2136 \r
2137                   if (String.IsNullOrEmpty(strKeyName) || String.Compare(strKeyName, row["CONSTRAINT_NAME"].ToString(), true, CultureInfo.InvariantCulture) == 0)\r
2138                     tbl.Rows.Add(row);\r
2139                 }\r
2140               }\r
2141             }\r
2142             catch (SQLiteException)\r
2143             {\r
2144             }\r
2145           }\r
2146         }\r
2147       }\r
2148 \r
2149       tbl.EndLoadData();\r
2150       tbl.AcceptChanges();\r
2151 \r
2152       return tbl;\r
2153     }\r
2154 \r
2155     /// <summary>\r
2156     /// This event is raised whenever SQLite makes an update/delete/insert into the database on\r
2157     /// this connection.  It only applies to the given connection.\r
2158     /// </summary>\r
2159     public event SQLiteUpdateEventHandler Update\r
2160     {\r
2161       add\r
2162       {\r
2163         if (_updateHandler == null)\r
2164         {\r
2165           _updateCallback = new SQLiteUpdateCallback(UpdateCallback);\r
2166           if (_sql != null) _sql.SetUpdateHook(_updateCallback);\r
2167         }\r
2168         _updateHandler += value;\r
2169       }\r
2170       remove\r
2171       {\r
2172         _updateHandler -= value;\r
2173         if (_updateHandler == null)\r
2174         {\r
2175           if (_sql != null) _sql.SetUpdateHook(null);\r
2176           _updateCallback = null;\r
2177         }\r
2178       }\r
2179     }\r
2180 \r
2181     private void UpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid)\r
2182     {\r
2183       _updateHandler(this, new UpdateEventArgs(\r
2184         SQLiteBase.UTF8ToString(database, -1),\r
2185         SQLiteBase.UTF8ToString(table, -1),\r
2186         (UpdateEventType)type,\r
2187         rowid));\r
2188     }\r
2189 \r
2190     /// <summary>\r
2191     /// This event is raised whenever SQLite is committing a transaction.\r
2192     /// Return non-zero to trigger a rollback\r
2193     /// </summary>\r
2194     public event SQLiteCommitHandler Commit\r
2195     {\r
2196       add\r
2197       {\r
2198         if (_commitHandler == null)\r
2199         {\r
2200           _commitCallback = new SQLiteCommitCallback(CommitCallback);\r
2201           if (_sql != null) _sql.SetCommitHook(_commitCallback);\r
2202         }\r
2203         _commitHandler += value;\r
2204       }\r
2205       remove\r
2206       {\r
2207         _commitHandler -= value;\r
2208         if (_commitHandler == null)\r
2209         {\r
2210           if (_sql != null) _sql.SetCommitHook(null);\r
2211           _commitCallback = null;\r
2212         }\r
2213       }\r
2214     }\r
2215 \r
2216     /// <summary>\r
2217     /// This event is raised whenever SQLite is committing a transaction.\r
2218     /// Return non-zero to trigger a rollback\r
2219     /// </summary>\r
2220     public event EventHandler RollBack\r
2221     {\r
2222       add\r
2223       {\r
2224         if (_rollbackHandler == null)\r
2225         {\r
2226           _rollbackCallback = new SQLiteRollbackCallback(RollbackCallback);\r
2227           if (_sql != null) _sql.SetRollbackHook(_rollbackCallback);\r
2228         }\r
2229         _rollbackHandler += value;\r
2230       }\r
2231       remove\r
2232       {\r
2233         _rollbackHandler -= value;\r
2234         if (_rollbackHandler == null)\r
2235         {\r
2236           if (_sql != null) _sql.SetRollbackHook(null);\r
2237           _rollbackCallback = null;\r
2238         }\r
2239       }\r
2240     }\r
2241 \r
2242 \r
2243     private int CommitCallback(IntPtr parg)\r
2244     {\r
2245       CommitEventArgs e = new CommitEventArgs();\r
2246       _commitHandler(this, e);\r
2247       return (e.AbortTransaction == true) ? 1 : 0;\r
2248     }\r
2249 \r
2250     private void RollbackCallback(IntPtr parg)\r
2251     {\r
2252       _rollbackHandler(this, EventArgs.Empty);\r
2253     }\r
2254   }\r
2255 \r
2256   /// <summary>\r
2257   /// The I/O file cache flushing behavior for the connection\r
2258   /// </summary>\r
2259   public enum SynchronizationModes\r
2260   {\r
2261     /// <summary>\r
2262     /// Normal file flushing at critical sections of the code\r
2263     /// </summary>\r
2264     Normal = 0,\r
2265     /// <summary>\r
2266     /// Full file flushing after every write operation\r
2267     /// </summary>\r
2268     Full = 1,\r
2269     /// <summary>\r
2270     /// Use the default operating system's file flushing, SQLite does not explicitly flush the file buffers after writing\r
2271     /// </summary>\r
2272     Off = 2,\r
2273   }\r
2274 \r
2275 #if !PLATFORM_COMPACTFRAMEWORK\r
2276   [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\r
2277 #endif\r
2278   internal delegate void SQLiteUpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid);\r
2279 #if !PLATFORM_COMPACTFRAMEWORK\r
2280   [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\r
2281 #endif\r
2282   internal delegate int SQLiteCommitCallback(IntPtr puser);\r
2283 #if !PLATFORM_COMPACTFRAMEWORK\r
2284   [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\r
2285 #endif\r
2286   internal delegate void SQLiteRollbackCallback(IntPtr puser);\r
2287 \r
2288   /// <summary>\r
2289   /// Raised when a transaction is about to be committed.  To roll back a transaction, set the \r
2290   /// rollbackTrans boolean value to true.\r
2291   /// </summary>\r
2292   /// <param name="sender">The connection committing the transaction</param>\r
2293   /// <param name="e">Event arguments on the transaction</param>\r
2294   public delegate void SQLiteCommitHandler(object sender, CommitEventArgs e);\r
2295 \r
2296   /// <summary>\r
2297   /// Raised when data is inserted, updated and deleted on a given connection\r
2298   /// </summary>\r
2299   /// <param name="sender">The connection committing the transaction</param>\r
2300   /// <param name="e">The event parameters which triggered the event</param>\r
2301   public delegate void SQLiteUpdateEventHandler(object sender, UpdateEventArgs e);\r
2302 \r
2303   /// <summary>\r
2304   /// Whenever an update event is triggered on a connection, this enum will indicate\r
2305   /// exactly what type of operation is being performed.\r
2306   /// </summary>\r
2307   public enum UpdateEventType\r
2308   {\r
2309     /// <summary>\r
2310     /// A row is being deleted from the given database and table\r
2311     /// </summary>\r
2312     Delete = 9,\r
2313     /// <summary>\r
2314     /// A row is being inserted into the table.\r
2315     /// </summary>\r
2316     Insert = 18,\r
2317     /// <summary>\r
2318     /// A row is being updated in the table.\r
2319     /// </summary>\r
2320     Update = 23,\r
2321   }\r
2322 \r
2323   /// <summary>\r
2324   /// Passed during an Update callback, these event arguments detail the type of update operation being performed\r
2325   /// on the given connection.\r
2326   /// </summary>\r
2327   public class UpdateEventArgs : EventArgs\r
2328   {\r
2329     /// <summary>\r
2330     /// The name of the database being updated (usually "main" but can be any attached or temporary database)\r
2331     /// </summary>\r
2332     public readonly string Database;\r
2333 \r
2334     /// <summary>\r
2335     /// The name of the table being updated\r
2336     /// </summary>\r
2337     public readonly string Table;\r
2338 \r
2339     /// <summary>\r
2340     /// The type of update being performed (insert/update/delete)\r
2341     /// </summary>\r
2342     public readonly UpdateEventType Event;\r
2343 \r
2344     /// <summary>\r
2345     /// The RowId affected by this update.\r
2346     /// </summary>\r
2347     public readonly Int64 RowId;\r
2348 \r
2349     internal UpdateEventArgs(string database, string table, UpdateEventType eventType, Int64 rowid)\r
2350     {\r
2351       Database = database;\r
2352       Table = table;\r
2353       Event = eventType;\r
2354       RowId = rowid;\r
2355     }\r
2356   }\r
2357 \r
2358   /// <summary>\r
2359   /// Event arguments raised when a transaction is being committed\r
2360   /// </summary>\r
2361   public class CommitEventArgs : EventArgs\r
2362   {\r
2363     internal CommitEventArgs()\r
2364     {\r
2365     }\r
2366 \r
2367     /// <summary>\r
2368     /// Set to true to abort the transaction and trigger a rollback\r
2369     /// </summary>\r
2370     public bool AbortTransaction;\r
2371   }\r
2372 \r
2373 }\r