Click here to Skip to main content
Email Password   helpLost your password?

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:

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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralA possible alternative; NFileStorage
snip1
7:54 17 Mar '09  
See an alternative file storage engine to SQLite (c#.NET)/[^] Thumbs Up

bla

Generalcreate a database
Chaozzster
7:28 16 Feb '09  
hi everyone!
how can I create a new database in file e.g. test.db?
GeneralRe: create a database
thk_sompi
13:16 1 Mar '09  
Just call
SQLiteBase db = new SQLiteBase("test.db");

If the file doesn't exist, it's created by default
GeneralRe: create a database
Ken Eucker
10:25 3 Jun '09  
"SQLiteBase db = new SQLiteBase("test.db");"

throws an exception no matter what I do...
GeneralRe: create a database
fbelic
0:14 4 Jun '09  
What's the text of the exception?
GeneralRe: create a database
Ken Eucker
22:19 4 Jun '09  
"Cannot create instance of 'Window1' defined in assembly 'sqlSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Exception has been thrown by the target of an invocation. Error in markup file 'Window1.xaml' Line 1 Position 9."

In debugging the error, it happens when I try to open a database (the file does not currently exist). But it should make the db file if it doesn't exist correct?

I am not entirely sure that I am implementing this correctly. From a blank solution all I have to do is insert the dll you provided into the project as a reference and I can start making/using sqlite?
Generalout of memory
saifsail
9:32 24 Jan '09  
I seem to be getting an out of memory error(taskman says i am not even using 40% of mem). Here is the full execption message:

Error with executing non-query: "INSERT INTO user(username,password,email,network) VALUES('bob','dylan','bob.dylan@live.com','bd')"!
out of memory


GeneralPossible memory leak
Jacky__E
4:34 24 Nov '08  
The documentation for SQLite reads:

"The error message passed back through the 5th parameter is held in memory obtained from sqlite3_malloc(). To avoid a memory leak, the calling application should call sqlite3_free() on any error message returned through the 5th parameter when it has finished using the error message."

A possible fix could be:
1. add the sqlite3_free function pointer
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_free(IntPtr error);

2. free the error memory in the catch of the ExecuteNonQuery function
string errorMessage = PointerToString(sqlite3_errmsg(error));
sqlite3_free(error);
throw new SqlQueryException("Error with executing non-query: \"" + query + "\"!\n" + errorMessage);

GeneralRe: Possible memory leak [modified]
oren.shnitzer
11: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 itFrown , 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
Jacky__E
4:16 23 Dec '08  
Great fix, thanks! I found no more leaks. Smile
GeneralNo more memory leak
Sten Hjelmqvist
3:19 23 Sep '09  
I used oren.shnitzer's modified code and inserted ~20 million records into a table without any leaks.

Thanks Oren

---------------
Sten Hjelmqvist

GeneralSame Error
Member 3312055
9:50 21 Sep '08  
Hi I am getting a simmilar error message: Unable to load DLL 'sqlite3': The specified module could not be found. Any ideas?
GeneralRe: Same Error
Member 3312055
10:21 21 Sep '08  
Figured it out... go to http://www.sqlite.org/sqlite-3_6_2.zip[^] and download the sqlite3.dll. Place this in the bin\debug folder and you'll be away.
Generalfacing some bug with your dll
chal_adiera
23:42 27 Aug '08  
hii..

i have some error like this after debug..

"Unable to find an entry point named 'sqlite3_prepare_v2' in DLL 'sqlite3'"

i already add reference SQLiteWrapper..

Can you help me..

Tq..
need some guide
chalAdiera
GeneralCan I use it in my Media Player?
Sukhjinder_K
1:58 4 Jan '08  
I'd like to use your classes in my MediaPlayer LetsYo. It is opensource and free. So do you grant me the permission to do so. Also please let me know what would you like to have in the Credits to you section.

Thanks
GeneralVery Nice & Easy to Use
Sukhjinder_K
22:22 15 Nov '07  
Thanks for the Wrapper. Works Wonderfully. flangset Mem Leak[^]has something to say about memory leaks, will you please look into it.

Thanks...
AnswerRe: Very Nice & Easy to Use
Jacky__E
4:18 23 Dec '08  
oren.shnitzer fixed the leaks. check his comment
GeneralThanks for this class
flangset
1:02 29 Aug '07  
This wrapper is just as easy and lightweight as SQLite itself. Makes implementing an embeded database into C# like a charm. There is a slight memory leak due to the HeapAlloc calls. Just call HeapFree for every HeapAlloc.

Frode


Last Updated 16 Aug 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010