Lightweight SQLite access from .NET

This is a lightweight (single file) SQLite access layer for executing queries on an sqlite db. Requires a cpu matching sqlite dll (ie the 32 bits sqlite3.dll from http://sqlite.org/download.html requires the host application to be 32bits)

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace RemObjects.Elements.SQLite
{
	public class SQLiteConnection : IDisposable
	{
		private IntPtr handle;

		public IntPtr Handle { get { return handle; } }

		public SQLiteConnection(string fn, bool @readonly)
		{
			var res = SQLite.sqlite3_open_v2(fn, ref handle,
				@readonly ?
				SQLite.SQLITE_OPEN_READONLY :
				SQLite.SQLITE_OPEN_CREATE | SQLite.SQLITE_OPEN_READWRITE,
				null);
			SQLite.Throw(handle, res);
		}


		public void Dispose()
		{
			if (handle != IntPtr.Zero)
				SQLite.sqlite3_close_v2(handle);
			handle = IntPtr.Zero;
		}

		private IntPtr Prepare(string cmd, string[] args = null, object[] val = null)
		{
			byte[] data = Encoding.UTF8.GetBytes(cmd);
			IntPtr res = IntPtr.Zero;
			SQLite.Throw(handle, SQLite.sqlite3_prepare_v2(handle, data, data.Length, ref res, IntPtr.Zero));

			BindArgs(res, args, val);
			return res;
		}

        // Use this with BindArgs(IntPtr, string[], object[]) to set the parameters and 
        // ExecuteCommand(IntPtr) to execute it 
        public IntPtr Prepare(string cmd)
        {
            byte[] data = Encoding.UTF8.GetBytes(cmd);
            IntPtr res = IntPtr.Zero;
            SQLite.Throw(handle, SQLite.sqlite3_prepare_v2(handle, data, data.Length, ref res, IntPtr.Zero));

            return res;
        }

        public int ExecuteCommand(IntPtr res)
        {
            var step = SQLite.sqlite3_step(res);

            if (step != SQLite.SQLITE_DONE)
            {
                SQLite.sqlite3_reset(res);
                SQLite.Throw(handle, step);
                return 0; //unreachable
            }
            int revs = SQLite.sqlite3_changes(handle);
            SQLite.sqlite3_reset(res);
            return revs;
        }

        public void CloseCommand(IntPtr res)
        {
            SQLite.sqlite3_finalize(res);
        }

   
        public void BindArgs(IntPtr stmt, string[] args, object[] val)
		{
			if (args == null) return;
			if (args.Length != val.Length) throw new SQLiteException("val does not match args!");
			for (int i = 0; i < args.Length; i++)
			{
				int n = SQLite.sqlite3_bind_parameter_index(stmt, args[i].StartsWith("@") ? args[i] : "@" + args[i]);
				if (n == 0) throw new SQLiteException("Unknown parameter: " + args[i]);
				var o = val[i];
				if (o == null)
					SQLite.sqlite3_bind_null(stmt, n);
				else if (o is byte[])
					SQLite.sqlite3_bind_blob(stmt, n, (byte[])o, ((byte[])o).Length, IntPtr.Zero);
				else if (o is double)
					SQLite.sqlite3_bind_double(stmt, n, (double)o);
				else if (o is int)
					SQLite.sqlite3_bind_int(stmt, n, (int)o);
				else if (o is long)
					SQLite.sqlite3_bind_int64(stmt, n, (long)o);
				else if (o is String)
					SQLite.sqlite3_bind_text16(stmt, n, (string)o, -1, IntPtr.Zero);
				else
					throw new SQLiteException("Unknown type for " + args[i]);
			}
		}

		public int ExecuteCommand(string cmd, string[] args = null, object[] val = null)
		{
			IntPtr res = Prepare(cmd, args, val);
			var step = SQLite.sqlite3_step(res);

			if (step != SQLite.SQLITE_DONE)
			{
				SQLite.sqlite3_finalize(res);
				SQLite.Throw(handle, step);
				return 0; //unreachable
			}
			int revs = SQLite.sqlite3_changes(handle);
			SQLite.sqlite3_finalize(res);
			return revs;
		}

		public SQLite3Result ExecuteQuery(string cmd, string[] args = null, object[] val = null)
		{
			IntPtr res = Prepare(cmd, args, val);
			return new SQLite3Result(this, res);
		}

		public void BeginTransaction()
		{
			ExecuteCommand("begin transaction");
		}

		public void Commit()
		{
			ExecuteCommand("commit");
		}

		public void Rollback()
		{
			ExecuteCommand("rollback");
		}
	}

	public class SQLite3Result : IDisposable
	{
		private SQLiteConnection conn;
		private IntPtr stmt;
		private Dictionary<string, int> offs;

		public SQLite3Result(SQLiteConnection conn, IntPtr stmt)
		{
			this.conn = conn;
			this.stmt = stmt;
		}

		private void LoadOffsets()
		{
			offs = new Dictionary<string, int>();
			int c = SQLite.sqlite3_column_count(stmt);
			for (int i = 0; i < c; i++)
			{
				offs[Marshal.PtrToStringUni(SQLite.sqlite3_column_name16(stmt, i))] = i;
			}
		}

		public int ColumnIndex(string name)
		{
			if (offs == null)
			{
				LoadOffsets();
			}
			int n;
			if (offs.TryGetValue(name, out n))
				return n;
			return -1;
		}

		int ColumnIndexThrow(string name)
		{
			if (offs == null)
			{
				LoadOffsets();
			}
			return offs[name];
		}

		public bool Next()
		{
			int res = SQLite.sqlite3_step(stmt);
			if (res == SQLite.SQLITE_ROW)
				return true;
			if (res == SQLite.SQLITE_DONE)
				return false;

			SQLite.Throw(conn.Handle, res);
			return false; // unreachable
		}

		public byte[] GetByteArray(int idx)
		{
			IntPtr data = SQLite.sqlite3_column_blob(stmt, idx);
			if (data == IntPtr.Zero) return null;
			byte[] n = new byte[SQLite.sqlite3_column_bytes(stmt, idx)];
			Marshal.Copy(data, n, 0, n.Length);
			return n;
		}

		public byte[] GetByteArray(string idx)
		{
			return GetByteArray(ColumnIndexThrow(idx));
		}

		public double? GetDouble(int idx)
		{
			if (SQLite.sqlite3_column_type(stmt, idx) == SQLite.SQLITE_NULL)
				return null;
			return SQLite.sqlite3_column_double(stmt, idx);
		}
		public int? GetInt(int idx)
		{
			if (SQLite.sqlite3_column_type(stmt, idx) == SQLite.SQLITE_NULL)
				return null;
			return SQLite.sqlite3_column_int(stmt, idx);
		}
		public long? GetLong(int idx)
		{
			if (SQLite.sqlite3_column_type(stmt, idx) == SQLite.SQLITE_NULL)
				return null;
			return SQLite.sqlite3_column_int64(stmt, idx);
		}
		public string GetString(int idx)
		{
			if (SQLite.sqlite3_column_type(stmt, idx) == SQLite.SQLITE_NULL)
				return null;
			return Marshal.PtrToStringUni(SQLite.sqlite3_column_text16(stmt, idx));
		}


		public double? GetDouble(string idx)
		{
			return GetDouble(ColumnIndexThrow(idx));
		}
		public int? GetInt(string idx)
		{
			return GetInt(ColumnIndexThrow(idx));
		}
		public long? GetLong(string idx)
		{
			return GetLong(ColumnIndexThrow(idx));
		}
		public string GetString(string idx)
		{
			return GetString(ColumnIndexThrow(idx));
		}


		public void Dispose()
		{
			SQLite.sqlite3_finalize(stmt);
		}

	}

	public class SQLite
	{
		public const string DLLName = "sqlite3.dll";
		public const string SQLITE_VERSION = "3.8.9";
		public const int SQLITE_VERSION_NUMBER = 3008009;
		public const string SQLITE_SOURCE_ID = "2015-04-01 13:21:33 30011ad2f55cfcacaf23a58ebcc17b17a7b9355e";


		public const int SQLITE_INTEGER = 1;
		public const int SQLITE_FLOAT = 2;
		public const int SQLITE_BLOB = 4;
		public const int SQLITE_NULL = 5;
		public const int SQLITE3_TEXT = 3;


		public const int SQLITE_OK = 0;
		public const int SQLITE_ERROR = 1;
		public const int SQLITE_INTERNAL = 2;
		public const int SQLITE_PERM = 3;
		public const int SQLITE_ABORT = 4;
		public const int SQLITE_BUSY = 5;
		public const int SQLITE_LOCKED = 6;
		public const int SQLITE_NOMEM = 7;
		public const int SQLITE_READONLY = 8;
		public const int SQLITE_INTERRUPT = 9;
		public const int SQLITE_IOERR = 10;
		public const int SQLITE_CORRUPT = 11;
		public const int SQLITE_NOTFOUND = 12;
		public const int SQLITE_FULL = 13;
		public const int SQLITE_CANTOPEN = 14;
		public const int SQLITE_PROTOCOL = 15;
		public const int SQLITE_EMPTY = 16;
		public const int SQLITE_SCHEMA = 17;
		public const int SQLITE_TOOBIG = 18;
		public const int SQLITE_CONSTRAINT = 19;
		public const int SQLITE_MISMATCH = 20;
		public const int SQLITE_MISUSE = 21;
		public const int SQLITE_NOLFS = 22;
		public const int SQLITE_AUTH = 23;
		public const int SQLITE_FORMAT = 24;
		public const int SQLITE_RANGE = 25;
		public const int SQLITE_NOTADB = 26;
		public const int SQLITE_NOTICE = 27;
		public const int SQLITE_WARNING = 28;
		public const int SQLITE_ROW = 100;
		public const int SQLITE_DONE = 101;
		public const int SQLITE_IOERR_READ = (SQLITE_IOERR | (1) << (8));
		public const int SQLITE_IOERR_SHORT_READ = (SQLITE_IOERR | (2) << (8));
		public const int SQLITE_IOERR_WRITE = (SQLITE_IOERR | (3) << (8));
		public const int SQLITE_IOERR_FSYNC = (SQLITE_IOERR | (4) << (8));
		public const int SQLITE_IOERR_DIR_FSYNC = (SQLITE_IOERR | (5) << (8));
		public const int SQLITE_IOERR_TRUNCATE = (SQLITE_IOERR | (6) << (8));
		public const int SQLITE_IOERR_FSTAT = (SQLITE_IOERR | (7) << (8));
		public const int SQLITE_IOERR_UNLOCK = (SQLITE_IOERR | (8) << (8));
		public const int SQLITE_IOERR_RDLOCK = (SQLITE_IOERR | (9) << (8));
		public const int SQLITE_IOERR_DELETE = (SQLITE_IOERR | (10) << (8));
		public const int SQLITE_IOERR_BLOCKED = (SQLITE_IOERR | (11) << (8));
		public const int SQLITE_IOERR_NOMEM = (SQLITE_IOERR | (12) << (8));
		public const int SQLITE_IOERR_ACCESS = (SQLITE_IOERR | (13) << (8));
		public const int SQLITE_IOERR_CHECKRESERVEDLOCK = (SQLITE_IOERR | (14) << (8));
		public const int SQLITE_IOERR_LOCK = (SQLITE_IOERR | (15) << (8));
		public const int SQLITE_IOERR_CLOSE = (SQLITE_IOERR | (16) << (8));
		public const int SQLITE_IOERR_DIR_CLOSE = (SQLITE_IOERR | (17) << (8));
		public const int SQLITE_IOERR_SHMOPEN = (SQLITE_IOERR | (18) << (8));
		public const int SQLITE_IOERR_SHMSIZE = (SQLITE_IOERR | (19) << (8));
		public const int SQLITE_IOERR_SHMLOCK = (SQLITE_IOERR | (20) << (8));
		public const int SQLITE_IOERR_SHMMAP = (SQLITE_IOERR | (21) << (8));
		public const int SQLITE_IOERR_SEEK = (SQLITE_IOERR | (22) << (8));
		public const int SQLITE_IOERR_DELETE_NOENT = (SQLITE_IOERR | (23) << (8));
		public const int SQLITE_IOERR_MMAP = (SQLITE_IOERR | (24) << (8));
		public const int SQLITE_IOERR_GETTEMPPATH = (SQLITE_IOERR | (25) << (8));
		public const int SQLITE_IOERR_CONVPATH = (SQLITE_IOERR | (26) << (8));
		public const int SQLITE_LOCKED_SHAREDCACHE = (SQLITE_LOCKED | (1) << (8));
		public const int SQLITE_BUSY_RECOVERY = (SQLITE_BUSY | (1) << (8));
		public const int SQLITE_BUSY_SNAPSHOT = (SQLITE_BUSY | (2) << (8));
		public const int SQLITE_CANTOPEN_NOTEMPDIR = (SQLITE_CANTOPEN | (1) << (8));
		public const int SQLITE_CANTOPEN_ISDIR = (SQLITE_CANTOPEN | (2) << (8));
		public const int SQLITE_CANTOPEN_FULLPATH = (SQLITE_CANTOPEN | (3) << (8));
		public const int SQLITE_CANTOPEN_CONVPATH = (SQLITE_CANTOPEN | (4) << (8));
		public const int SQLITE_CORRUPT_VTAB = (SQLITE_CORRUPT | (1) << (8));
		public const int SQLITE_READONLY_RECOVERY = (SQLITE_READONLY | (1) << (8));
		public const int SQLITE_READONLY_CANTLOCK = (SQLITE_READONLY | (2) << (8));
		public const int SQLITE_READONLY_ROLLBACK = (SQLITE_READONLY | (3) << (8));
		public const int SQLITE_READONLY_DBMOVED = (SQLITE_READONLY | (4) << (8));
		public const int SQLITE_ABORT_ROLLBACK = (SQLITE_ABORT | (2) << (8));
		public const int SQLITE_CONSTRAINT_CHECK = (SQLITE_CONSTRAINT | (1) << (8));
		public const int SQLITE_CONSTRAINT_COMMITHOOK = (SQLITE_CONSTRAINT | (2) << (8));
		public const int SQLITE_CONSTRAINT_FOREIGNKEY = (SQLITE_CONSTRAINT | (3) << (8));
		public const int SQLITE_CONSTRAINT_FUNCTION = (SQLITE_CONSTRAINT | (4) << (8));
		public const int SQLITE_CONSTRAINT_NOTNULL = (SQLITE_CONSTRAINT | (5) << (8));
		public const int SQLITE_CONSTRAINT_PRIMARYKEY = (SQLITE_CONSTRAINT | (6) << (8));
		public const int SQLITE_CONSTRAINT_TRIGGER = (SQLITE_CONSTRAINT | (7) << (8));
		public const int SQLITE_CONSTRAINT_UNIQUE = (SQLITE_CONSTRAINT | (8) << (8));
		public const int SQLITE_CONSTRAINT_VTAB = (SQLITE_CONSTRAINT | (9) << (8));
		public const int SQLITE_CONSTRAINT_ROWID = (SQLITE_CONSTRAINT | (10) << (8));
		public const int SQLITE_NOTICE_RECOVER_WAL = (SQLITE_NOTICE | (1) << (8));
		public const int SQLITE_NOTICE_RECOVER_ROLLBACK = (SQLITE_NOTICE | (2) << (8));
		public const int SQLITE_WARNING_AUTOINDEX = (SQLITE_WARNING | (1) << (8));
		public const int SQLITE_AUTH_USER = (SQLITE_AUTH | (1) << (8));
		public const int SQLITE_OPEN_READONLY = 1;
		public const int SQLITE_OPEN_READWRITE = 2;
		public const int SQLITE_OPEN_CREATE = 4;
		public const int SQLITE_OPEN_DELETEONCLOSE = 8;
		public const int SQLITE_OPEN_EXCLUSIVE = 16;
		public const int SQLITE_OPEN_AUTOPROXY = 32;
		public const int SQLITE_OPEN_URI = 64;
		public const int SQLITE_OPEN_MEMORY = 128;
		public const int SQLITE_OPEN_MAIN_DB = 256;
		public const int SQLITE_OPEN_TEMP_DB = 512;
		public const int SQLITE_OPEN_TRANSIENT_DB = 1024;
		public const int SQLITE_OPEN_MAIN_JOURNAL = 2048;
		public const int SQLITE_OPEN_TEMP_JOURNAL = 4096;
		public const int SQLITE_OPEN_SUBJOURNAL = 8192;
		public const int SQLITE_OPEN_MASTER_JOURNAL = 16384;
		public const int SQLITE_OPEN_NOMUTEX = 32768;
		public const int SQLITE_OPEN_FULLMUTEX = 65536;
		public const int SQLITE_OPEN_SHAREDCACHE = 131072;
		public const int SQLITE_OPEN_PRIVATECACHE = 262144;
		public const int SQLITE_OPEN_WAL = 524288;
		public const int SQLITE_IOCAP_ATOMIC = 1;
		public const int SQLITE_IOCAP_ATOMIC512 = 2;
		public const int SQLITE_IOCAP_ATOMIC1K = 4;
		public const int SQLITE_IOCAP_ATOMIC2K = 8;
		public const int SQLITE_IOCAP_ATOMIC4K = 16;
		public const int SQLITE_IOCAP_ATOMIC8K = 32;
		public const int SQLITE_IOCAP_ATOMIC16K = 64;
		public const int SQLITE_IOCAP_ATOMIC32K = 128;
		public const int SQLITE_IOCAP_ATOMIC64K = 256;
		public const int SQLITE_IOCAP_SAFE_APPEND = 512;
		public const int SQLITE_IOCAP_SEQUENTIAL = 1024;
		public const int SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 2048;
		public const int SQLITE_IOCAP_POWERSAFE_OVERWRITE = 4096;
		public const int SQLITE_IOCAP_IMMUTABLE = 8192;
		public const int SQLITE_LOCK_NONE = 0;
		public const int SQLITE_LOCK_SHARED = 1;
		public const int SQLITE_LOCK_RESERVED = 2;
		public const int SQLITE_LOCK_PENDING = 3;
		public const int SQLITE_LOCK_EXCLUSIVE = 4;
		public const int SQLITE_SYNC_NORMAL = 2;
		public const int SQLITE_SYNC_FULL = 3;
		public const int SQLITE_SYNC_DATAONLY = 16;

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern IntPtr sqlite3_libversion();


		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern IntPtr sqlite3_sourceid();
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_libversion_number();


		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern void sqlite3_close_v2(IntPtr handle);


		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern long sqlite3_last_insert_rowid(/*sqlite3* */IntPtr handle);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_changes(/*sqlite3* */IntPtr handle);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_open_v2(
			String filename,   /* Database filename (UTF-8) */
			ref IntPtr handle,         /* OUT: SQLite db handle */
			int flags,              /* Flags */
			String zVfs        /* Name of VFS module to use */
			);


		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		public static extern int sqlite3_prepare_v2(
  IntPtr /*sqlite3 **/db,            /* Database handle */
  byte[] zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
			/*sqlite3_stmt* */ref IntPtr ppStmt,  /* OUT: Statement handle */
  IntPtr pzTail     /* OUT: Pointer to unused portion of zSql */
);


		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_column_count(/*sqlite3_stmt* */IntPtr pStmt);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		public static extern IntPtr sqlite3_column_name16(/*sqlite3_stmt* */IntPtr stmt, int N);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_bind_blob(/*sqlite3_stmt* */IntPtr st, int idx, byte[] data, int len, IntPtr free);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_bind_double(/*sqlite3_stmt* */IntPtr st, int idx, double val);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_bind_int(/*sqlite3_stmt* */IntPtr st, int idx, int val);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_bind_int64(/*sqlite3_stmt* */IntPtr st, int idx, long val);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_bind_null(/*sqlite3_stmt* */IntPtr st, int idx);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		public static extern int sqlite3_bind_text16(/*sqlite3_stmt* */IntPtr st, int idx, String val, int len, IntPtr free);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_bind_parameter_count(/*sqlite3_stmt* */IntPtr st);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern IntPtr sqlite3_bind_parameter_name(/*sqlite3_stmt* */IntPtr st, int idx);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_bind_parameter_index(/*sqlite3_stmt* */IntPtr st, String zName);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_step(/*sqlite3_stmt* */IntPtr st);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern IntPtr sqlite3_column_blob(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_column_bytes(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		public static extern int sqlite3_column_bytes16(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern double sqlite3_column_double(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_column_int(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern long sqlite3_column_int64(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		public static extern IntPtr sqlite3_column_text16(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_column_type(/*sqlite3_stmt* */IntPtr st, int iCol);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_finalize(/*sqlite3_stmt* */IntPtr pStmt);
		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		public static extern int sqlite3_reset(/*sqlite3_stmt* */IntPtr pStmt);

		[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		public static extern IntPtr sqlite3_errmsg16(/*sqlite3**/ IntPtr handle);

		public static void Throw(IntPtr handle, int res)
		{
			switch (res)
			{
				case 0: break;
				case SQLITE_ERROR: 
					if (handle == IntPtr.Zero)
						throw new SQLiteException("SQL error or missing database ");
					throw new SQLiteException(Marshal.PtrToStringUni(SQLite.sqlite3_errmsg16(handle)));
				case SQLITE_INTERNAL: throw new SQLiteException("Internal logic error in SQLite ");
				case SQLITE_PERM: throw new SQLiteException("Access permission denied ");
				case SQLITE_ABORT: throw new SQLiteException("Callback routine requested an abort ");
				case SQLITE_BUSY: throw new SQLiteException("The database file is locked ");
				case SQLITE_LOCKED: throw new SQLiteException("A table in the database is locked ");
				case SQLITE_NOMEM: throw new SQLiteException("A malloc() failed ");
				case SQLITE_READONLY: throw new SQLiteException("Attempt to write a readonly database ");
				case SQLITE_INTERRUPT: throw new SQLiteException("Operation terminated by sqlite3_interrupt()");
				case SQLITE_IOERR: throw new SQLiteException("Some kind of disk I/O error occurred ");
				case SQLITE_CORRUPT: throw new SQLiteException("The database disk image is malformed ");
				case SQLITE_NOTFOUND: throw new SQLiteException("Unknown opcode in sqlite3_file_control() ");
				case SQLITE_FULL: throw new SQLiteException("Insertion failed because database is full ");
				case SQLITE_CANTOPEN: throw new SQLiteException("Unable to open the database file ");
				case SQLITE_PROTOCOL: throw new SQLiteException("Database lock protocol error ");
				case SQLITE_EMPTY: throw new SQLiteException("Database is empty ");
				case SQLITE_SCHEMA: throw new SQLiteException("The database schema changed ");
				case SQLITE_TOOBIG: throw new SQLiteException("String or BLOB exceeds size limit ");
				case SQLITE_CONSTRAINT: throw new SQLiteException("Abort due to constraint violation ");
				case SQLITE_MISMATCH: throw new SQLiteException("Data type mismatch ");
				case SQLITE_MISUSE: throw new SQLiteException("Library used incorrectly ");
				case SQLITE_NOLFS: throw new SQLiteException("Uses OS features not supported on host ");
				case SQLITE_AUTH: throw new SQLiteException("Authorization denied ");
				case SQLITE_FORMAT: throw new SQLiteException("Auxiliary database format error ");
				case SQLITE_RANGE: throw new SQLiteException("2nd parameter to sqlite3_bind out of range ");
				case SQLITE_NOTADB: throw new SQLiteException("File opened that is not a database file ");
				case SQLITE_NOTICE: throw new SQLiteException("Notifications from sqlite3_log() ");
				case SQLITE_WARNING: throw new SQLiteException("Warnings from sqlite3_log() ");
				case SQLITE_ROW: throw new SQLiteException("sqlite3_step() has another row ready ");
				case SQLITE_DONE: throw new SQLiteException("sqlite3_step() has finished executing ");
				default:
					throw new SQLiteException("unknown error");
			}
		}
	}
	class SQLiteException : Exception { public SQLiteException(String s) : base(s) { } }
}