1 /********************************************************
\r
2 * ADO.NET 2.0 Data Provider for SQLite Version 3.X
\r
3 * Written by Robert Simpson (robert@blackcastlesoft.com)
\r
5 * Released to the public domain, use at your own risk!
\r
6 ********************************************************/
\r
8 namespace Mono.Data.Sqlite
\r
12 using System.Runtime.InteropServices;
\r
13 using System.Collections.Generic;
\r
16 /// This internal class provides the foundation of SQLite support. It defines all the abstract members needed to implement
\r
17 /// a SQLite data provider, and inherits from SqliteConvert which allows for simple translations of string to and from SQLite.
\r
19 internal abstract class SQLiteBase : SqliteConvert, IDisposable
\r
21 internal SQLiteBase(SQLiteDateFormats fmt)
\r
24 static internal object _lock = new object();
\r
27 /// Returns a string representing the active version of SQLite
\r
29 internal abstract string Version { get; }
\r
31 /// Returns the number of changes the last executing insert/update caused.
\r
33 internal abstract int Changes { get; }
\r
35 /// Opens a database.
\r
38 /// Implementers should call SqliteFunction.BindFunctions() and save the array after opening a connection
\r
39 /// to bind all attributed user-defined functions and collating sequences to the new connection.
\r
41 /// <param name="strFilename">The filename of the database to open. SQLite automatically creates it if it doesn't exist.</param>
\r
42 /// <param name="flags">The open flags to use when creating the connection</param>
\r
43 /// <param name="maxPoolSize">The maximum size of the pool for the given filename</param>
\r
44 /// <param name="usePool">If true, the connection can be pulled from the connection pool</param>
\r
45 internal abstract void Open(string strFilename, SQLiteOpenFlagsEnum flags, int maxPoolSize, bool usePool);
\r
47 /// Closes the currently-open database.
\r
50 /// After the database has been closed implemeters should call SqliteFunction.UnbindFunctions() to deallocate all interop allocated
\r
51 /// memory associated with the user-defined functions and collating sequences tied to the closed connection.
\r
53 internal abstract void Close();
\r
55 /// Sets the busy timeout on the connection. SqliteCommand will call this before executing any command.
\r
57 /// <param name="nTimeoutMS">The number of milliseconds to wait before returning SQLITE_BUSY</param>
\r
58 internal abstract void SetTimeout(int nTimeoutMS);
\r
60 /// Returns the text of the last error issued by SQLite
\r
62 /// <returns></returns>
\r
63 internal abstract string SQLiteLastError();
\r
66 /// When pooling is enabled, force this connection to be disposed rather than returned to the pool
\r
68 internal abstract void ClearPool();
\r
71 /// Prepares a SQL statement for execution.
\r
73 /// <param name="cnn">The source connection preparing the command. Can be null for any caller except LINQ</param>
\r
74 /// <param name="strSql">The SQL command text to prepare</param>
\r
75 /// <param name="previous">The previous statement in a multi-statement command, or null if no previous statement exists</param>
\r
76 /// <param name="timeoutMS">The timeout to wait before aborting the prepare</param>
\r
77 /// <param name="strRemain">The remainder of the statement that was not processed. Each call to prepare parses the
\r
78 /// SQL up to to either the end of the text or to the first semi-colon delimiter. The remaining text is returned
\r
79 /// here for a subsequent call to Prepare() until all the text has been processed.</param>
\r
80 /// <returns>Returns an initialized SqliteStatement.</returns>
\r
81 internal abstract SqliteStatement Prepare(SqliteConnection cnn, string strSql, SqliteStatement previous, uint timeoutMS, out string strRemain);
\r
83 /// Steps through a prepared statement.
\r
85 /// <param name="stmt">The SqliteStatement to step through</param>
\r
86 /// <returns>True if a row was returned, False if not.</returns>
\r
87 internal abstract bool Step(SqliteStatement stmt);
\r
89 /// Resets a prepared statement so it can be executed again. If the error returned is SQLITE_SCHEMA,
\r
90 /// transparently attempt to rebuild the SQL statement and throw an error if that was not possible.
\r
92 /// <param name="stmt">The statement to reset</param>
\r
93 /// <returns>Returns -1 if the schema changed while resetting, 0 if the reset was sucessful or 6 (SQLITE_LOCKED) if the reset failed due to a lock</returns>
\r
94 internal abstract int Reset(SqliteStatement stmt);
\r
95 internal abstract void Cancel();
\r
97 internal abstract void Bind_Double(SqliteStatement stmt, int index, double value);
\r
98 internal abstract void Bind_Int32(SqliteStatement stmt, int index, Int32 value);
\r
99 internal abstract void Bind_Int64(SqliteStatement stmt, int index, Int64 value);
\r
100 internal abstract void Bind_Text(SqliteStatement stmt, int index, string value);
\r
101 internal abstract void Bind_Blob(SqliteStatement stmt, int index, byte[] blobData);
\r
102 internal abstract void Bind_DateTime(SqliteStatement stmt, int index, DateTime dt);
\r
103 internal abstract void Bind_Null(SqliteStatement stmt, int index);
\r
105 internal abstract int Bind_ParamCount(SqliteStatement stmt);
\r
106 internal abstract string Bind_ParamName(SqliteStatement stmt, int index);
\r
107 internal abstract int Bind_ParamIndex(SqliteStatement stmt, string paramName);
\r
109 internal abstract int ColumnCount(SqliteStatement stmt);
\r
110 internal abstract string ColumnName(SqliteStatement stmt, int index);
\r
111 internal abstract TypeAffinity ColumnAffinity(SqliteStatement stmt, int index);
\r
112 internal abstract string ColumnType(SqliteStatement stmt, int index, out TypeAffinity nAffinity);
\r
113 internal abstract int ColumnIndex(SqliteStatement stmt, string columnName);
\r
114 internal abstract string ColumnOriginalName(SqliteStatement stmt, int index);
\r
115 internal abstract string ColumnDatabaseName(SqliteStatement stmt, int index);
\r
116 internal abstract string ColumnTableName(SqliteStatement stmt, int index);
\r
117 internal abstract void ColumnMetaData(string dataBase, string table, string column, out string dataType, out string collateSequence, out bool notNull, out bool primaryKey, out bool autoIncrement);
\r
118 internal abstract void GetIndexColumnExtendedInfo(string database, string index, string column, out int sortMode, out int onError, out string collationSequence);
\r
120 internal abstract double GetDouble(SqliteStatement stmt, int index);
\r
121 internal abstract Int32 GetInt32(SqliteStatement stmt, int index);
\r
122 internal abstract Int64 GetInt64(SqliteStatement stmt, int index);
\r
123 internal abstract string GetText(SqliteStatement stmt, int index);
\r
124 internal abstract long GetBytes(SqliteStatement stmt, int index, int nDataoffset, byte[] bDest, int nStart, int nLength);
\r
125 internal abstract long GetChars(SqliteStatement stmt, int index, int nDataoffset, char[] bDest, int nStart, int nLength);
\r
126 internal abstract DateTime GetDateTime(SqliteStatement stmt, int index);
\r
127 internal abstract bool IsNull(SqliteStatement stmt, int index);
\r
129 internal abstract void CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16, IntPtr user_data);
\r
130 internal abstract void CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal);
\r
131 internal abstract CollationSequence GetCollationSequence(SqliteFunction func, IntPtr context);
\r
132 internal abstract int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, string s1, string s2);
\r
133 internal abstract int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, char[] c1, char[] c2);
\r
135 internal abstract int AggregateCount(IntPtr context);
\r
136 internal abstract IntPtr AggregateContext(IntPtr context);
\r
138 internal abstract long GetParamValueBytes(IntPtr ptr, int nDataOffset, byte[] bDest, int nStart, int nLength);
\r
139 internal abstract double GetParamValueDouble(IntPtr ptr);
\r
140 internal abstract int GetParamValueInt32(IntPtr ptr);
\r
141 internal abstract Int64 GetParamValueInt64(IntPtr ptr);
\r
142 internal abstract string GetParamValueText(IntPtr ptr);
\r
143 internal abstract TypeAffinity GetParamValueType(IntPtr ptr);
\r
145 internal abstract void ReturnBlob(IntPtr context, byte[] value);
\r
146 internal abstract void ReturnDouble(IntPtr context, double value);
\r
147 internal abstract void ReturnError(IntPtr context, string value);
\r
148 internal abstract void ReturnInt32(IntPtr context, Int32 value);
\r
149 internal abstract void ReturnInt64(IntPtr context, Int64 value);
\r
150 internal abstract void ReturnNull(IntPtr context);
\r
151 internal abstract void ReturnText(IntPtr context, string value);
\r
153 internal abstract void SetPassword(byte[] passwordBytes);
\r
154 internal abstract void ChangePassword(byte[] newPasswordBytes);
\r
156 internal abstract void SetUpdateHook(SQLiteUpdateCallback func);
\r
157 internal abstract void SetCommitHook(SQLiteCommitCallback func);
\r
158 internal abstract void SetRollbackHook(SQLiteRollbackCallback func);
\r
160 internal abstract int GetCursorForTable(SqliteStatement stmt, int database, int rootPage);
\r
161 internal abstract long GetRowIdForCursor(SqliteStatement stmt, int cursor);
\r
163 internal abstract object GetValue(SqliteStatement stmt, int index, SQLiteType typ);
\r
165 protected virtual void Dispose(bool bDisposing)
\r
169 public void Dispose()
\r
174 // These statics are here for lack of a better place to put them.
\r
175 // They exist here because they are called during the finalization of
\r
176 // a SqliteStatementHandle, SqliteConnectionHandle, and SqliteFunctionCookieHandle.
\r
177 // Therefore these functions have to be static, and have to be low-level.
\r
179 internal static string SQLiteLastError(SqliteConnectionHandle db)
\r
181 #if !SQLITE_STANDARD
\r
183 return UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg_interop(db, out len), len);
\r
185 return UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg(db), -1);
\r
189 internal static void FinalizeStatement(SqliteStatementHandle stmt)
\r
193 #if !SQLITE_STANDARD
\r
194 int n = UnsafeNativeMethods.sqlite3_finalize_interop(stmt);
\r
196 int n = UnsafeNativeMethods.sqlite3_finalize(stmt);
\r
198 if (n > 0) throw new SqliteException(n, null);
\r
202 internal static void CloseConnection(SqliteConnectionHandle db)
\r
206 #if !SQLITE_STANDARD
\r
207 int n = UnsafeNativeMethods.sqlite3_close_interop(db);
\r
209 ResetConnection(db);
\r
210 int n = UnsafeNativeMethods.sqlite3_close(db);
\r
212 if (n > 0) throw new SqliteException(n, SQLiteLastError(db));
\r
216 internal static void ResetConnection(SqliteConnectionHandle db)
\r
220 IntPtr stmt = IntPtr.Zero;
\r
224 stmt = UnsafeNativeMethods.sqlite3_next_stmt(db, stmt);
\r
225 if (stmt != IntPtr.Zero)
\r
227 #if !SQLITE_STANDARD
\r
228 UnsafeNativeMethods.sqlite3_reset_interop(stmt);
\r
230 UnsafeNativeMethods.sqlite3_reset(stmt);
\r
233 } while (stmt != IntPtr.Zero);
\r
235 // Not overly concerned with the return value from a rollback.
\r
236 UnsafeNativeMethods.sqlite3_exec(db, ToUTF8("ROLLBACK"), IntPtr.Zero, IntPtr.Zero, out stmt);
\r
237 // but free the error message if any!
\r
238 if (stmt != IntPtr.Zero)
\r
239 UnsafeNativeMethods.sqlite3_free (stmt);
\r
244 internal interface ISQLiteSchemaExtensions
\r
246 void BuildTempSchema(SqliteConnection cnn);
\r
250 internal enum SQLiteOpenFlagsEnum
\r
256 //SharedCache = 0x01000000,
\r
260 FileProtectionComplete = 0x00100000,
\r
261 FileProtectionCompleteUnlessOpen = 0x00200000,
\r
262 FileProtectionCompleteUntilFirstUserAuthentication = 0x00300000,
\r
263 FileProtectionNone = 0x00400000
\r
266 // subset of the options available in http://www.sqlite.org/c3ref/c_config_getmalloc.html
\r
267 public enum SQLiteConfig {
\r