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