(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 , 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
{
public class SQLiteBase
{
[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);
[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);
private const int SQL_OK = 0;
private const int SQL_ROW = 100;
private const int SQL_DONE = 101;
public enum SQLiteDataTypes {
INT = 1,
FLOAT,
TEXT,
BLOB,
NULL };
private IntPtr database;
public SQLiteBase()
{
database = IntPtr.Zero;
}
public SQLiteBase(String baseName)
{
OpenDatabase(baseName);
}
public void OpenDatabase(String baseName)
{
IntPtr ptr = StringToPointer(baseName);
if (sqlite3_open(ptr, out database) != SQL_OK)
{
database = IntPtr.Zero;
HeapFree(GetProcessHeap(), 0, ptr);
throw new Exception("Error with opening database " + baseName + "!");
}
HeapFree(GetProcessHeap(), 0, ptr);
}
public void CloseDatabase()
{
if (database != IntPtr.Zero)
{
sqlite3_close(database);
}
}
public ArrayList GetTables()
{
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);
ArrayList list = new ArrayList();
foreach (DataRow row in table.Rows)
{
list.Add(row.ItemArray[0].ToString());
}
return list;
}
public void ExecuteNonQuery(String query)
{
IntPtr error;
IntPtr ptr = StringToPointer(query);
sqlite3_exec(database, ptr, IntPtr.Zero, IntPtr.Zero, out error);
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);
}
public DataTable ExecuteQuery(String query)
{
IntPtr statement;
IntPtr excessData;
IntPtr ptr = StringToPointer(query);
sqlite3_prepare_v2(database, ptr, GetPointerLenght(ptr), out statement, out excessData);
DataTable table = new DataTable();
int result = ReadFirstRow(statement, ref table);
while (result == SQL_ROW)
{
result = ReadNextRow(statement, ref table);
}
sqlite3_finalize(statement);
HeapFree(GetProcessHeap(), 0, ptr);
return table;
}
private int ReadFirstRow(IntPtr statement, ref DataTable table)
{
table = new DataTable("resultTable");
int resultType = sqlite3_step(statement);
if (resultType == SQL_ROW)
{
int columnCount = sqlite3_column_count(statement);
String columnName = "";
int columnType = 0;
object[] columnValues = new object[columnCount];
for (int i = 0; i < columnCount; i++)
{
columnName = PointerToString(sqlite3_column_name(statement, i));
columnType = sqlite3_column_type(statement, i);
switch (columnType)
{
case (int)SQLiteDataTypes.INT:
{
table.Columns.Add(columnName, Type.GetType("System.Int32"));
columnValues[i] = sqlite3_column_int(statement, i);
break;
}
case (int)SQLiteDataTypes.FLOAT:
{
table.Columns.Add(columnName, Type.GetType("System.Single"));
columnValues[i] = sqlite3_column_double(statement, i);
break;
}
case (int)SQLiteDataTypes.TEXT:
{
table.Columns.Add(columnName, Type.GetType("System.String"));
columnValues[i] = PointerToString(sqlite3_column_text(statement, i));
break;
}
case (int)SQLiteDataTypes.BLOB:
{
table.Columns.Add(columnName, Type.GetType("System.String"));
columnValues[i] = PointerToString(sqlite3_column_blob(statement, i));
break;
}
default:
{
table.Columns.Add(columnName, Type.GetType("System.String"));
columnValues[i] = "";
break;
}
}
}
table.Rows.Add(columnValues);
}
return sqlite3_step(statement);
}
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);
}
private IntPtr StringToPointer(String str)
{
if (str == null)
{
return IntPtr.Zero;
}
else
{
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;
}
}
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);
}
private int GetPointerLenght(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return 0;
return lstrlen(ptr);
}
}
}
modified on Wednesday, January 7, 2009 3:07 AM
|