prettify

baseline

sqlite c# wrapper

Here are the classes to include in your C# project, so that you can use sqlite. The classes should work without problem with sqlite version 3.7.4 2010-12-08.
If you find any problem you can mail me at moc.liamg@oirakp (do not copy paste, it is written rtl and set ltr by CSS) or you can leave a message. I you need a feature, that I did not implement, you can tell me, but no promises here.
The usage of the classes is covered in the next post: Sqlite c# usage. Include the following classes in your project. Put all classes in one file (Sqlite.cs).
(Don't forget "using System.Runtime.InteropServices;" Class sqlite (Database):
public class SQLiteException: Exception {public SQLiteException(string message) : base(message) {}}
public class sqlite {
   
public enum Access: int {
        EXISTS
= 0,
        READWRITE
= 1,
        READ
= 2
   
}
   
public enum Result: int {
        OK
= 0,
        ERROR
= 1,
        INTERNAL
= 2,
        PERM
= 3,
        ABORT
= 4,
        BUSY
= 5,
        LOCKED
= 6,
        NOMEM
= 7,
        READONLY
= 8,
        INTERRUPT
= 9,
        IOERR
= 10,
        CORRUPT
= 11,
        NOTFOUND
= 12,
        FULL
= 13,
        CANTOPEN
= 14,
        PROTOCOL
= 15,
        EMPTY
= 16,
        SCHEMA
= 17,
        TOOBIG
= 18,
        CONSTRAINT
= 19,
        MISMATCH
= 20,
        MISUSE
= 21,
        NOLFS
= 22,
        AUTH
= 23,
        FORMAT
= 24,
        RANGE
= 25,
        NOTADB
= 26,
        ROW
= 100,
        DONE
= 101
   
}
   
public enum DataType: int {
        INTEGER
= 1,
        FLOAT
= 2,
        TEXT
= 3,
        BLOB
= 4,
        NULL
= 5
   
}
   
public enum Parameter: int {
        TRANSIENT
= -1,
        STATIC
= 0
   
}
   
[DllImport("kernel32.dll", EntryPoint = "HeapAlloc")]
   
private extern static IntPtr HeapAlloc(IntPtr heap, UInt32 flags, UInt32 bytes);
   
[DllImport("kernel32.dll", EntryPoint = "GetProcessHeap")]
   
private extern static IntPtr GetProcessHeap();
   
[DllImport("kernel32.dll", EntryPoint = "lstrlen")]
   
public extern static int lstrlen(IntPtr hStr);

   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_open", CallingConvention = CallingConvention.Cdecl)]
   
public static extern sqlite.Result open(string filename, out IntPtr hDb);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_close", CallingConvention = CallingConvention.Cdecl)]
   
public static extern int close(IntPtr hDb);

   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_exec", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern Result exec(IntPtr hDb, string sql, IntPtr callback, IntPtr args, out IntPtr errorMessage);

   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_prepare_v2", CallingConvention = CallingConvention.Cdecl)]
   
//public static extern sqlite.Result prepare_v2(IntPtr hDb, string zSql, int nByte, out IntPtr ppStmpt, IntPtr pzTail);
   
public static extern sqlite.Result prepare_v2(IntPtr hDb, IntPtr zSql, int nByte, out IntPtr ppStmpt, IntPtr pzTail);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_step", CallingConvention = CallingConvention.Cdecl)]
   
public static extern sqlite.Result step(IntPtr hStm);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_finalize", CallingConvention = CallingConvention.Cdecl)]
   
public static extern sqlite.Result finalize(IntPtr hStm);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_reset", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result reset(IntPtr hStm);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_clear_bindings", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result clear_bindings(IntPtr hStm);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_errmsg", CallingConvention = CallingConvention.Cdecl)]
   
public static extern string errmsg(IntPtr hDb);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_last_insert_rowid", CallingConvention = CallingConvention.Cdecl)]
   
public static extern long last_insert_rowid(IntPtr hDb);

   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_count", CallingConvention = CallingConvention.Cdecl)]
   
public static extern int column_count(IntPtr hStm);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_name", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern IntPtr column_name(IntPtr hStm, int nCol);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_origin_name", CallingConvention = CallingConvention.Cdecl)]
   
public static extern string column_origin_name(IntPtr hStm, int nCol);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_type", CallingConvention = CallingConvention.Cdecl)]
   
public static extern sqlite.DataType column_type(IntPtr hStm, int nCol);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_int", CallingConvention = CallingConvention.Cdecl)]
   
public static extern int column_int(IntPtr hStm, int nCol);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_text", CallingConvention = CallingConvention.Cdecl)]
   
public static extern IntPtr column_text(IntPtr hStm, int nCol);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_double", CallingConvention = CallingConvention.Cdecl)]
   
public static extern double column_double(IntPtr hStm, int nCol);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_blob", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern IntPtr column_blob(IntPtr hStm, int nCol);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_bytes", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern double column_bytes(IntPtr hStm, int nCol);

   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_parameter_count", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern int bind_parameter_count(IntPtr hStm);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_parameter_name", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern IntPtr bind_parameter_name(IntPtr hStm, int n); // UTF-8 encoded return
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_blob", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result bind_blob(IntPtr hStm, int n, byte[] blob, int length, IntPtr freetype);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_double", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result bind_double(IntPtr hStm, int n, double value);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_int", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result bind_int(IntPtr hStm, int n, int value);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_int64", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result bind_int64(IntPtr hStm, int n, long value);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_null", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result bind_null(IntPtr hStm, int n);
   
[DllImport("sqlite3.dll", EntryPoint = "sqlite3_bind_text", CallingConvention = CallingConvention.Cdecl)] //
   
public static extern sqlite.Result bind_text(IntPtr hStm, int n, byte[] value, int length, IntPtr freetype);
   
#region statics
   
public static byte[] ToUtf8(string sText) {
       
byte[] byteArray;
       
int nLen = Encoding.UTF8.GetByteCount(sText) + 1;
        byteArray
= new byte[nLen];
        nLen
= Encoding.UTF8.GetBytes(sText, 0, sText.Length, byteArray, 0);
        byteArray
[nLen] = 0;
       
return byteArray;
   
}
   
public static string Utf8ToString(IntPtr hStr) {
       
if (hStr == IntPtr.Zero) return "";
       
int nLen = lstrlen(hStr);
       
byte[] byteArray = new byte[nLen];
       
Marshal.Copy(hStr, byteArray, 0, nLen);
       
return Encoding.UTF8.GetString(byteArray, 0, nLen);
   
}
   
public static IntPtr Str2h(string str) {
       
if (str == null) { return IntPtr.Zero; }
       
byte[] buffer = Encoding.UTF8.GetBytes(str);
       
int length = buffer.Length + 1;
       
IntPtr hStr = HeapAlloc(GetProcessHeap(), 0, (UInt32)length);
       
Marshal.Copy(buffer, 0, hStr, buffer.Length);
       
Marshal.WriteByte(hStr, buffer.Length, 0);
       
return hStr;
   
}
   
public static String h2Str(IntPtr hStr) {
       
if (hStr == IntPtr.Zero) { return null; }
       
int nLen = GetPointerLength(hStr);
       
byte[] buffer = new byte[nLen];
       
Marshal.Copy(hStr, buffer, 0, nLen);
       
return Encoding.UTF8.GetString(buffer, 0, nLen);
   
}
   
private static int GetPointerLength(IntPtr hStr) {
       
if (hStr == IntPtr.Zero) { return 0; }
       
return lstrlen(hStr);
   
}
   
#endregion statics
   
public string sPath;
   
public IntPtr hDb = IntPtr.Zero;
   
public sqlite(string sPath) {
       
this.sPath = sPath;
       
if (sqlite.open(sPath, out hDb) != sqlite.Result.OK) throw new SQLiteException("Could not open database file: " + sPath);
   
}
   
~sqlite() {
       
if (hDb != IntPtr.Zero) {
            sqlite
.close(hDb);
            hDb
= IntPtr.Zero;
       
}
   
}
   
public sqliteStm Prepare(string SQL) {
       
if (hDb == IntPtr.Zero) throw new SQLiteException("Database closed: " + sPath);
       
return new sqliteStm(this, SQL);
   
}
   
public void OpenDatabase() {
       
if (sqlite.open(sPath, out hDb) != sqlite.Result.OK) throw new SQLiteException("Could not open database file: " + sPath);
   
}
   
public void CloseDatabase() {
       
if (hDb != IntPtr.Zero) {
            sqlite
.close(hDb);
            hDb
= IntPtr.Zero;
       
}
   
}
   
public long LastInsertRowid() {
       
return sqlite.last_insert_rowid(hDb);
   
}
   
public void Exec(string sSQL) {
       
IntPtr hError;
       
Result nResult;
       
if (hDb == IntPtr.Zero) throw new SQLiteException("SQLite database is not open.");
        nResult
= exec(hDb, sSQL, IntPtr.Zero, IntPtr.Zero, out hError);
       
if (nResult != Result.OK) throw new SQLiteException("Could not execute SQL statement.");
   
}
}
Class sqliteField (Query result column):
These field are used to read an sqlite table.
public class sqliteField {
   
public string sName;
   
public int nColumn;
   
public sqlite.DataType eType;
   
public long nValue;
   
public string sValue;
   
public double fValue;
   
public bool bNull;
   
private IntPtr hStm = IntPtr.Zero;
   
public sqliteField(IntPtr hStm) {
       
this.hStm = hStm;
   
}
   
public void Read() {
        bNull
= false;
        nValue
= 0;
        sValue
= "";
        fValue
= 0d;
        eType
= sqlite.column_type(hStm, nColumn);
       
switch (eType) {
       
case sqlite.DataType.NULL:
            bNull
= true;
           
break;
       
case sqlite.DataType.INTEGER:
            nValue
= sqlite.column_int(hStm, nColumn);
           
break;
       
case sqlite.DataType.TEXT:
            sValue
= sqlite.Utf8ToString(sqlite.column_text(hStm, nColumn));
           
break;
       
case sqlite.DataType.FLOAT:
            fValue
= sqlite.column_double(hStm, nColumn);
           
break;
       
case sqlite.DataType.BLOB:
            sValue
= sqlite.Utf8ToString(sqlite.column_blob(hStm, nColumn));
           
break;
       
default:
           
throw new SQLiteException("Unknown datatype");
       
}
   
}
   
public bool IsNull() {
       
return bNull;
   
}
   
public bool BOOL {
        get
{
           
if (eType != sqlite.DataType.INTEGER) throw new SQLiteException("Datatype: " + eType.ToString());
           
return (nValue != 0);
       
}
   
}
   
public string TEXT {
        get
{
           
if (eType != sqlite.DataType.TEXT) throw new SQLiteException("Datatype: " + eType.ToString());
           
return sValue;
       
}
   
}
   
public long INTEGER {
        get
{
           
if (eType != sqlite.DataType.INTEGER) throw new SQLiteException("Datatype: " + eType.ToString());
           
return nValue;
       
}
   
}
   
public double DOUBLE {
        get
{
           
if (eType == sqlite.DataType.FLOAT) {
               
return fValue;
           
} else if (eType == sqlite.DataType.INTEGER) {
               
return (double)nValue;
           
} else {
               
throw new SQLiteException("Datatype: " + eType.ToString());
           
}
       
}
   
}
   
public DateTime DATE {
        get
{
           
if (eType != sqlite.DataType.TEXT) throw new SQLiteException("Datatype: " + eType.ToString());
           
DateTime dValue = DateTime.MinValue;
           
DateTime.TryParseExact(sValue, "yyyyMMdd", (IFormatProvider)null, System.Globalization.DateTimeStyles.None, out dValue);
           
return dValue;
       
}
   
}
   
public DateTime DATETIME {
        get
{
           
if (eType != sqlite.DataType.TEXT) throw new SQLiteException("Datatype: " + eType.ToString());
           
DateTime dValue = DateTime.MinValue;
           
DateTime.TryParseExact(sValue, "yyyyMMddTHHmmss", (IFormatProvider)null, System.Globalization.DateTimeStyles.None, out dValue);
           
return dValue;
       
}
   
}
   
public decimal DECIMAL(int nDec) {
       
if (eType != sqlite.DataType.INTEGER) throw new SQLiteException("Datatype: " + eType.ToString());
       
decimal n = nValue;
       
return nValue;
   
}
}
Class sqliteStm (SQL statement that needs parameters and/or returns resultset).
public class sqliteStm {
   
internal sqliteStm(sqlite db, string sSQL) {
       
this.db = db;
       
this.nColumns = 0;
       
this.sSQL = sSQL;
       
this.hStm = IntPtr.Zero;
       
IntPtr hStr = sqlite.Str2h(sSQL);
        sqlite
.Result eResult = sqlite.prepare_v2(db.hDb, hStr, sqlite.lstrlen(hStr), out hStm, IntPtr.Zero);
       
if (eResult != sqlite.Result.OK) { throw new SQLiteException(sqlite.errmsg(db.hDb)); }
   
}
   
~sqliteStm() {
       
if (hStm != IntPtr.Zero) { FinalizeStm(); }
   
}
   
private readonly sqlite db;
   
private readonly string sSQL;
   
private IntPtr hStm;
   
private int nColumns;
   
public Dictionary Fields = new Dictionary();
   
public sqliteStm BindInt(int nParam, int nValue) {
       
if (sqlite.bind_int(hStm, nParam, nValue) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb) + "\nBind failed, Parameter:" + nParam.ToString() + ", Value:" + nValue.ToString());
       
return this;
   
}
   
public sqliteStm BindBool(int nParam, bool bValue) {
       
if (sqlite.bind_int(hStm, nParam, bValue ? 1 : 0) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb) + "\nBind failed, Parameter:" + nParam.ToString() + ", Value:" + (bValue ? "true" : "false"));
       
return this;
   
}
   
public sqliteStm BindNull(int nParam) {
       
if (sqlite.bind_null(hStm, nParam) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb) + "\nBind failed, Parameter:" + nParam.ToString());
       
return this;
   
}
   
public sqliteStm BindText(int nParam, string sValue) {
       
if (sqlite.bind_text(hStm, nParam, sqlite.ToUtf8(sValue), -1, new IntPtr(-1)) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb) + "\nBind failed, Parameter:" + nParam.ToString());
       
return this;
   
}
   
public sqliteStm BindDate(int nParam, DateTime dValue) {
       
if (sqlite.bind_text(hStm, nParam, sqlite.ToUtf8(dValue.ToString("yyyyMMdd")), -1, new IntPtr(-1)) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb) + "\nBind failed, Parameter:" + nParam.ToString());
       
return this;
   
}
   
public sqliteStm BindDateTime(int nParam, DateTime dValue) {
       
if (sqlite.bind_text(hStm, nParam, sqlite.ToUtf8(dValue.ToString("yyyyMMddTHHmmss")), -1, new IntPtr(-1)) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb) + "\nBind failed, Parameter:" + nParam.ToString());
       
return this;
   
}
   
public sqliteStm BindDec(int nParam, decimal nValue, int nDec) {
       
for (int n = nDec; n > 0; n--) { nValue *= 10; }
       
if (sqlite.bind_int(hStm, nParam, (int)Decimal.Round(nValue, MidpointRounding.AwayFromZero)) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb) + "\nBind failed, Parameter:" + nParam.ToString() + ", Value:" + nValue.ToString());
       
return this;
   
}
   
public sqliteStm Step() {
        sqlite
.Result eResult = sqlite.step(hStm);
       
if (eResult != sqlite.Result.DONE) throw new SQLiteException(sqlite.errmsg(db.hDb));
       
return this;
   
}
   
public bool Read() {
        bool bContinue
= false;
        sqliteField oField
;
        sqlite
.Result eResult = sqlite.step(hStm);
       
if (eResult == sqlite.Result.DONE) {
           
//return false;
       
} else if (eResult == sqlite.Result.ROW) {
           
if (nColumns == 0) {
                nColumns
= sqlite.column_count(hStm);
               
Fields.Clear();
               
for (int nColumn = 0; nColumn < nColumns; nColumn++) {
                    oField
= new sqliteField(hStm);
                    oField
.nColumn = nColumn;
                    oField
.sName = sqlite.Utf8ToString(sqlite.column_name(hStm, nColumn));
                   
Fields.Add(oField.sName, oField);
               
}
           
}
           
foreach (string sColumn in Fields.Keys) { Fields[sColumn].Read(); }
            bContinue
= true;
       
} else {
           
throw new SQLiteException(eResult.ToString() + "\n" + sqlite.errmsg(db.hDb));
       
}
       
return bContinue;
   
}
   
public sqliteStm Reset() {
       
if (sqlite.reset(hStm) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb));
       
return this;
   
}
   
public sqliteStm ClearBindings() {
       
if (sqlite.clear_bindings(hStm) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb));
       
return this;
   
}
   
public sqliteStm FinalizeStm() {
       
if (sqlite.finalize(hStm) != sqlite.Result.OK) throw new SQLiteException(sqlite.errmsg(db.hDb));
        hStm
= IntPtr.Zero;
        nColumns
= 0;
       
return this;
   
}

}

5 comments:

  1. Very nice and helpful post.
    For another open sorce dotnet provider for sqlite please see
    http://bimbim.in/post/2011/03/10/Using-SQLite-Database-With-Dotnet-C-Sharp.aspx

    ReplyDelete
  2. What is the Dictionary data type? Which namespace it belongs to? It does not compile.

    ReplyDelete
  3. what is the license for this code ?

    ReplyDelete
    Replies
    1. The code is "as it is". If you like it and you can guarantee that it works just copy it.
      I would like a note of you on the use, but nothing mandatory. If you want.

      Delete
  4. Hey, great implementation. However, I wonder whether you have the implementation for the callback function in sqlite3_exec. In my small project I only need sqlite3_open and sqlite3_exec, and I cant seem to get the marshalling for the callback function correctly. The callback function is called by sqlite3, and I received the number of rows correctly, but I could not get the data correctly. Do you have any advise?

    ReplyDelete