PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteConnection.cs

https://bitbucket.org/danipen/mono
C# | 2428 lines | 1659 code | 196 blank | 573 comment | 233 complexity | dd19fad745cccb87e22d48ef7d171bb0 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /********************************************************
  2. * ADO.NET 2.0 Data Provider for SQLite Version 3.X
  3. * Written by Robert Simpson (robert@blackcastlesoft.com)
  4. *
  5. * Released to the public domain, use at your own risk!
  6. ********************************************************/
  7. namespace Mono.Data.Sqlite
  8. {
  9. using System;
  10. using System.Data;
  11. using System.Data.Common;
  12. using System.Collections.Generic;
  13. using System.Globalization;
  14. using System.ComponentModel;
  15. using System.Text;
  16. using System.Runtime.InteropServices;
  17. using System.IO;
  18. /// <summary>
  19. /// SQLite implentation of DbConnection.
  20. /// </summary>
  21. /// <remarks>
  22. /// The <see cref="ConnectionString">ConnectionString</see> property of the SqliteConnection class can contain the following parameter(s), delimited with a semi-colon:
  23. /// <list type="table">
  24. /// <listheader>
  25. /// <term>Parameter</term>
  26. /// <term>Values</term>
  27. /// <term>Required</term>
  28. /// <term>Default</term>
  29. /// </listheader>
  30. /// <item>
  31. /// <description>Data Source</description>
  32. /// <description>{filename}</description>
  33. /// <description>Y</description>
  34. /// <description></description>
  35. /// </item>
  36. /// <item>
  37. /// <description>Version</description>
  38. /// <description>3</description>
  39. /// <description>N</description>
  40. /// <description>3</description>
  41. /// </item>
  42. /// <item>
  43. /// <description>UseUTF16Encoding</description>
  44. /// <description><b>True</b><br/><b>False</b></description>
  45. /// <description>N</description>
  46. /// <description>False</description>
  47. /// </item>
  48. /// <item>
  49. /// <description>DateTimeFormat</description>
  50. /// <description><b>Ticks</b> - Use DateTime.Ticks<br/><b>ISO8601</b> - Use ISO8601 DateTime format</description>
  51. /// <description>N</description>
  52. /// <description>ISO8601</description>
  53. /// </item>
  54. /// <item>
  55. /// <description>BinaryGUID</description>
  56. /// <description><b>True</b> - Store GUID columns in binary form<br/><b>False</b> - Store GUID columns as text</description>
  57. /// <description>N</description>
  58. /// <description>True</description>
  59. /// </item>
  60. /// <item>
  61. /// <description>Cache Size</description>
  62. /// <description>{size in bytes}</description>
  63. /// <description>N</description>
  64. /// <description>2000</description>
  65. /// </item>
  66. /// <item>
  67. /// <description>Synchronous</description>
  68. /// <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>
  69. /// <description>N</description>
  70. /// <description>Normal</description>
  71. /// </item>
  72. /// <item>
  73. /// <description>Page Size</description>
  74. /// <description>{size in bytes}</description>
  75. /// <description>N</description>
  76. /// <description>1024</description>
  77. /// </item>
  78. /// <item>
  79. /// <description>Password</description>
  80. /// <description>{password}</description>
  81. /// <description>N</description>
  82. /// <description></description>
  83. /// </item>
  84. /// <item>
  85. /// <description>Enlist</description>
  86. /// <description><b>Y</b> - Automatically enlist in distributed transactions<br/><b>N</b> - No automatic enlistment</description>
  87. /// <description>N</description>
  88. /// <description>Y</description>
  89. /// </item>
  90. /// <item>
  91. /// <description>Pooling</description>
  92. /// <description><b>True</b> - Use connection pooling<br/><b>False</b> - Do not use connection pooling</description>
  93. /// <description>N</description>
  94. /// <description>False</description>
  95. /// </item>
  96. /// <item>
  97. /// <description>FailIfMissing</description>
  98. /// <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>
  99. /// <description>N</description>
  100. /// <description>False</description>
  101. /// </item>
  102. /// <item>
  103. /// <description>Max Page Count</description>
  104. /// <description>{size in pages} - Limits the maximum number of pages (limits the size) of the database</description>
  105. /// <description>N</description>
  106. /// <description>0</description>
  107. /// </item>
  108. /// <item>
  109. /// <description>Legacy Format</description>
  110. /// <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>
  111. /// <description>N</description>
  112. /// <description>False</description>
  113. /// </item>
  114. /// <item>
  115. /// <description>Default Timeout</description>
  116. /// <description>{time in seconds}<br/>The default command timeout</description>
  117. /// <description>N</description>
  118. /// <description>30</description>
  119. /// </item>
  120. /// <item>
  121. /// <description>Journal Mode</description>
  122. /// <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>
  123. /// <description>N</description>
  124. /// <description>Delete</description>
  125. /// </item>
  126. /// <item>
  127. /// <description>Read Only</description>
  128. /// <description><b>True</b> - Open the database for read only access<br/><b>False</b> - Open the database for normal read/write access</description>
  129. /// <description>N</description>
  130. /// <description>False</description>
  131. /// </item>
  132. /// <item>
  133. /// <description>Max Pool Size</description>
  134. /// <description>The maximum number of connections for the given connection string that can be in the connection pool</description>
  135. /// <description>N</description>
  136. /// <description>100</description>
  137. /// </item>
  138. /// <item>
  139. /// <description>Default IsolationLevel</description>
  140. /// <description>The default transaciton isolation level</description>
  141. /// <description>N</description>
  142. /// <description>Serializable</description>
  143. /// </item>
  144. /// </list>
  145. /// </remarks>
  146. public sealed partial class SqliteConnection : DbConnection, ICloneable
  147. {
  148. private const string _dataDirectory = "|DataDirectory|";
  149. private const string _masterdb = "sqlite_master";
  150. private const string _tempmasterdb = "sqlite_temp_master";
  151. /// <summary>
  152. /// State of the current connection
  153. /// </summary>
  154. private ConnectionState _connectionState;
  155. /// <summary>
  156. /// The connection string
  157. /// </summary>
  158. private string _connectionString;
  159. /// <summary>
  160. /// Nesting level of the transactions open on the connection
  161. /// </summary>
  162. internal int _transactionLevel;
  163. /// <summary>
  164. /// The default isolation level for new transactions
  165. /// </summary>
  166. private IsolationLevel _defaultIsolation;
  167. #if !PLATFORM_COMPACTFRAMEWORK
  168. /// <summary>
  169. /// Whether or not the connection is enlisted in a distrubuted transaction
  170. /// </summary>
  171. internal SQLiteEnlistment _enlistment;
  172. #endif
  173. /// <summary>
  174. /// The base SQLite object to interop with
  175. /// </summary>
  176. internal SQLiteBase _sql;
  177. /// <summary>
  178. /// The database filename minus path and extension
  179. /// </summary>
  180. private string _dataSource;
  181. /// <summary>
  182. /// Temporary password storage, emptied after the database has been opened
  183. /// </summary>
  184. private byte[] _password;
  185. /// <summary>
  186. /// Default command timeout
  187. /// </summary>
  188. private int _defaultTimeout = 30;
  189. internal bool _binaryGuid;
  190. internal long _version;
  191. private event SQLiteUpdateEventHandler _updateHandler;
  192. private event SQLiteCommitHandler _commitHandler;
  193. private event EventHandler _rollbackHandler;
  194. private SQLiteUpdateCallback _updateCallback;
  195. private SQLiteCommitCallback _commitCallback;
  196. private SQLiteRollbackCallback _rollbackCallback;
  197. /// <summary>
  198. /// This event is raised whenever the database is opened or closed.
  199. /// </summary>
  200. public override event StateChangeEventHandler StateChange;
  201. ///<overloads>
  202. /// Constructs a new SqliteConnection object
  203. /// </overloads>
  204. /// <summary>
  205. /// Default constructor
  206. /// </summary>
  207. public SqliteConnection()
  208. : this("")
  209. {
  210. }
  211. /// <summary>
  212. /// Initializes the connection with the specified connection string
  213. /// </summary>
  214. /// <param name="connectionString">The connection string to use on the connection</param>
  215. public SqliteConnection(string connectionString)
  216. {
  217. _sql = null;
  218. _connectionState = ConnectionState.Closed;
  219. _connectionString = "";
  220. _transactionLevel = 0;
  221. _version = 0;
  222. //_commandList = new List<WeakReference>();
  223. if (connectionString != null)
  224. ConnectionString = connectionString;
  225. }
  226. /// <summary>
  227. /// Clones the settings and connection string from an existing connection. If the existing connection is already open, this
  228. /// function will open its own connection, enumerate any attached databases of the original connection, and automatically
  229. /// attach to them.
  230. /// </summary>
  231. /// <param name="connection"></param>
  232. public SqliteConnection(SqliteConnection connection)
  233. : this(connection.ConnectionString)
  234. {
  235. string str;
  236. if (connection.State == ConnectionState.Open)
  237. {
  238. Open();
  239. // Reattach all attached databases from the existing connection
  240. using (DataTable tbl = connection.GetSchema("Catalogs"))
  241. {
  242. foreach (DataRow row in tbl.Rows)
  243. {
  244. str = row[0].ToString();
  245. if (String.Compare(str, "main", true, CultureInfo.InvariantCulture) != 0
  246. && String.Compare(str, "temp", true, CultureInfo.InvariantCulture) != 0)
  247. {
  248. using (SqliteCommand cmd = CreateCommand())
  249. {
  250. cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "ATTACH DATABASE '{0}' AS [{1}]", row[1], row[0]);
  251. cmd.ExecuteNonQuery();
  252. }
  253. }
  254. }
  255. }
  256. }
  257. }
  258. #if PLATFORM_COMPACTFRAMEWORK
  259. /// <summary>
  260. /// Obsolete
  261. /// </summary>
  262. public override int ConnectionTimeout
  263. {
  264. get
  265. {
  266. return 30;
  267. }
  268. }
  269. #endif
  270. /// <summary>
  271. /// Creates a clone of the connection. All attached databases and user-defined functions are cloned. If the existing connection is open, the cloned connection
  272. /// will also be opened.
  273. /// </summary>
  274. /// <returns></returns>
  275. public object Clone()
  276. {
  277. return new SqliteConnection(this);
  278. }
  279. /// <summary>
  280. /// Disposes of the SqliteConnection, closing it if it is active.
  281. /// </summary>
  282. /// <param name="disposing">True if the connection is being explicitly closed.</param>
  283. protected override void Dispose(bool disposing)
  284. {
  285. base.Dispose(disposing);
  286. if (_sql != null)
  287. _sql.Dispose ();
  288. if (disposing)
  289. Close();
  290. }
  291. /// <summary>
  292. /// Creates a database file. This just creates a zero-byte file which SQLite
  293. /// will turn into a database when the file is opened properly.
  294. /// </summary>
  295. /// <param name="databaseFileName">The file to create</param>
  296. static public void CreateFile(string databaseFileName)
  297. {
  298. FileStream fs = File.Create(databaseFileName);
  299. fs.Close();
  300. }
  301. #if !SQLITE_STANDARD
  302. /// <summary>
  303. /// On NTFS volumes, this function turns on the compression attribute for the given file.
  304. /// It must not be open or referenced at the time of the function call.
  305. /// </summary>
  306. /// <param name="databaseFileName">The file to compress</param>
  307. [Obsolete("This functionality is being removed from a future version of the SQLite provider")]
  308. static public void CompressFile(string databaseFileName)
  309. {
  310. UnsafeNativeMethods.sqlite3_compressfile(databaseFileName);
  311. }
  312. #endif
  313. #if !SQLITE_STANDARD
  314. /// <summary>
  315. /// On NTFS volumes, this function removes the compression attribute for the given file.
  316. /// It must not be open or referenced at the time of the function call.
  317. /// </summary>
  318. /// <param name="databaseFileName">The file to decompress</param>
  319. [Obsolete("This functionality is being removed from a future version of the SQLite provider")]
  320. static public void DecompressFile(string databaseFileName)
  321. {
  322. UnsafeNativeMethods.sqlite3_decompressfile(databaseFileName);
  323. }
  324. #endif
  325. /// <summary>
  326. /// Raises the state change event when the state of the connection changes
  327. /// </summary>
  328. /// <param name="newState">The new state. If it is different from the previous state, an event is raised.</param>
  329. internal void OnStateChange(ConnectionState newState)
  330. {
  331. ConnectionState oldState = _connectionState;
  332. _connectionState = newState;
  333. if (StateChange != null && oldState != newState)
  334. {
  335. StateChangeEventArgs e = new StateChangeEventArgs(oldState, newState);
  336. StateChange(this, e);
  337. }
  338. }
  339. /// <summary>
  340. /// OBSOLETE. Creates a new SqliteTransaction if one isn't already active on the connection.
  341. /// </summary>
  342. /// <param name="isolationLevel">This parameter is ignored.</param>
  343. /// <param name="deferredLock">When TRUE, SQLite defers obtaining a write lock until a write operation is requested.
  344. /// When FALSE, a writelock is obtained immediately. The default is TRUE, but in a multi-threaded multi-writer
  345. /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.</param>
  346. /// <returns>Returns a SqliteTransaction object.</returns>
  347. [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]
  348. public SqliteTransaction BeginTransaction(IsolationLevel isolationLevel, bool deferredLock)
  349. {
  350. return (SqliteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted);
  351. }
  352. /// <summary>
  353. /// OBSOLETE. Creates a new SqliteTransaction if one isn't already active on the connection.
  354. /// </summary>
  355. /// <param name="deferredLock">When TRUE, SQLite defers obtaining a write lock until a write operation is requested.
  356. /// When FALSE, a writelock is obtained immediately. The default is false, but in a multi-threaded multi-writer
  357. /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.</param>
  358. /// <returns>Returns a SqliteTransaction object.</returns>
  359. [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]
  360. public SqliteTransaction BeginTransaction(bool deferredLock)
  361. {
  362. return (SqliteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted);
  363. }
  364. /// <summary>
  365. /// Creates a new SqliteTransaction if one isn't already active on the connection.
  366. /// </summary>
  367. /// <param name="isolationLevel">Supported isolation levels are Serializable, ReadCommitted and Unspecified.</param>
  368. /// <remarks>
  369. /// Unspecified will use the default isolation level specified in the connection string. If no isolation level is specified in the
  370. /// connection string, Serializable is used.
  371. /// Serializable transactions are the default. In this mode, the engine gets an immediate lock on the database, and no other threads
  372. /// may begin a transaction. Other threads may read from the database, but not write.
  373. /// With a ReadCommitted isolation level, locks are deferred and elevated as needed. It is possible for multiple threads to start
  374. /// a transaction in ReadCommitted mode, but if a thread attempts to commit a transaction while another thread
  375. /// has a ReadCommitted lock, it may timeout or cause a deadlock on both threads until both threads' CommandTimeout's are reached.
  376. /// </remarks>
  377. /// <returns>Returns a SqliteTransaction object.</returns>
  378. public new SqliteTransaction BeginTransaction(IsolationLevel isolationLevel)
  379. {
  380. return (SqliteTransaction)BeginDbTransaction(isolationLevel);
  381. }
  382. /// <summary>
  383. /// Creates a new SqliteTransaction if one isn't already active on the connection.
  384. /// </summary>
  385. /// <returns>Returns a SqliteTransaction object.</returns>
  386. public new SqliteTransaction BeginTransaction()
  387. {
  388. return (SqliteTransaction)BeginDbTransaction(_defaultIsolation);
  389. }
  390. /// <summary>
  391. /// Forwards to the local BeginTransaction() function
  392. /// </summary>
  393. /// <param name="isolationLevel">Supported isolation levels are Unspecified, Serializable, and ReadCommitted</param>
  394. /// <returns></returns>
  395. protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
  396. {
  397. if (_connectionState != ConnectionState.Open)
  398. throw new InvalidOperationException();
  399. if (isolationLevel == IsolationLevel.Unspecified) isolationLevel = _defaultIsolation;
  400. if (isolationLevel != IsolationLevel.Serializable && isolationLevel != IsolationLevel.ReadCommitted)
  401. throw new ArgumentException("isolationLevel");
  402. return new SqliteTransaction(this, isolationLevel != IsolationLevel.Serializable);
  403. }
  404. /// <summary>
  405. /// Not implemented
  406. /// </summary>
  407. /// <param name="databaseName"></param>
  408. public override void ChangeDatabase(string databaseName)
  409. {
  410. throw new NotImplementedException();
  411. }
  412. /// <summary>
  413. /// When the database connection is closed, all commands linked to this connection are automatically reset.
  414. /// </summary>
  415. public override void Close()
  416. {
  417. if (_sql != null)
  418. {
  419. #if !PLATFORM_COMPACTFRAMEWORK
  420. if (_enlistment != null)
  421. {
  422. // If the connection is enlisted in a transaction scope and the scope is still active,
  423. // we cannot truly shut down this connection until the scope has completed. Therefore make a
  424. // hidden connection temporarily to hold open the connection until the scope has completed.
  425. SqliteConnection cnn = new SqliteConnection();
  426. cnn._sql = _sql;
  427. cnn._transactionLevel = _transactionLevel;
  428. cnn._enlistment = _enlistment;
  429. cnn._connectionState = _connectionState;
  430. cnn._version = _version;
  431. cnn._enlistment._transaction._cnn = cnn;
  432. cnn._enlistment._disposeConnection = true;
  433. _sql = null;
  434. _enlistment = null;
  435. }
  436. #endif
  437. if (_sql != null)
  438. {
  439. _sql.Close();
  440. }
  441. _sql = null;
  442. _transactionLevel = 0;
  443. }
  444. OnStateChange(ConnectionState.Closed);
  445. }
  446. /// <summary>
  447. /// Clears the connection pool associated with the connection. Any other active connections using the same database file
  448. /// will be discarded instead of returned to the pool when they are closed.
  449. /// </summary>
  450. /// <param name="connection"></param>
  451. public static void ClearPool(SqliteConnection connection)
  452. {
  453. if (connection._sql == null) return;
  454. connection._sql.ClearPool();
  455. }
  456. /// <summary>
  457. /// Clears all connection pools. Any active connections will be discarded instead of sent to the pool when they are closed.
  458. /// </summary>
  459. public static void ClearAllPools()
  460. {
  461. SqliteConnectionPool.ClearAllPools();
  462. }
  463. /// <summary>
  464. /// The connection string containing the parameters for the connection
  465. /// </summary>
  466. /// <remarks>
  467. /// <list type="table">
  468. /// <listheader>
  469. /// <term>Parameter</term>
  470. /// <term>Values</term>
  471. /// <term>Required</term>
  472. /// <term>Default</term>
  473. /// </listheader>
  474. /// <item>
  475. /// <description>Data Source</description>
  476. /// <description>{filename}</description>
  477. /// <description>Y</description>
  478. /// <description></description>
  479. /// </item>
  480. /// <item>
  481. /// <description>Version</description>
  482. /// <description>3</description>
  483. /// <description>N</description>
  484. /// <description>3</description>
  485. /// </item>
  486. /// <item>
  487. /// <description>UseUTF16Encoding</description>
  488. /// <description><b>True</b><br/><b>False</b></description>
  489. /// <description>N</description>
  490. /// <description>False</description>
  491. /// </item>
  492. /// <item>
  493. /// <description>DateTimeFormat</description>
  494. /// <description><b>Ticks</b> - Use DateTime.Ticks<br/><b>ISO8601</b> - Use ISO8601 DateTime format<br/><b>JulianDay</b> - Use JulianDay format</description>
  495. /// <description>N</description>
  496. /// <description>ISO8601</description>
  497. /// </item>
  498. /// <item>
  499. /// <description>BinaryGUID</description>
  500. /// <description><b>Yes/On/1</b> - Store GUID columns in binary form<br/><b>No/Off/0</b> - Store GUID columns as text</description>
  501. /// <description>N</description>
  502. /// <description>On</description>
  503. /// </item>
  504. /// <item>
  505. /// <description>Cache Size</description>
  506. /// <description>{size in bytes}</description>
  507. /// <description>N</description>
  508. /// <description>2000</description>
  509. /// </item>
  510. /// <item>
  511. /// <description>Synchronous</description>
  512. /// <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>
  513. /// <description>N</description>
  514. /// <description>Normal</description>
  515. /// </item>
  516. /// <item>
  517. /// <description>Page Size</description>
  518. /// <description>{size in bytes}</description>
  519. /// <description>N</description>
  520. /// <description>1024</description>
  521. /// </item>
  522. /// <item>
  523. /// <description>Password</description>
  524. /// <description>{password}</description>
  525. /// <description>N</description>
  526. /// <description></description>
  527. /// </item>
  528. /// <item>
  529. /// <description>Enlist</description>
  530. /// <description><B>Y</B> - Automatically enlist in distributed transactions<br/><b>N</b> - No automatic enlistment</description>
  531. /// <description>N</description>
  532. /// <description>Y</description>
  533. /// </item>
  534. /// <item>
  535. /// <description>Pooling</description>
  536. /// <description><b>True</b> - Use connection pooling<br/><b>False</b> - Do not use connection pooling</description>
  537. /// <description>N</description>
  538. /// <description>False</description>
  539. /// </item>
  540. /// <item>
  541. /// <description>FailIfMissing</description>
  542. /// <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>
  543. /// <description>N</description>
  544. /// <description>False</description>
  545. /// </item>
  546. /// <item>
  547. /// <description>Max Page Count</description>
  548. /// <description>{size in pages} - Limits the maximum number of pages (limits the size) of the database</description>
  549. /// <description>N</description>
  550. /// <description>0</description>
  551. /// </item>
  552. /// <item>
  553. /// <description>Legacy Format</description>
  554. /// <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>
  555. /// <description>N</description>
  556. /// <description>False</description>
  557. /// </item>
  558. /// <item>
  559. /// <description>Default Timeout</description>
  560. /// <description>{time in seconds}<br/>The default command timeout</description>
  561. /// <description>N</description>
  562. /// <description>30</description>
  563. /// </item>
  564. /// <item>
  565. /// <description>Journal Mode</description>
  566. /// <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>
  567. /// <description>N</description>
  568. /// <description>Delete</description>
  569. /// </item>
  570. /// <item>
  571. /// <description>Read Only</description>
  572. /// <description><b>True</b> - Open the database for read only access<br/><b>False</b> - Open the database for normal read/write access</description>
  573. /// <description>N</description>
  574. /// <description>False</description>
  575. /// </item>
  576. /// <item>
  577. /// <description>Max Pool Size</description>
  578. /// <description>The maximum number of connections for the given connection string that can be in the connection pool</description>
  579. /// <description>N</description>
  580. /// <description>100</description>
  581. /// </item>
  582. /// <item>
  583. /// <description>Default IsolationLevel</description>
  584. /// <description>The default transaciton isolation level</description>
  585. /// <description>N</description>
  586. /// <description>Serializable</description>
  587. /// </item>
  588. /// </list>
  589. /// </remarks>
  590. #if !PLATFORM_COMPACTFRAMEWORK
  591. [RefreshProperties(RefreshProperties.All), DefaultValue("")]
  592. [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")]
  593. #endif
  594. public override string ConnectionString
  595. {
  596. get
  597. {
  598. return _connectionString;
  599. }
  600. set
  601. {
  602. if (value == null)
  603. throw new ArgumentNullException();
  604. else if (_connectionState != ConnectionState.Closed)
  605. throw new InvalidOperationException();
  606. _connectionString = value;
  607. }
  608. }
  609. /// <summary>
  610. /// Create a new SqliteCommand and associate it with this connection.
  611. /// </summary>
  612. /// <returns>Returns an instantiated SqliteCommand object already assigned to this connection.</returns>
  613. public new SqliteCommand CreateCommand()
  614. {
  615. return new SqliteCommand(this);
  616. }
  617. /// <summary>
  618. /// Forwards to the local CreateCommand() function
  619. /// </summary>
  620. /// <returns></returns>
  621. protected override DbCommand CreateDbCommand()
  622. {
  623. return CreateCommand();
  624. }
  625. /// <summary>
  626. /// Returns the filename without extension or path
  627. /// </summary>
  628. #if !PLATFORM_COMPACTFRAMEWORK
  629. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  630. #endif
  631. public override string DataSource
  632. {
  633. get
  634. {
  635. return _dataSource;
  636. }
  637. }
  638. /// <summary>
  639. /// Returns an empty string
  640. /// </summary>
  641. #if !PLATFORM_COMPACTFRAMEWORK
  642. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  643. #endif
  644. public override string Database
  645. {
  646. get
  647. {
  648. return "main";
  649. }
  650. }
  651. /// <summary>
  652. /// Maps mono-specific connection string keywords to the standard ones
  653. /// </summary>
  654. /// <returns>The mapped keyword name</returns>
  655. internal static void MapMonoKeyword (string[] arPiece, SortedList<string, string> ls)
  656. {
  657. string keyword, value;
  658. switch (arPiece[0].ToLower (CultureInfo.InvariantCulture)) {
  659. case "uri":
  660. keyword = "Data Source";
  661. value = MapMonoUriPath (arPiece[1]);
  662. break;
  663. default:
  664. keyword = arPiece[0];
  665. value = arPiece[1];
  666. break;
  667. }
  668. ls.Add(keyword, value);
  669. }
  670. internal static string MapMonoUriPath (string path)
  671. {
  672. if (path.StartsWith ("file://")) {
  673. return path.Substring (7);
  674. } else if (path.StartsWith ("file:")) {
  675. return path.Substring (5);
  676. } else if (path.StartsWith ("/")) {
  677. return path;
  678. } else {
  679. throw new InvalidOperationException ("Invalid connection string: invalid URI");
  680. }
  681. }
  682. internal static string MapUriPath(string path)
  683. {
  684. if (path.StartsWith ("file://"))
  685. return path.Substring (7);
  686. else if (path.StartsWith ("file:"))
  687. return path.Substring (5);
  688. else if (path.StartsWith ("/"))
  689. return path;
  690. else
  691. throw new InvalidOperationException ("Invalid connection string: invalid URI");
  692. }
  693. /// <summary>
  694. /// Parses the connection string into component parts
  695. /// </summary>
  696. /// <param name="connectionString">The connection string to parse</param>
  697. /// <returns>An array of key-value pairs representing each parameter of the connection string</returns>
  698. internal static SortedList<string, string> ParseConnectionString(string connectionString)
  699. {
  700. string s = connectionString.Replace (',', ';'); // Mono compatibility
  701. int n;
  702. SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase);
  703. // First split into semi-colon delimited values. The Split() function of SQLiteBase accounts for and properly
  704. // skips semi-colons in quoted strings
  705. string[] arParts = SqliteConvert.Split(s, ';');
  706. string[] arPiece;
  707. int x = arParts.Length;
  708. // For each semi-colon piece, split into key and value pairs by the presence of the = sign
  709. for (n = 0; n < x; n++)
  710. {
  711. arPiece = SqliteConvert.Split(arParts[n], '=');
  712. if (arPiece.Length == 2)
  713. {
  714. MapMonoKeyword (arPiece, ls);
  715. }
  716. else throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Invalid ConnectionString format for parameter \"{0}\"", (arPiece.Length > 0) ? arPiece[0] : "null"));
  717. }
  718. return ls;
  719. }
  720. #if !PLATFORM_COMPACTFRAMEWORK
  721. /// <summary>
  722. /// Manual distributed transaction enlistment support
  723. /// </summary>
  724. /// <param name="transaction">The distributed transaction to enlist in</param>
  725. public override void EnlistTransaction(System.Transactions.Transaction transaction)
  726. {
  727. if (_transactionLevel > 0 && transaction != null)
  728. throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");
  729. if (_enlistment != null && transaction != _enlistment._scope)
  730. throw new ArgumentException("Already enlisted in a transaction");
  731. _enlistment = new SQLiteEnlistment(this, transaction);
  732. }
  733. #endif
  734. /// <summary>
  735. /// Looks for a key in the array of key/values of the parameter string. If not found, return the specified default value
  736. /// </summary>
  737. /// <param name="items">The list to look in</param>
  738. /// <param name="key">The key to find</param>
  739. /// <param name="defValue">The default value to return if the key is not found</param>
  740. /// <returns>The value corresponding to the specified key, or the default value if not found.</returns>
  741. static internal string FindKey(SortedList<string, string> items, string key, string defValue)
  742. {
  743. string ret;
  744. if (items.TryGetValue(key, out ret)) return ret;
  745. return defValue;
  746. }
  747. /// <summary>
  748. /// Opens the connection using the parameters found in the <see cref="ConnectionString">ConnectionString</see>
  749. /// </summary>
  750. public override void Open()
  751. {
  752. if (_connectionState != ConnectionState.Closed)
  753. throw new InvalidOperationException();
  754. Close();
  755. SortedList<string, string> opts = ParseConnectionString(_connectionString);
  756. string fileName;
  757. if (Convert.ToInt32(FindKey(opts, "Version", "3"), CultureInfo.InvariantCulture) != 3)
  758. throw new NotSupportedException("Only SQLite Version 3 is supported at this time");
  759. fileName = FindKey(opts, "Data Source", "");
  760. if (String.IsNullOrEmpty(fileName))
  761. {
  762. fileName = FindKey(opts, "Uri", "");
  763. if (String.IsNullOrEmpty(fileName))
  764. throw new ArgumentException("Data Source cannot be empty. Use :memory: to open an in-memory database");
  765. else
  766. fileName = MapUriPath(fileName);
  767. }
  768. if (String.Compare(fileName, ":MEMORY:", true, CultureInfo.InvariantCulture) == 0)
  769. fileName = ":memory:";
  770. else
  771. {
  772. #if PLATFORM_COMPACTFRAMEWORK
  773. if (fileName.StartsWith(".\\"))
  774. fileName = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase) + fileName.Substring(1);
  775. #endif
  776. fileName = ExpandFileName(fileName);
  777. }
  778. try
  779. {
  780. bool usePooling = (SqliteConvert.ToBoolean(FindKey(opts, "Pooling", Boolean.FalseString)) == true);
  781. bool bUTF16 = (SqliteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true);
  782. int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", "100"));
  783. _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", "30"), CultureInfo.CurrentCulture);
  784. _defaultIsolation = (IsolationLevel)Enum.Parse(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", "Serializable"), true);
  785. if (_defaultIsolation != IsolationLevel.Serializable && _defaultIsolation != IsolationLevel.ReadCommitted)
  786. throw new NotSupportedException("Invalid Default IsolationLevel specified");
  787. SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat", "ISO8601"), true);
  788. //string temp = FindKey(opts, "DateTimeFormat", "ISO8601");
  789. //if (String.Compare(temp, "ticks", true, CultureInfo.InvariantCulture) == 0) dateFormat = SQLiteDateFormats.Ticks;
  790. //else if (String.Compare(temp, "julianday", true, CultureInfo.InvariantCulture) == 0) dateFormat = SQLiteDateFormats.JulianDay;
  791. if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16()
  792. _sql = new SQLite3_UTF16(dateFormat);
  793. else
  794. _sql = new SQLite3(dateFormat);
  795. SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None;
  796. if (SqliteConvert.ToBoolean(FindKey(opts, "Read Only", Boolean.FalseString)) == true)
  797. flags |= SQLiteOpenFlagsEnum.ReadOnly;
  798. else {
  799. flags |= SQLiteOpenFlagsEnum.ReadWrite;
  800. if (SqliteConvert.ToBoolean(FindKey(opts, "FailIfMissing", Boolean.FalseString)) == false)
  801. flags |= SQLiteOpenFlagsEnum.Create;
  802. }
  803. if (SqliteConvert.ToBoolean (FindKey (opts, "FileProtectionComplete", Boolean.FalseString)))
  804. flags |= SQLiteOpenFlagsEnum.FileProtectionComplete;
  805. if (SqliteConvert.ToBoolean (FindKey (opts, "FileProtectionCompleteUnlessOpen", Boolean.FalseString)))
  806. flags |= SQLiteOpenFlagsEnum.FileProtectionCompleteUnlessOpen;
  807. if (SqliteConvert.ToBoolean (FindKey (opts, "FileProtectionCompleteUntilFirstUserAuthentication", Boolean.FalseString)))
  808. flags |= SQLiteOpenFlagsEnum.FileProtectionCompleteUntilFirstUserAuthentication;
  809. if (SqliteConvert.ToBoolean (FindKey (opts, "FileProtectionNone", Boolean.FalseString)))
  810. flags |= SQLiteOpenFlagsEnum.FileProtectionNone;
  811. _sql.Open(fileName, flags, maxPoolSize, usePooling);
  812. _binaryGuid = (SqliteConvert.ToBoolean(FindKey(opts, "BinaryGUID", Boolean.TrueString)) == true);
  813. string password = FindKey(opts, "Password", null);
  814. if (String.IsNullOrEmpty(password) == false)
  815. _sql.SetPassword(System.Text.UTF8Encoding.UTF8.GetBytes(password));
  816. else if (_password != null)
  817. _sql.SetPassword(_password);
  818. _password = null;
  819. _dataSource = Path.GetFileNameWithoutExtension(fileName);
  820. OnStateChange(ConnectionState.Open);
  821. _version++;
  822. using (SqliteCommand cmd = CreateCommand())
  823. {
  824. string defValue;
  825. if (fileName != ":memory:")
  826. {
  827. defValue = FindKey(opts, "Page Size", "1024");
  828. if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 1024)
  829. {
  830. cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA page_size={0}", defValue);
  831. cmd.ExecuteNonQuery();
  832. }
  833. }
  834. defValue = FindKey(opts, "Max Page Count", "0");
  835. if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 0)
  836. {
  837. cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA max_page_count={0}", defValue);
  838. cmd.ExecuteNonQuery();
  839. }
  840. defValue = FindKey(opts, "Legacy Format", Boolean.FalseString);
  841. cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA legacy_file_format={0}", SqliteConvert.ToBoolean(defValue) == true ? "ON" : "OFF");
  842. cmd.ExecuteNonQuery();
  843. defValue = FindKey(opts, "Synchronous", "Normal");
  844. if (String.Compare(defValue, "Full", StringComparison.OrdinalIgnoreCase) != 0)
  845. {
  846. cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA synchronous={0}", defValue);
  847. cmd.ExecuteNonQuery();
  848. }
  849. defValue = FindKey(opts, "Cache Size", "2000");
  850. if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 2000)
  851. {
  852. cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA cache_size={0}", defValue);
  853. cmd.ExecuteNonQuery();
  854. }
  855. defValue = FindKey(opts, "Journal Mode", "Delete");
  856. if (String.Compare(defValue, "Default", StringComparison.OrdinalIgnoreCase) != 0)
  857. {
  858. cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA journal_mode={0}", defValue);
  859. cmd.ExecuteNonQuery();
  860. }
  861. }
  862. if (_commitHandler != null)
  863. _sql.SetCommitHook(_commitCallback);
  864. if (_updateHandler != null)
  865. _sql.SetUpdateHook(_updateCallback);
  866. if (_rollbackHandler != null)
  867. _sql.SetRollbackHook(_rollbackCallback);
  868. #if !PLATFORM_COMPACTFRAMEWORK
  869. if (global::System.Transactions.Transaction.Current != null && SqliteConvert.ToBoolean(FindKey(opts, "Enlist", Boolean.TrueString)) == true)
  870. EnlistTransaction(global::System.Transactions.Transaction.Current);
  871. #endif
  872. }
  873. catch (SqliteException)
  874. {
  875. Close();
  876. throw;
  877. }
  878. }
  879. /// <summary>
  880. /// Gets/sets the default command timeout for newly-created commands. This is especially useful for
  881. /// commands used internally such as inside a SqliteTransaction, where setting the timeout is not possible.
  882. /// This can also be set in the ConnectionString with "Default Timeout"
  883. /// </summary>
  884. public int DefaultTimeout
  885. {
  886. get { return _defaultTimeout; }
  887. set { _defaultTimeout = value; }
  888. }
  889. /// <summary>
  890. /// Returns the version of the underlying SQLite database engine
  891. /// </summary>
  892. #if !PLATFORM_COMPACTFRAMEWORK
  893. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  894. #endif
  895. public override string ServerVersion
  896. {
  897. get
  898. {
  899. if (_connectionState != ConnectionState.Open)
  900. throw new InvalidOperationException();
  901. return _sql.Version;
  902. }
  903. }
  904. /// <summary>
  905. /// Returns the version of the underlying SQLite database engine
  906. /// </summary>
  907. public static string SQLiteVersion
  908. {
  909. get { return SQLite3.SQLiteVersion; }
  910. }
  911. /// <summary>
  912. /// Returns the state of the connection.
  913. /// </summary>
  914. #if !PLATFORM_COMPACTFRAMEWORK
  915. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  916. #endif
  917. public override ConnectionState State
  918. {
  919. get
  920. {
  921. return _connectionState;
  922. }
  923. }
  924. /// <summary>
  925. /// Change the password (or assign a password) to an open database.
  926. /// </summary>
  927. /// <remarks>
  928. /// No readers or writers may be active for this process. The database must already be open
  929. /// and if it already was password protected, the existing password must already have been supplied.
  930. /// </remarks>
  931. /// <param name="newPassword">The new password to assign to the database</param>
  932. public void ChangePassword(string newPassword)
  933. {
  934. ChangePassword(String.IsNullOrEmpty(newPassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(newPassword));
  935. }
  936. /// <summary>
  937. /// Change the password (or assign a password) to an open database.
  938. /// </summary>
  939. /// <remarks>
  940. /// No readers or writers may be active for this process. The database must already be open
  941. /// and if it already was password protected, the existing password must already have been supplied.
  942. /// </remarks>
  943. /// <param name="newPassword">The new password to assign to the database</param>
  944. public void ChangePassword(byte[] newPassword)
  945. {
  946. if (_connectionState != ConnectionState.Open)
  947. throw new InvalidOperationException("Database must be opened before changing the password.");
  948. _sql.ChangePassword(newPassword);
  949. }
  950. /// <summary>
  951. /// Sets the password for a password-protected database. A password-protected database is
  952. /// unusable for any operation until the password has been set.
  953. /// </summary>
  954. /// <param name="databasePassword">The password for the database</param>
  955. public void SetPassword(string databasePassword)
  956. {
  957. SetPassword(String.IsNullOrEmpty(databasePassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(databasePassword));
  958. }
  959. /// <summary>
  960. /// Sets the password for a password-protected database. A password-protected database is
  961. /// unusable for any operation until the password has been set.
  962. /// </summary>
  963. /// <param name="databasePassword">The password for the database</param>
  964. public void SetPassword(byte[] databasePassword)
  965. {
  966. if (_connectionState != ConnectionState.Closed)
  967. throw new InvalidOperationException("Password can only be set before the database is opened.");
  968. if (databasePassword != null)
  969. if (databasePassword.Length == 0) databasePassword = null;
  970. _password = databasePassword;
  971. }
  972. /// <summary>
  973. /// Expand the filename of the data source, resolving the |DataDirectory| macro as appropriate.
  974. /// </summary>
  975. /// <param name="sourceFile">The database filename to expand</param>
  976. /// <returns>The expanded path and filename of the filename</returns>
  977. private string ExpandFileName(string sourceFile)
  978. {
  979. if (String.IsNullOrEmpty(sourceFile)) return sourceFile;
  980. if (sourceFile.StartsWith(_dataDirectory, StringComparison.OrdinalIgnoreCase))
  981. {
  982. string dataDirectory;
  983. #if PLATFORM_COMPACTFRAMEWORK
  984. dataDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase);
  985. #else
  986. dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory") as string;
  987. if (String.IsNullOrEmpty(dataDirectory))
  988. dataDirectory = AppDomain.CurrentDomain.BaseDirectory;
  989. #endif
  990. if (sourceFile.Length > _dataDirectory.Length)
  991. {
  992. if (sourceFile[_dataDirectory.Length] == Path.DirectorySeparatorChar ||
  993. sourceFile[_dataDirectory.Length] == Path.AltDirectorySeparatorChar)
  994. sourceFile = sourceFile.Remove(_dataDirectory.Length, 1);
  995. }
  996. sourceFile = Path.Combine(dataDirectory, sourceFile.Substring(_dataDirectory.Length));
  997. }
  998. #if !PLATFORM_COMPACTFRAMEWORK
  999. sourceFile = Path.GetFullPath(sourceFile);
  1000. #endif
  1001. return sourceFile;
  1002. }
  1003. ///<overloads>
  1004. /// The following commands are used to extract schema information out of the database. Valid schema types are:
  1005. /// <list type="bullet">
  1006. /// <item>
  1007. /// <description>MetaDataCollections</description>
  1008. /// </item>
  1009. /// <item>
  1010. /// <description>DataSourceInformation</description>
  1011. /// </item>
  1012. /// <item>
  1013. /// <description>Catalogs</description>
  1014. /// </item>
  1015. /// <item>
  1016. /// <description>Columns</description>
  1017. /// </item>
  1018. /// <item>
  1019. /// <description>ForeignKeys</description>
  1020. /// </item>
  1021. /// <item>
  1022. /// <description>Indexes</description>
  1023. /// </item>
  1024. /// <item>
  1025. /// <description>IndexColumns</description>
  1026. /// </item>
  1027. /// <item>
  1028. /// <description>Tables</description>
  1029. /// </item>
  1030. /// <item>
  1031. /// <description>Views</description>
  1032. /// </item>
  1033. /// <item>
  1034. /// <description>ViewColumns</description>
  1035. /// </item>
  1036. /// </list>
  1037. /// </overloads>
  1038. /// <summary>
  1039. /// Returns the MetaDataCollections schema
  1040. /// </summary>
  1041. /// <returns>A DataTable of the MetaDataCollections schema</returns>
  1042. public override DataTable GetSchema()
  1043. {
  1044. return GetSchema("MetaDataCollections", null);
  1045. }
  1046. /// <summary>
  1047. /// Returns schema information of the specified collection
  1048. /// </summary>
  1049. /// <param name="collectionName">The schema collection to retrieve</param>
  1050. /// <returns>A DataTable of the specified collection</returns>
  1051. public override DataTable GetSchema(string collectionName)
  1052. {
  1053. return GetSchema(collectionName, new string[0]);
  1054. }
  1055. /// <summary>
  1056. /// Retrieves schema information using the specified constraint(s) for the specified collection
  1057. /// </summary>
  1058. /// <param name="collectionName">The collection to retrieve</param>
  1059. /// <param name="restrictionValues">The restrictions to impose</param>
  1060. /// <returns>A DataTable of the specified collection</returns>
  1061. public override DataTable GetSchema(string collectionName, string[] restrictionValues)
  1062. {
  1063. if (_connectionState != ConnectionState.Open)
  1064. throw new InvalidOperationException();
  1065. string[] parms = new string[5];
  1066. if (restrictionValues == null) restrictionValues = new string[0];
  1067. restrictionValues.CopyTo(parms, 0);
  1068. switch (collectionName.ToUpper(CultureInfo.InvariantCulture))
  1069. {
  1070. case "METADATACOLLECTIONS":
  1071. return Schema_MetaDataCollections();
  1072. case "DATASOURCEINFORMATION":
  1073. return Schema_DataSourceInformation();
  1074. case "DATATYPES":
  1075. return Schema_DataTypes();
  1076. case "COLUMNS":
  1077. case "TABLECOLUMNS":
  1078. return Schema_Columns(parms[0], parms[2], parms[3]);
  1079. case "INDEXES":
  1080. return Schema_Indexes(parms[0], parms[2], parms[3]);
  1081. case "TRIGGERS":
  1082. return Schema_Triggers(parms[0], parms[2], parms[3]);
  1083. case "INDEXCOLUMNS":
  1084. return Schema_IndexColumns(parms[0], parms[2], parms[3], parms[4]);
  1085. case "TABLES":
  1086. return Schema_Tables(parms[0], parms[2], parms[3]);
  1087. case "VIEWS":
  1088. return Schema_Views(parms[0], parms[2]);
  1089. case "VIEWCOLUMNS":
  1090. return Schema_ViewColumns(parms[0], parms[2], parms[3]);
  1091. case "FOREIGNKEYS":
  1092. return Schema_ForeignKeys(parms[0], parms[2], parms[3]);
  1093. case "CATALOGS":
  1094. return Schema_Catalogs(parms[0]);
  1095. case "RESERVEDWORDS":
  1096. return Schema_ReservedWords();
  1097. }
  1098. throw new NotSupportedException();
  1099. }
  1100. private static DataTable Schema_ReservedWords()
  1101. {
  1102. DataTable tbl = new DataTable("MetaDataCollections");
  1103. tbl.Locale = CultureInfo.InvariantCulture;
  1104. tbl.Columns.Add("ReservedWord", typeof(string));
  1105. tbl.Columns.Add("MaximumVersion", typeof(string));
  1106. tbl.Columns.Add("MinimumVersion", typeof(string));
  1107. tbl.BeginLoadData();
  1108. DataRow row;
  1109. foreach (string word in SR.Keywords.Split(new char[] { ',' }))
  1110. {
  1111. row = tbl.NewRow();
  1112. row[0] = word;
  1113. tbl.Rows.Add(row);
  1114. }
  1115. tbl.AcceptChanges();
  1116. tbl.EndLoadData();
  1117. return tbl;
  1118. }
  1119. /// <summary>
  1120. /// Builds a MetaDataCollections schema datatable
  1121. /// </summary>
  1122. /// <returns>DataTable</returns>
  1123. private static DataTable Schema_MetaDataCollections()
  1124. {
  1125. DataTable tbl = new DataTable("MetaDataCollections");
  1126. tbl.Locale = CultureInfo.InvariantCulture;
  1127. tbl.Columns.Add("CollectionName", typeof(string));
  1128. tbl.Columns.Add("NumberOfRestrictions", typeof(int));
  1129. tbl.Columns.Add("NumberOfIdentifierParts", typeof(int));
  1130. tbl.BeginLoadData();
  1131. StringReader reader = new StringReader(SR.MetaDataCollections);
  1132. tbl.ReadXml(reader);
  1133. reader.Close();
  1134. tbl.AcceptChanges();
  1135. tbl.EndLoadData();
  1136. return tbl;
  1137. }
  1138. /// <summary>
  1139. /// Builds a DataSourceInformation datatable
  1140. /// </summary>
  1141. /// <returns>DataTable</returns>
  1142. private DataTable Schema_DataSourceInformation()
  1143. {
  1144. DataTable tbl = new DataTable("DataSourceInformation");
  1145. DataRow row;

Large files files are truncated, but you can click here to view the full file