Click here to Skip to main content
Licence 
First Posted 16 Aug 2007
Views 71,220
Downloads 1,607
Bookmarked 51 times

Simple C# Wrapper for SQLite

By fbelic | 16 Aug 2007
An article that describes lightweight C# wrapper for SQLite
4 votes, 25.0%
1
1 vote, 6.3%
2

3
4 votes, 25.0%
4
7 votes, 43.8%
5
3.19/5 - 16 votes
μ 3.19, σa 3.00 [?]

Introduction

This article describes a very simple wrapper class for SQLite. This class provides only few simple functions: opening and closing database, returning the list of tables and executing queries. Although these are basic functions, they are enough for someone who needs only a storage engine for his/her program.

Background

While writing this class, I've been using ADO.NET Data Provider for SQLite as a reference, which I found here.

Using the Code

Using this class is very easy: add a reference to this DLL (Project->AddReference...) in your project and a line to your code:

using SQLWrapper;

Now you can start using database functions in your program.

Example:

// creates a new instance of SQLiteBase and opens database in file "test.db"
SQLiteBase db = new SQLiteBase("test.db");
// executes SELECT query and store results in new data table
DataTable table = db.ExecuteQuery("SELECT * FROM table1 WHERE id = 1;");
// closes the database
db.CloseDatabase();

Source Code

There are two constructors and and five public functions:

  • OpenDatabase
  • CloseDatabase
  • GetTables
  • ExecuteNonQuery
  • ExecuteQuery

which are all well-documented in the source file, so I don't see a point in explaining them again.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

fbelic

Software Developer (Junior)

Croatia Croatia

Member


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralEtrow Exception PinmemberAl Baikr22:30 16 Jul '10  
Rant[My vote of 2] Not reacting to malformed queries PinmemberRobhol13:44 5 Jul '10  
GeneralUpdate for Net 4.0 [modified] Pinmemberanss12322:04 6 Jun '10  
GeneralRe: Update for Net 4.0 Pinmemberupsfeup5:24 7 Jul '10  
GeneralRe: Update for Net 4.0 Pinmemberanss1234:59 27 Aug '10  
GeneralHaving Error: library routine called out of sequence Pinmemberupsfeup3:44 26 May '10  
GeneralRe: Having Error: library routine called out of sequence Pinmemberanss1234:02 4 Jun '10  
GeneralMy Sincerest THANKS!! Pinmemberkidzopa17:12 15 May '10  
GeneralA possible alternative; NFileStorage Pinmembersnip17:54 17 Mar '09  
Generalcreate a database PinmemberChaozzster7:28 16 Feb '09  
GeneralRe: create a database Pinmemberthk_sompi13:16 1 Mar '09  
GeneralRe: create a database PinmemberKen Eucker10:25 3 Jun '09  
GeneralRe: create a database Pinmemberfbelic0:14 4 Jun '09  
GeneralRe: create a database PinmemberKen Eucker22:19 4 Jun '09  
Generalout of memory Pinmembersaifsail9:32 24 Jan '09  
GeneralPossible memory leak PinmemberJacky__E4:34 24 Nov '08  
GeneralRe: Possible memory leak [modified] Pinmemberoren.shnitzer11:21 22 Dec '08  
(fixed a problem in the order of the free and execute query. sorry)
thanks for the article! seems like a really KISS solution that could help in simple cases (I am pretty stuck trying to add BLOBs using it Frown | :( , but it works nicely for simple tasks)

I went according to comment #1 and the above comment. seems like there are a few small leaks. I took the liberty of fixing both issues. here is my code (it's not throughly checked but it seems to work).
using System;
using System.Collections;
using System.Data;
using System.Runtime.InteropServices;
using System.Text;
 
namespace SQLiteWrapper
{
    /// <summary>
    /// SQLite wrapper with functions for opening, closing and executing queries.
    /// </summary>
    public class SQLiteBase
    {
        // imports system functions for work with pointers
        [DllImport("kernel32")]
        private extern static IntPtr HeapAlloc(IntPtr heap, UInt32 flags, UInt32 bytes);
 
		[DllImport("kernel32")]
		static extern bool HeapFree(IntPtr heap, UInt32 flags, IntPtr block);
 
        [DllImport("kernel32")]
        private extern static IntPtr GetProcessHeap();
 
        [DllImport("kernel32")]
        private extern static int lstrlen(IntPtr str);
 
        // imports SQLite functions
        [DllImport("sqlite3")]
        private static extern int sqlite3_open(IntPtr fileName, out IntPtr database);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_close(IntPtr database);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_exec(IntPtr database, IntPtr query, IntPtr callback, IntPtr arguments, out IntPtr error);
 
        [DllImport("sqlite3")]
        private static extern IntPtr sqlite3_errmsg(IntPtr database);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_prepare_v2(IntPtr database, IntPtr query, int length, out IntPtr statement, out IntPtr tail);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_step(IntPtr statement);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_column_count(IntPtr statement);
 
        [DllImport("sqlite3")]
        private static extern IntPtr sqlite3_column_name(IntPtr statement, int columnNumber);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_column_type(IntPtr statement, int columnNumber);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_column_int(IntPtr statement, int columnNumber);
 
        [DllImport("sqlite3")]
        private static extern double sqlite3_column_double(IntPtr statement, int columnNumber);
 
        [DllImport("sqlite3")]
        private static extern IntPtr sqlite3_column_text(IntPtr statement, int columnNumber);
 
        [DllImport("sqlite3")]
        private static extern IntPtr sqlite3_column_blob(IntPtr statement, int columnNumber);
 
        [DllImport("sqlite3")]
        private static extern IntPtr sqlite3_column_table_name(IntPtr statement, int columnNumber);
 
        [DllImport("sqlite3")]
        private static extern int sqlite3_finalize(IntPtr handle);
 
		[DllImport("sqlite3")]
		private static extern IntPtr sqlite3_free(IntPtr error);
 
        // SQLite constants 
        private const int SQL_OK = 0;
        private const int SQL_ROW = 100;
        private const int SQL_DONE = 101;
 
        /// <summary>
        /// SQLite data types.
        /// </summary>
        public enum SQLiteDataTypes { 
            /// <summary>
            /// Integer numbers.
            /// </summary>
            INT = 1, 
            /// <summary>
            /// Decimal numbers.
            /// </summary>
            FLOAT,
            /// <summary>
            /// All kinds of texts.
            /// </summary>
            TEXT, 
            /// <summary>
            /// Blob objects - binary large objects.
            /// </summary>
            BLOB, 
            /// <summary>
            /// Nothing.
            /// </summary>
            NULL };
 
        // pointer to database
        private IntPtr database;
 
        /// <summary>
        /// Creates new instance of SQLiteBase class with no database attached.
        /// </summary>
        public SQLiteBase()
        {
            database = IntPtr.Zero;
        }
 
        /// <summary>
        /// Creates new instance of SQLiteBase class and opens database with given name.
        /// </summary>
        /// <param name="baseName">Name (and path) to SQLite database file</param>
        public SQLiteBase(String baseName)
        {
            OpenDatabase(baseName);
        }
 
        /// <summary>
        /// Opens database. 
        /// </summary>
        /// <param name="baseName">Name of database file</param>
        public void OpenDatabase(String baseName)
        {
			IntPtr ptr = StringToPointer(baseName);
            // opens database 
            if (sqlite3_open(ptr, out database) != SQL_OK)
            {
                // if there is some error, database pointer is set to 0 and exception is throws
                database = IntPtr.Zero;
				HeapFree(GetProcessHeap(), 0, ptr);
                throw new Exception("Error with opening database " + baseName + "!");
            }
			HeapFree(GetProcessHeap(), 0, ptr);
		}
 
        /// <summary>
        /// Closes opened database.
        /// </summary>
        public void CloseDatabase()
        {
            // closes the database if there is one opened
            if (database != IntPtr.Zero)
            {
                sqlite3_close(database);
            }
        }
 
        /// <summary>
        /// Returns the list of tables in opened database.
        /// </summary>
        /// <returns></returns>
        public ArrayList GetTables()
        {
            // executes query that select names of all tables and views in master table of every database
            String query = "SELECT name FROM sqlite_master " +
                                        "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'" +
                                        "UNION ALL " +
                                        "SELECT name FROM sqlite_temp_master " +
                                        "WHERE type IN ('table','view') " +
                                        "ORDER BY 1";
            DataTable table = ExecuteQuery(query);
 
            // when table is generater, it writes all table names in list that is returned
            ArrayList list = new ArrayList();
            foreach (DataRow row in table.Rows)
            {
                list.Add(row.ItemArray[0].ToString());
            }
            return list;
        }
 
        /// <summary>
        /// Executes query that does not return anything (e.g. UPDATE, INSERT, DELETE).
        /// </summary>
        /// <param name="query"></param>
        public void ExecuteNonQuery(String query)
        {
            // calles SQLite function that executes non-query
            IntPtr error;
			IntPtr ptr = StringToPointer(query);
            sqlite3_exec(database, ptr, IntPtr.Zero, IntPtr.Zero, out error);
            // if there is error, excetion is thrown
            if (error != IntPtr.Zero) {
				HeapFree(GetProcessHeap(), 0, ptr);
				string errorMessage = PointerToString(sqlite3_errmsg(error));
				sqlite3_free(error);
				throw new SystemException("Error with executing non-query: \"" + query + "\"!\n" + errorMessage);
			}
        	HeapFree(GetProcessHeap(), 0, ptr);
        }
 
        /// <summary>
        /// Executes query that does return something (e.g. SELECT).
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public DataTable ExecuteQuery(String query)
        {
            // processed query
            IntPtr statement;
 
            // excess data, it has no use
            IntPtr excessData;
 
            // process query and make statement
			IntPtr ptr = StringToPointer(query);
            sqlite3_prepare_v2(database, ptr, GetPointerLenght(ptr), out statement, out excessData);
            // table for result of function
            DataTable table = new DataTable();
 
            // reads first row - it is different from next rows because it also creates table columns
            // result - returns SLQ_ROW while there is next row
            int result = ReadFirstRow(statement, ref table);
 
            // reads rows
            while (result == SQL_ROW)
            {
                result = ReadNextRow(statement, ref table);
            }
 
            // finalize executing this query
            sqlite3_finalize(statement);
 
            HeapFree(GetProcessHeap(), 0, ptr);
 
            // returns table
            return table;
        }
 
        // private function for reading firs row and creating DataTable
        private int ReadFirstRow(IntPtr statement, ref DataTable table)
        {
            // create new instance of DataTable with name "resultTable"
            table = new DataTable("resultTable");
 
            // evaluates statement 
            int resultType = sqlite3_step(statement);
 
            // if result of statement is SQL_ROW, create new table and write row in it
            if (resultType == SQL_ROW)
            {
                // returns number of columns returned by statement
                int columnCount = sqlite3_column_count(statement);
 
                // declartaion of variables for reading first row
                String columnName = "";
                int columnType = 0;
                object[] columnValues = new object[columnCount];
 
                // reads columns one by one
                for (int i = 0; i < columnCount; i++)
                {
                    // returns the name of current column
                    columnName = PointerToString(sqlite3_column_name(statement, i));
 
                    // returns the type of current column
                    columnType = sqlite3_column_type(statement, i);
 
                    // checks type of columns - neccessary because different functions are required for different types
                    switch (columnType)
                    {
                            // in case of integer column
                        case (int)SQLiteDataTypes.INT:
                            {
                                // adds new integer column to table
                                table.Columns.Add(columnName, Type.GetType("System.Int32"));
 
                                // writes column value in object array
                                columnValues[i] = sqlite3_column_int(statement, i);
                                break;
                            }
                            // same as for integer, this one is for float
                        case (int)SQLiteDataTypes.FLOAT:
                            {
                                table.Columns.Add(columnName, Type.GetType("System.Single"));
                                columnValues[i] = sqlite3_column_double(statement, i);
                                break;
                            }
                            // ... for text
                        case (int)SQLiteDataTypes.TEXT:
                            {
                                table.Columns.Add(columnName, Type.GetType("System.String"));
                                columnValues[i] = PointerToString(sqlite3_column_text(statement, i));
                                break;
                            }
                            // ... for blob - blob are written in table as strings!!
                        case (int)SQLiteDataTypes.BLOB:
                            {
                                table.Columns.Add(columnName, Type.GetType("System.String"));
                                columnValues[i] = PointerToString(sqlite3_column_blob(statement, i));
                                break;
                            }
                            // in case of something other, value is read as string
                        default:
                            {
                                table.Columns.Add(columnName, Type.GetType("System.String"));
                                columnValues[i] = "";
                                break;
                            }
                    }
                }
 
                // writes column values to table
                table.Rows.Add(columnValues);
            }
 
            // evalute statemnet for next results
            return sqlite3_step(statement);
        }
 
        // private function for reading rows other than first
        // it' same like first row, only without creating table and columns
        private int ReadNextRow(IntPtr statement, ref DataTable table)
        {
            int columnCount = sqlite3_column_count(statement);
 
            int columnType = 0;
            object[] columnValues = new object[columnCount];
 
            for (int i = 0; i < columnCount; i++)
            {
                columnType = sqlite3_column_type(statement, i);
 
                switch (columnType)
                {
                    case (int)SQLiteDataTypes.INT:
                        {
                            columnValues[i] = sqlite3_column_int(statement, i);
                            break;
                        }
                    case (int)SQLiteDataTypes.FLOAT:
                        {
                            columnValues[i] = sqlite3_column_double(statement, i);
                            break;
                        }
                    case (int)SQLiteDataTypes.TEXT:
                        {
                            columnValues[i] = PointerToString(sqlite3_column_text(statement, i));
                            break;
                        }
                    case (int)SQLiteDataTypes.BLOB:
                        {
                            columnValues[i] = PointerToString(sqlite3_column_blob(statement, i));
                            break;
                        }
                    default:
                        {
                            columnValues[i] = "";
                            break;
                        }
                }
            }
            table.Rows.Add(columnValues);
            return sqlite3_step(statement);
        }
 
        // converts string to pointer
        private IntPtr StringToPointer(String str)
        {
            // if string is null, pointer is 0
            if (str == null)
            {
                return IntPtr.Zero;
            }
            else
            {
                // else, convert it to pointer
                Encoding encoding = Encoding.UTF8;
                Byte[] bytes = encoding.GetBytes(str);
                int length = bytes.Length + 1;
                IntPtr pointer = HeapAlloc(GetProcessHeap(), 0, (UInt32)length);
                Marshal.Copy(bytes, 0, pointer, bytes.Length);
                Marshal.WriteByte(pointer, bytes.Length, 0);
                return pointer;
            }
        }
 
        // convert pointer to string
        private String PointerToString(IntPtr ptr)
        {
            if (ptr == IntPtr.Zero)
                return null;
 
            Encoding encoding = Encoding.UTF8;
 
            int length = GetPointerLenght(ptr);
            Byte[] bytes = new Byte[length];
            Marshal.Copy(ptr, bytes, 0, length);
            return encoding.GetString(bytes, 0, length);
        }
 
        // returns length of pointer
        private int GetPointerLenght(IntPtr ptr)
        {
            if (ptr == IntPtr.Zero)
                return 0;
            return lstrlen(ptr);
        }
    }
}

 
modified on Wednesday, January 7, 2009 3:07 AM

GeneralRe: Possible memory leak PinmemberJacky__E4:16 23 Dec '08  
GeneralNo more memory leak PinmemberSten Hjelmqvist3:19 23 Sep '09  
GeneralRe: Possible memory leak Pinmemberkidzopa17:28 15 May '10  
GeneralSame Error PinmemberMember 33120559:50 21 Sep '08  
GeneralRe: Same Error PinmemberMember 331205510:21 21 Sep '08  
Generalfacing some bug with your dll Pinmemberchal_adiera23:42 27 Aug '08  
QuestionCan I use it in my Media Player? PinmemberSukhjinder_K1:58 4 Jan '08  
GeneralVery Nice & Easy to Use PinmemberSukhjinder_K22:22 15 Nov '07  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120210.1 | Last Updated 16 Aug 2007
Article Copyright 2007 by fbelic
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid