/********************************************************
* ADO.NET 2.0 Data Provider for SQLite Version 3.X
* Written by Joe Mistachkin (joe@mistachkin.com)
*
* Released to the public domain, use at your own risk!
********************************************************/
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
namespace System.Data.SQLite
{
#region SQLiteContext Helper Class
///
/// This class represents a context from the SQLite core library that can
/// be passed to the sqlite3_result_*() and associated functions.
///
public sealed class SQLiteContext : ISQLiteNativeHandle
{
#region Private Data
///
/// The native context handle.
///
private IntPtr pContext;
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Constructors
///
/// Constructs an instance of this class using the specified native
/// context handle.
///
///
/// The native context handle to use.
///
internal SQLiteContext(IntPtr pContext)
{
this.pContext = pContext;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region ISQLiteNativeHandle Members
///
/// Returns the underlying SQLite native handle associated with this
/// object instance.
///
public IntPtr NativeHandle
{
get { return pContext; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Methods
///
/// Sets the context result to NULL.
///
public void SetNull()
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
UnsafeNativeMethods.sqlite3_result_null(pContext);
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified
/// value.
///
///
/// The value to use.
///
public void SetDouble(double value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
#if !PLATFORM_COMPACTFRAMEWORK
UnsafeNativeMethods.sqlite3_result_double(pContext, value);
#elif !SQLITE_STANDARD
UnsafeNativeMethods.sqlite3_result_double_interop(pContext, ref value);
#else
throw new NotImplementedException();
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified
/// value.
///
///
/// The value to use.
///
public void SetInt(int value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
UnsafeNativeMethods.sqlite3_result_int(pContext, value);
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified
/// value.
///
///
/// The value to use.
///
public void SetInt64(long value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
#if !PLATFORM_COMPACTFRAMEWORK
UnsafeNativeMethods.sqlite3_result_int64(pContext, value);
#elif !SQLITE_STANDARD
UnsafeNativeMethods.sqlite3_result_int64_interop(pContext, ref value);
#else
throw new NotImplementedException();
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified
/// value.
///
///
/// The value to use. This value will be
/// converted to the UTF-8 encoding prior to being used.
///
public void SetString(string value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
byte[] bytes = SQLiteString.GetUtf8BytesFromString(value);
if (bytes == null)
throw new ArgumentNullException("value");
UnsafeNativeMethods.sqlite3_result_text(
pContext, bytes, bytes.Length, (IntPtr)(-1));
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified
/// value containing an error message.
///
///
/// The value containing the error message text.
/// This value will be converted to the UTF-8 encoding prior to being
/// used.
///
public void SetError(string value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
byte[] bytes = SQLiteString.GetUtf8BytesFromString(value);
if (bytes == null)
throw new ArgumentNullException("value");
UnsafeNativeMethods.sqlite3_result_error(
pContext, bytes, bytes.Length);
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified
/// value.
///
///
/// The value to use.
///
public void SetErrorCode(SQLiteErrorCode value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
UnsafeNativeMethods.sqlite3_result_error_code(pContext, value);
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to contain the error code SQLITE_TOOBIG.
///
public void SetErrorTooBig()
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
UnsafeNativeMethods.sqlite3_result_error_toobig(pContext);
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to contain the error code SQLITE_NOMEM.
///
public void SetErrorNoMemory()
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
UnsafeNativeMethods.sqlite3_result_error_nomem(pContext);
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified array
/// value.
///
///
/// The array value to use.
///
public void SetBlob(byte[] value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
if (value == null)
throw new ArgumentNullException("value");
UnsafeNativeMethods.sqlite3_result_blob(
pContext, value, value.Length, (IntPtr)(-1));
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to a BLOB of zeros of the specified size.
///
///
/// The number of zero bytes to use for the BLOB context result.
///
public void SetZeroBlob(int value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
UnsafeNativeMethods.sqlite3_result_zeroblob(pContext, value);
}
///////////////////////////////////////////////////////////////////////
///
/// Sets the context result to the specified .
///
///
/// The to use.
///
public void SetValue(SQLiteValue value)
{
if (pContext == IntPtr.Zero)
throw new InvalidOperationException();
if (value == null)
throw new ArgumentNullException("value");
UnsafeNativeMethods.sqlite3_result_value(
pContext, value.NativeHandle);
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteValue Helper Class
///
/// This class represents a value from the SQLite core library that can be
/// passed to the sqlite3_value_*() and associated functions.
///
public sealed class SQLiteValue : ISQLiteNativeHandle
{
#region Private Data
///
/// The native value handle.
///
private IntPtr pValue;
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Constructors
///
/// Constructs an instance of this class using the specified native
/// value handle.
///
///
/// The native value handle to use.
///
private SQLiteValue(IntPtr pValue)
{
this.pValue = pValue;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Methods
///
/// Invalidates the native value handle, thereby preventing further
/// access to it from this object instance.
///
private void PreventNativeAccess()
{
pValue = IntPtr.Zero;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Internal Marshal Helper Methods
///
/// Converts a logical array of native pointers to native sqlite3_value
/// structures into a managed array of
/// object instances.
///
///
/// The number of elements in the logical array of native sqlite3_value
/// structures.
///
///
/// The native pointer to the logical array of native sqlite3_value
/// structures to convert.
///
///
/// The managed array of object instances or
/// null upon failure.
///
internal static SQLiteValue[] ArrayFromSizeAndIntPtr(
int argc,
IntPtr argv
)
{
if (argc < 0)
return null;
if (argv == IntPtr.Zero)
return null;
SQLiteValue[] result = new SQLiteValue[argc];
for (int index = 0, offset = 0;
index < result.Length;
index++, offset += IntPtr.Size)
{
IntPtr pArg = SQLiteMarshal.ReadIntPtr(argv, offset);
result[index] = (pArg != IntPtr.Zero) ?
new SQLiteValue(pArg) : null;
}
return result;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region ISQLiteNativeHandle Members
///
/// Returns the underlying SQLite native handle associated with this
/// object instance.
///
public IntPtr NativeHandle
{
get { return pValue; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
private bool persisted;
///
/// Returns non-zero if the native SQLite value has been successfully
/// persisted as a managed value within this object instance (i.e. the
/// property may then be read successfully).
///
public bool Persisted
{
get { return persisted; }
}
///////////////////////////////////////////////////////////////////////
private object value;
///
/// If the managed value for this object instance is available (i.e. it
/// has been previously persisted via the ) method,
/// that value is returned; otherwise, an exception is thrown. The
/// returned value may be null.
///
public object Value
{
get
{
if (!persisted)
{
throw new InvalidOperationException(
"value was not persisted");
}
return value;
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Methods
///
/// Gets and returns the type affinity associated with this value.
///
///
/// The type affinity associated with this value.
///
public TypeAffinity GetTypeAffinity()
{
if (pValue == IntPtr.Zero) return TypeAffinity.None;
return UnsafeNativeMethods.sqlite3_value_type(pValue);
}
///////////////////////////////////////////////////////////////////////
///
/// Gets and returns the number of bytes associated with this value, if
/// it refers to a UTF-8 encoded string.
///
///
/// The number of bytes associated with this value. The returned value
/// may be zero.
///
public int GetBytes()
{
if (pValue == IntPtr.Zero) return 0;
return UnsafeNativeMethods.sqlite3_value_bytes(pValue);
}
///////////////////////////////////////////////////////////////////////
///
/// Gets and returns the associated with this
/// value.
///
///
/// The associated with this value.
///
public int GetInt()
{
if (pValue == IntPtr.Zero) return default(int);
return UnsafeNativeMethods.sqlite3_value_int(pValue);
}
///////////////////////////////////////////////////////////////////////
///
/// Gets and returns the associated with
/// this value.
///
///
/// The associated with this value.
///
public long GetInt64()
{
if (pValue == IntPtr.Zero) return default(long);
#if !PLATFORM_COMPACTFRAMEWORK
return UnsafeNativeMethods.sqlite3_value_int64(pValue);
#elif !SQLITE_STANDARD
long value;
UnsafeNativeMethods.sqlite3_value_int64_interop(pValue, out value);
return value;
#else
throw new NotImplementedException();
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Gets and returns the associated with this
/// value.
///
///
/// The associated with this value.
///
public double GetDouble()
{
if (pValue == IntPtr.Zero) return default(double);
#if !PLATFORM_COMPACTFRAMEWORK
return UnsafeNativeMethods.sqlite3_value_double(pValue);
#elif !SQLITE_STANDARD
double value;
UnsafeNativeMethods.sqlite3_value_double_interop(pValue, out value);
return value;
#else
throw new NotImplementedException();
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Gets and returns the associated with this
/// value.
///
///
/// The associated with this value. The value is
/// converted from the UTF-8 encoding prior to being returned.
///
public string GetString()
{
if (pValue == IntPtr.Zero) return null;
return SQLiteString.StringFromUtf8IntPtr(pValue, GetBytes());
}
///////////////////////////////////////////////////////////////////////
///
/// Gets and returns the array associated with this
/// value.
///
///
/// The array associated with this value.
///
public byte[] GetBlob()
{
if (pValue == IntPtr.Zero) return null;
return SQLiteBytes.FromIntPtr(pValue, GetBytes());
}
///////////////////////////////////////////////////////////////////////
///
/// Uses the native value handle to obtain and store the managed value
/// for this object instance, thus saving it for later use. The type
/// of the managed value is determined by the type affinity of the
/// native value. If the type affinity is not recognized by this
/// method, no work is done and false is returned.
///
///
/// Non-zero if the native value was persisted successfully.
///
public bool Persist()
{
switch (GetTypeAffinity())
{
case TypeAffinity.Uninitialized:
{
value = null;
PreventNativeAccess();
return (persisted = true);
}
case TypeAffinity.Int64:
{
value = GetInt64();
PreventNativeAccess();
return (persisted = true);
}
case TypeAffinity.Double:
{
value = GetDouble();
PreventNativeAccess();
return (persisted = true);
}
case TypeAffinity.Text:
{
value = GetString();
PreventNativeAccess();
return (persisted = true);
}
case TypeAffinity.Blob:
{
value = GetBytes();
PreventNativeAccess();
return (persisted = true);
}
case TypeAffinity.Null:
{
value = DBNull.Value;
PreventNativeAccess();
return (persisted = true);
}
default:
{
return false;
}
}
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteIndexConstraintOp Enumeration
///
/// These are the allowed values for the operators that are part of a
/// constraint term in the WHERE clause of a query that uses a virtual
/// table.
///
public enum SQLiteIndexConstraintOp : byte
{
///
/// This value represents the equality operator.
///
EqualTo = 2,
///
/// This value represents the greater than operator.
///
GreaterThan = 4,
///
/// This value represents the less than or equal to operator.
///
LessThanOrEqualTo = 8,
///
/// This value represents the less than operator.
///
LessThan = 16,
///
/// This value represents the greater than or equal to operator.
///
GreaterThanOrEqualTo = 32,
///
/// This value represents the MATCH operator.
///
Match = 64
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteIndexConstraint Helper Class
///
/// This class represents the native sqlite3_index_constraint structure
/// from the SQLite core library.
///
public sealed class SQLiteIndexConstraint
{
#region Internal Constructors
///
/// Constructs an instance of this class using the specified native
/// sqlite3_index_constraint structure.
///
///
/// The native sqlite3_index_constraint structure to use.
///
internal SQLiteIndexConstraint(
UnsafeNativeMethods.sqlite3_index_constraint constraint
)
: this(constraint.iColumn, constraint.op, constraint.usable,
constraint.iTermOffset)
{
// do nothing.
}
#endregion
//////////////////////////////////////////////////////////////////////
#region Private Constructors
///
/// Constructs an instance of this class using the specified field
/// values.
///
///
/// Column on left-hand side of constraint.
///
///
/// Constraint operator ().
///
///
/// True if this constraint is usable.
///
///
/// Used internally -
/// should ignore.
///
private SQLiteIndexConstraint(
int iColumn,
SQLiteIndexConstraintOp op,
byte usable,
int iTermOffset
)
{
this.iColumn = iColumn;
this.op = op;
this.usable = usable;
this.iTermOffset = iTermOffset;
}
#endregion
//////////////////////////////////////////////////////////////////////
#region Public Fields
///
/// Column on left-hand side of constraint.
///
public int iColumn;
//////////////////////////////////////////////////////////////////////
///
/// Constraint operator ().
///
public SQLiteIndexConstraintOp op;
//////////////////////////////////////////////////////////////////////
///
/// True if this constraint is usable.
///
public byte usable;
//////////////////////////////////////////////////////////////////////
///
/// Used internally -
/// should ignore.
///
public int iTermOffset;
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteIndexOrderBy Helper Class
///
/// This class represents the native sqlite3_index_orderby structure from
/// the SQLite core library.
///
public sealed class SQLiteIndexOrderBy
{
#region Internal Constructors
///
/// Constructs an instance of this class using the specified native
/// sqlite3_index_orderby structure.
///
///
/// The native sqlite3_index_orderby structure to use.
///
internal SQLiteIndexOrderBy(
UnsafeNativeMethods.sqlite3_index_orderby orderBy
)
: this(orderBy.iColumn, orderBy.desc)
{
// do nothing.
}
#endregion
//////////////////////////////////////////////////////////////////////
#region Private Constructors
///
/// Constructs an instance of this class using the specified field
/// values.
///
///
/// Column number.
///
///
/// True for DESC. False for ASC.
///
private SQLiteIndexOrderBy(
int iColumn,
byte desc
)
{
this.iColumn = iColumn;
this.desc = desc;
}
#endregion
//////////////////////////////////////////////////////////////////////
#region Public Fields
///
/// Column number.
///
public int iColumn;
//////////////////////////////////////////////////////////////////////
///
/// True for DESC. False for ASC.
///
public byte desc;
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteIndexConstraintUsage Helper Class
///
/// This class represents the native sqlite3_index_constraint_usage
/// structure from the SQLite core library.
///
public sealed class SQLiteIndexConstraintUsage
{
#region Internal Constructors
///
/// Constructs an instance of this class using the specified native
/// sqlite3_index_constraint_usage structure.
///
///
/// The native sqlite3_index_constraint_usage structure to use.
///
internal SQLiteIndexConstraintUsage(
UnsafeNativeMethods.sqlite3_index_constraint_usage constraintUsage
)
: this(constraintUsage.argvIndex, constraintUsage.omit)
{
// do nothing.
}
#endregion
//////////////////////////////////////////////////////////////////////
#region Private Constructors
///
/// Constructs an instance of this class using the specified field
/// values.
///
///
/// If greater than 0, constraint is part of argv to xFilter.
///
///
/// Do not code a test for this constraint.
///
private SQLiteIndexConstraintUsage(
int argvIndex,
byte omit
)
{
this.argvIndex = argvIndex;
this.omit = omit;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Fields
///
/// If greater than 0, constraint is part of argv to xFilter.
///
public int argvIndex;
///////////////////////////////////////////////////////////////////////
///
/// Do not code a test for this constraint.
///
public byte omit;
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteIndexInputs Helper Class
///
/// This class represents the various inputs provided by the SQLite core
/// library to the method.
///
public sealed class SQLiteIndexInputs
{
#region Internal Constructors
///
/// Constructs an instance of this class.
///
///
/// The number of instances to
/// pre-allocate space for.
///
///
/// The number of instances to
/// pre-allocate space for.
///
internal SQLiteIndexInputs(int nConstraint, int nOrderBy)
{
constraints = new SQLiteIndexConstraint[nConstraint];
orderBys = new SQLiteIndexOrderBy[nOrderBy];
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
private SQLiteIndexConstraint[] constraints;
///
/// An array of object instances,
/// each containing information supplied by the SQLite core library.
///
public SQLiteIndexConstraint[] Constraints
{
get { return constraints; }
}
///////////////////////////////////////////////////////////////////////
private SQLiteIndexOrderBy[] orderBys;
///
/// An array of object instances,
/// each containing information supplied by the SQLite core library.
///
public SQLiteIndexOrderBy[] OrderBys
{
get { return orderBys; }
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteIndexOutputs Helper Class
///
/// This class represents the various outputs provided to the SQLite core
/// library by the method.
///
public sealed class SQLiteIndexOutputs
{
#region Internal Constructors
///
/// Constructs an instance of this class.
///
///
/// The number of instances
/// to pre-allocate space for.
///
internal SQLiteIndexOutputs(int nConstraint)
{
constraintUsages = new SQLiteIndexConstraintUsage[nConstraint];
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
private SQLiteIndexConstraintUsage[] constraintUsages;
///
/// An array of object
/// instances, each containing information to be supplied to the SQLite
/// core library.
///
public SQLiteIndexConstraintUsage[] ConstraintUsages
{
get { return constraintUsages; }
}
///////////////////////////////////////////////////////////////////////
private int indexNumber;
///
/// Number used to help identify the selected index. This value will
/// later be provided to the
/// method.
///
public int IndexNumber
{
get { return indexNumber; }
set { indexNumber = value; }
}
///////////////////////////////////////////////////////////////////////
private string indexString;
///
/// String used to help identify the selected index. This value will
/// later be provided to the
/// method.
///
public string IndexString
{
get { return indexString; }
set { indexString = value; }
}
///////////////////////////////////////////////////////////////////////
private int needToFreeIndexString;
///
/// Non-zero if the index string must be freed by the SQLite core
/// library.
///
public int NeedToFreeIndexString
{
get { return needToFreeIndexString; }
set { needToFreeIndexString = value; }
}
///////////////////////////////////////////////////////////////////////
private int orderByConsumed;
///
/// True if output is already ordered.
///
public int OrderByConsumed
{
get { return orderByConsumed; }
set { orderByConsumed = value; }
}
///////////////////////////////////////////////////////////////////////
private double? estimatedCost;
///
/// Estimated cost of using this index. Using a null value here
/// indicates that a default estimated cost value should be used.
///
public double? EstimatedCost
{
get { return estimatedCost; }
set { estimatedCost = value; }
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteIndex Helper Class
///
/// This class represents the various inputs and outputs used with the
/// method.
///
public sealed class SQLiteIndex
{
#region Private Constants
///
/// The default estimated cost for use with the
/// method.
///
internal static readonly double DefaultEstimatedCost = double.MaxValue;
#endregion
///////////////////////////////////////////////////////////////////////
#region Internal Constructors
///
/// Constructs an instance of this class.
///
///
/// The number of (and
/// ) instances to
/// pre-allocate space for.
///
///
/// The number of instances to
/// pre-allocate space for.
///
internal SQLiteIndex(
int nConstraint,
int nOrderBy
)
{
inputs = new SQLiteIndexInputs(nConstraint, nOrderBy);
outputs = new SQLiteIndexOutputs(nConstraint);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Internal Marshal Helper Methods
///
/// Converts a native pointer to a native sqlite3_index_info structure
/// into a new object instance.
///
///
/// The native pointer to the native sqlite3_index_info structure to
/// convert.
///
///
/// Upon success, this parameter will be modified to contain the newly
/// created object instance.
///
internal static void FromIntPtr(
IntPtr pIndex,
ref SQLiteIndex index
)
{
if (pIndex == IntPtr.Zero)
return;
int offset = 0;
int nConstraint = SQLiteMarshal.ReadInt32(pIndex, offset);
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
IntPtr.Size);
IntPtr pConstraint = SQLiteMarshal.ReadIntPtr(pIndex, offset);
offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
sizeof(int));
int nOrderBy = SQLiteMarshal.ReadInt32(pIndex, offset);
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
IntPtr.Size);
IntPtr pOrderBy = SQLiteMarshal.ReadIntPtr(pIndex, offset);
index = new SQLiteIndex(nConstraint, nOrderBy);
Type indexConstraintType = typeof(
UnsafeNativeMethods.sqlite3_index_constraint);
int sizeOfConstraintType = Marshal.SizeOf(indexConstraintType);
for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
{
IntPtr pOffset = SQLiteMarshal.IntPtrForOffset(
pConstraint, iConstraint * sizeOfConstraintType);
UnsafeNativeMethods.sqlite3_index_constraint constraint =
(UnsafeNativeMethods.sqlite3_index_constraint)
Marshal.PtrToStructure(pOffset, indexConstraintType);
index.Inputs.Constraints[iConstraint] =
new SQLiteIndexConstraint(constraint);
}
Type indexOrderByType = typeof(
UnsafeNativeMethods.sqlite3_index_orderby);
int sizeOfOrderByType = Marshal.SizeOf(indexOrderByType);
for (int iOrderBy = 0; iOrderBy < nOrderBy; iOrderBy++)
{
IntPtr pOffset = SQLiteMarshal.IntPtrForOffset(
pOrderBy, iOrderBy * sizeOfOrderByType);
UnsafeNativeMethods.sqlite3_index_orderby orderBy =
(UnsafeNativeMethods.sqlite3_index_orderby)
Marshal.PtrToStructure(pOffset, indexOrderByType);
index.Inputs.OrderBys[iOrderBy] =
new SQLiteIndexOrderBy(orderBy);
}
}
///////////////////////////////////////////////////////////////////////
///
/// Populates the outputs of a pre-allocated native sqlite3_index_info
/// structure using an existing object
/// instance.
///
///
/// The existing object instance containing
/// the output data to use.
///
///
/// The native pointer to the pre-allocated native sqlite3_index_info
/// structure.
///
internal static void ToIntPtr(
SQLiteIndex index,
IntPtr pIndex
)
{
if ((index == null) || (index.Inputs == null) ||
(index.Inputs.Constraints == null) ||
(index.Outputs == null) ||
(index.Outputs.ConstraintUsages == null))
{
return;
}
if (pIndex == IntPtr.Zero)
return;
int offset = 0;
int nConstraint = SQLiteMarshal.ReadInt32(pIndex, offset);
if (nConstraint != index.Inputs.Constraints.Length)
return;
if (nConstraint != index.Outputs.ConstraintUsages.Length)
return;
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
IntPtr.Size);
offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
sizeof(int));
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
IntPtr.Size);
offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
sizeof(int));
IntPtr pConstraintUsage = SQLiteMarshal.ReadIntPtr(pIndex, offset);
int sizeOfConstraintUsageType = Marshal.SizeOf(typeof(
UnsafeNativeMethods.sqlite3_index_constraint_usage));
for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
{
UnsafeNativeMethods.sqlite3_index_constraint_usage constraintUsage =
new UnsafeNativeMethods.sqlite3_index_constraint_usage(
index.Outputs.ConstraintUsages[iConstraint]);
Marshal.StructureToPtr(
constraintUsage, SQLiteMarshal.IntPtrForOffset(
pConstraintUsage, iConstraint * sizeOfConstraintUsageType),
false);
}
offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
sizeof(int));
SQLiteMarshal.WriteInt32(pIndex, offset,
index.Outputs.IndexNumber);
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
IntPtr.Size);
SQLiteMarshal.WriteIntPtr(pIndex, offset,
SQLiteString.Utf8IntPtrFromString(index.Outputs.IndexString));
offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
sizeof(int));
//
// NOTE: We just allocated the IndexString field; therefore, we
// need to set the NeedToFreeIndexString field to non-zero.
//
SQLiteMarshal.WriteInt32(pIndex, offset, 1);
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
sizeof(int));
SQLiteMarshal.WriteInt32(pIndex, offset,
index.Outputs.OrderByConsumed);
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
sizeof(double));
SQLiteMarshal.WriteDouble(pIndex, offset,
index.Outputs.EstimatedCost.GetValueOrDefault());
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
private SQLiteIndexInputs inputs;
///
/// The object instance containing
/// the inputs to the
/// method.
///
public SQLiteIndexInputs Inputs
{
get { return inputs; }
}
///////////////////////////////////////////////////////////////////////
private SQLiteIndexOutputs outputs;
///
/// The object instance containing
/// the outputs from the
/// method.
///
public SQLiteIndexOutputs Outputs
{
get { return outputs; }
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteVirtualTable Base Class
///
/// This class represents a managed virtual table implementation. It is
/// not sealed and should be used as the base class for any user-defined
/// virtual table classes implemented in managed code.
///
public class SQLiteVirtualTable :
ISQLiteNativeHandle, IDisposable /* NOT SEALED */
{
#region Private Constants
///
/// The index within the array of strings provided to the
/// and
/// methods containing the
/// name of the module implementing this virtual table.
///
private const int ModuleNameIndex = 0;
///////////////////////////////////////////////////////////////////////
///
/// The index within the array of strings provided to the
/// and
/// methods containing the
/// name of the database containing this virtual table.
///
private const int DatabaseNameIndex = 1;
///////////////////////////////////////////////////////////////////////
///
/// The index within the array of strings provided to the
/// and
/// methods containing the
/// name of the virtual table.
///
private const int TableNameIndex = 2;
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Constructors
///
/// Constructs an instance of this class.
///
///
/// The original array of strings provided to the
/// and
/// methods.
///
public SQLiteVirtualTable(
string[] arguments
)
{
this.arguments = arguments;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
private string[] arguments;
///
/// The original array of strings provided to the
/// and
/// methods.
///
public virtual string[] Arguments
{
get { CheckDisposed(); return arguments; }
}
///////////////////////////////////////////////////////////////////////
///
/// The name of the module implementing this virtual table.
///
public virtual string ModuleName
{
get
{
CheckDisposed();
string[] arguments = Arguments;
if ((arguments != null) &&
(arguments.Length > ModuleNameIndex))
{
return arguments[ModuleNameIndex];
}
else
{
return null;
}
}
}
///////////////////////////////////////////////////////////////////////
///
/// The name of the database containing this virtual table.
///
public virtual string DatabaseName
{
get
{
CheckDisposed();
string[] arguments = Arguments;
if ((arguments != null) &&
(arguments.Length > DatabaseNameIndex))
{
return arguments[DatabaseNameIndex];
}
else
{
return null;
}
}
}
///////////////////////////////////////////////////////////////////////
///
/// The name of the virtual table.
///
public virtual string TableName
{
get
{
CheckDisposed();
string[] arguments = Arguments;
if ((arguments != null) &&
(arguments.Length > TableNameIndex))
{
return arguments[TableNameIndex];
}
else
{
return null;
}
}
}
///////////////////////////////////////////////////////////////////////
private SQLiteIndex index;
///
/// The object instance containing all the
/// data for the inputs and outputs relating to the most recent index
/// selection.
///
public virtual SQLiteIndex Index
{
get { CheckDisposed(); return index; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Methods
///
/// This method should normally be used by the
/// method in order to
/// perform index selection based on the constraints provided by the
/// SQLite core library.
///
///
/// The object instance containing all the
/// data for the inputs and outputs relating to index selection.
///
///
/// Non-zero upon success.
///
public virtual bool BestIndex(
SQLiteIndex index
)
{
CheckDisposed();
this.index = index;
return true;
}
///////////////////////////////////////////////////////////////////////
///
/// Attempts to record the renaming of the virtual table associated
/// with this object instance.
///
///
/// The new name for the virtual table.
///
///
/// Non-zero upon success.
///
public virtual bool Rename(
string name
)
{
CheckDisposed();
if ((arguments != null) &&
(arguments.Length > TableNameIndex))
{
arguments[TableNameIndex] = name;
return true;
}
return false;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region ISQLiteNativeHandle Members
private IntPtr nativeHandle;
///
/// Returns the underlying SQLite native handle associated with this
/// object instance.
///
public virtual IntPtr NativeHandle
{
get { CheckDisposed(); return nativeHandle; }
internal set { nativeHandle = value; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region IDisposable Members
///
/// Disposes of this object instance.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region IDisposable "Pattern" Members
private bool disposed;
///
/// Throws an if this object
/// instance has been disposed.
///
private void CheckDisposed() /* throw */
{
#if THROW_ON_DISPOSED
if (disposed)
{
throw new ObjectDisposedException(
typeof(SQLiteVirtualTable).Name);
}
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Disposes of this object instance.
///
///
/// Non-zero if this method is being called from the
/// method. Zero if this method is being called
/// from the finalizer.
///
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
//if (disposing)
//{
// ////////////////////////////////////
// // dispose managed resources here...
// ////////////////////////////////////
//}
//////////////////////////////////////
// release unmanaged resources here...
//////////////////////////////////////
disposed = true;
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Destructor
///
/// Finalizes this object instance.
///
~SQLiteVirtualTable()
{
Dispose(false);
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteVirtualTableCursor Base Class
///
/// This class represents a managed virtual table cursor implementation.
/// It is not sealed and should be used as the base class for any
/// user-defined virtual table cursor classes implemented in managed code.
///
public class SQLiteVirtualTableCursor :
ISQLiteNativeHandle, IDisposable /* NOT SEALED */
{
#region Protected Constants
///
/// This value represents an invalid integer row sequence number.
///
protected static readonly int InvalidRowIndex = 0;
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Data
///
/// The field holds the integer row sequence number for the current row
/// pointed to by this cursor object instance.
///
private int rowIndex;
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Constructors
///
/// Constructs an instance of this class.
///
///
/// The object instance associated
/// with this object instance.
///
public SQLiteVirtualTableCursor(
SQLiteVirtualTable table
)
: this()
{
this.table = table;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Constructors
///
/// Constructs an instance of this class.
///
private SQLiteVirtualTableCursor()
{
rowIndex = InvalidRowIndex;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
private SQLiteVirtualTable table;
///
/// The object instance associated
/// with this object instance.
///
public virtual SQLiteVirtualTable Table
{
get { CheckDisposed(); return table; }
}
///////////////////////////////////////////////////////////////////////
private int indexNumber;
///
/// Number used to help identify the selected index. This value will
/// be set via the method.
///
public virtual int IndexNumber
{
get { CheckDisposed(); return indexNumber; }
}
///////////////////////////////////////////////////////////////////////
private string indexString;
///
/// String used to help identify the selected index. This value will
/// be set via the method.
///
public virtual string IndexString
{
get { CheckDisposed(); return indexString; }
}
///////////////////////////////////////////////////////////////////////
private SQLiteValue[] values;
///
/// The values used to filter the rows returned via this cursor object
/// instance. This value will be set via the
/// method.
///
public virtual SQLiteValue[] Values
{
get { CheckDisposed(); return values; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Protected Methods
///
/// Attempts to persist the specified object
/// instances in order to make them available after the
/// method returns.
///
///
/// The array of object instances to be
/// persisted.
///
///
/// The number of object instances that were
/// successfully persisted.
///
protected virtual int TryPersistValues(
SQLiteValue[] values
)
{
int result = 0;
if (values != null)
{
foreach (SQLiteValue value in values)
{
if (value == null)
continue;
if (value.Persist())
result++;
}
}
return result;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Methods
///
/// This method should normally be used by the
/// method in order to
/// perform filtering of the result rows and/or to record the filtering
/// criteria provided by the SQLite core library.
///
///
/// Number used to help identify the selected index.
///
///
/// String used to help identify the selected index.
///
///
/// The values corresponding to each column in the selected index.
///
public virtual void Filter(
int indexNumber,
string indexString,
SQLiteValue[] values
)
{
CheckDisposed();
if ((values != null) &&
(TryPersistValues(values) != values.Length))
{
throw new SQLiteException(
"failed to persist one or more values");
}
this.indexNumber = indexNumber;
this.indexString = indexString;
this.values = values;
}
///////////////////////////////////////////////////////////////////////
///
/// Determines the integer row sequence number for the current row.
///
///
/// The integer row sequence number for the current row -OR- zero if
/// it cannot be determined.
///
public virtual int GetRowIndex()
{
return rowIndex;
}
///////////////////////////////////////////////////////////////////////
///
/// Adjusts the integer row sequence number so that it refers to the
/// next row.
///
public virtual void NextRowIndex()
{
rowIndex++;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region ISQLiteNativeHandle Members
private IntPtr nativeHandle;
///
/// Returns the underlying SQLite native handle associated with this
/// object instance.
///
public virtual IntPtr NativeHandle
{
get { CheckDisposed(); return nativeHandle; }
internal set { nativeHandle = value; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region IDisposable Members
///
/// Disposes of this object instance.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region IDisposable "Pattern" Members
private bool disposed;
///
/// Throws an if this object
/// instance has been disposed.
///
private void CheckDisposed() /* throw */
{
#if THROW_ON_DISPOSED
if (disposed)
{
throw new ObjectDisposedException(
typeof(SQLiteVirtualTableCursor).Name);
}
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Disposes of this object instance.
///
///
/// Non-zero if this method is being called from the
/// method. Zero if this method is being called
/// from the finalizer.
///
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
//if (disposing)
//{
// ////////////////////////////////////
// // dispose managed resources here...
// ////////////////////////////////////
//}
//////////////////////////////////////
// release unmanaged resources here...
//////////////////////////////////////
disposed = true;
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Destructor
///
/// Finalizes this object instance.
///
~SQLiteVirtualTableCursor()
{
Dispose(false);
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region ISQLiteNativeHandle Interface
///
/// This interface represents a native handle provided by the SQLite core
/// library.
///
public interface ISQLiteNativeHandle
{
///
/// The native handle value.
///
IntPtr NativeHandle { get; }
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region ISQLiteNativeModule Interface
///
/// This interface represents a virtual table implementation written in
/// native code.
///
public interface ISQLiteNativeModule
{
///
///
/// This method is called to create a new instance of a virtual table
/// in response to a CREATE VIRTUAL TABLE statement. The db parameter
/// is a pointer to the SQLite database connection that is executing
/// the CREATE VIRTUAL TABLE statement. The pAux argument is the copy
/// of the client data pointer that was the fourth argument to the
/// sqlite3_create_module() or sqlite3_create_module_v2() call that
/// registered the virtual table module. The argv parameter is an
/// array of argc pointers to null terminated strings. The first
/// string, argv[0], is the name of the module being invoked. The
/// module name is the name provided as the second argument to
/// sqlite3_create_module() and as the argument to the USING clause of
/// the CREATE VIRTUAL TABLE statement that is running. The second,
/// argv[1], is the name of the database in which the new virtual table
/// is being created. The database name is "main" for the primary
/// database, or "temp" for TEMP database, or the name given at the
/// end of the ATTACH statement for attached databases. The third
/// element of the array, argv[2], is the name of the new virtual
/// table, as specified following the TABLE keyword in the CREATE
/// VIRTUAL TABLE statement. If present, the fourth and subsequent
/// strings in the argv[] array report the arguments to the module name
/// in the CREATE VIRTUAL TABLE statement.
///
///
/// The job of this method is to construct the new virtual table object
/// (an sqlite3_vtab object) and return a pointer to it in *ppVTab.
///
///
/// As part of the task of creating a new sqlite3_vtab structure, this
/// method must invoke sqlite3_declare_vtab() to tell the SQLite core
/// about the columns and datatypes in the virtual table. The
/// sqlite3_declare_vtab() API has the following prototype:
///
///
///
/// int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)
///
///
///
/// The first argument to sqlite3_declare_vtab() must be the same
/// database connection pointer as the first parameter to this method.
/// The second argument to sqlite3_declare_vtab() must a
/// zero-terminated UTF-8 string that contains a well-formed CREATE
/// TABLE statement that defines the columns in the virtual table and
/// their data types. The name of the table in this CREATE TABLE
/// statement is ignored, as are all constraints. Only the column names
/// and datatypes matter. The CREATE TABLE statement string need not to
/// be held in persistent memory. The string can be deallocated and/or
/// reused as soon as the sqlite3_declare_vtab() routine returns.
///
///
///
/// The native database connection handle.
///
///
/// The original native pointer value that was provided to the
/// sqlite3_create_module(), sqlite3_create_module_v2() or
/// sqlite3_create_disposable_module() functions.
///
///
/// The number of arguments from the CREATE VIRTUAL TABLE statement.
///
///
/// The array of string arguments from the CREATE VIRTUAL TABLE
/// statement.
///
///
/// Upon success, this parameter must be modified to point to the newly
/// created native sqlite3_vtab derived structure.
///
///
/// Upon failure, this parameter must be modified to point to the error
/// message, with the underlying memory having been obtained from the
/// sqlite3_malloc() function.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xCreate(
IntPtr pDb,
IntPtr pAux,
int argc,
IntPtr argv,
ref IntPtr pVtab,
ref IntPtr pError
);
///////////////////////////////////////////////////////////////////////
///
///
/// The xConnect method is very similar to xCreate. It has the same
/// parameters and constructs a new sqlite3_vtab structure just like
/// xCreate. And it must also call sqlite3_declare_vtab() like xCreate.
///
///
/// The difference is that xConnect is called to establish a new
/// connection to an existing virtual table whereas xCreate is called
/// to create a new virtual table from scratch.
///
///
/// The xCreate and xConnect methods are only different when the
/// virtual table has some kind of backing store that must be
/// initialized the first time the virtual table is created. The
/// xCreate method creates and initializes the backing store. The
/// xConnect method just connects to an existing backing store.
///
///
/// As an example, consider a virtual table implementation that
/// provides read-only access to existing comma-separated-value (CSV)
/// files on disk. There is no backing store that needs to be created
/// or initialized for such a virtual table (since the CSV files
/// already exist on disk) so the xCreate and xConnect methods will be
/// identical for that module.
///
///
/// Another example is a virtual table that implements a full-text
/// index. The xCreate method must create and initialize data
/// structures to hold the dictionary and posting lists for that index.
/// The xConnect method, on the other hand, only has to locate and use
/// an existing dictionary and posting lists that were created by a
/// prior xCreate call.
///
///
/// The xConnect method must return SQLITE_OK if it is successful in
/// creating the new virtual table, or SQLITE_ERROR if it is not
/// successful. If not successful, the sqlite3_vtab structure must not
/// be allocated. An error message may optionally be returned in *pzErr
/// if unsuccessful. Space to hold the error message string must be
/// allocated using an SQLite memory allocation function like
/// sqlite3_malloc() or sqlite3_mprintf() as the SQLite core will
/// attempt to free the space using sqlite3_free() after the error has
/// been reported up to the application.
///
///
/// The xConnect method is required for every virtual table
/// implementation, though the xCreate and xConnect pointers of the
/// sqlite3_module object may point to the same function the virtual
/// table does not need to initialize backing store.
///
///
///
/// The native database connection handle.
///
///
/// The original native pointer value that was provided to the
/// sqlite3_create_module(), sqlite3_create_module_v2() or
/// sqlite3_create_disposable_module() functions.
///
///
/// The number of arguments from the CREATE VIRTUAL TABLE statement.
///
///
/// The array of string arguments from the CREATE VIRTUAL TABLE
/// statement.
///
///
/// Upon success, this parameter must be modified to point to the newly
/// created native sqlite3_vtab derived structure.
///
///
/// Upon failure, this parameter must be modified to point to the error
/// message, with the underlying memory having been obtained from the
/// sqlite3_malloc() function.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xConnect(
IntPtr pDb,
IntPtr pAux,
int argc,
IntPtr argv,
ref IntPtr pVtab,
ref IntPtr pError
);
///////////////////////////////////////////////////////////////////////
///
///
/// SQLite uses the xBestIndex method of a virtual table module to
/// determine the best way to access the virtual table. The xBestIndex
/// method has a prototype like this:
///
///
/// int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
///
///
/// The SQLite core communicates with the xBestIndex method by filling
/// in certain fields of the sqlite3_index_info structure and passing a
/// pointer to that structure into xBestIndex as the second parameter.
/// The xBestIndex method fills out other fields of this structure
/// which forms the reply. The sqlite3_index_info structure looks like
/// this:
///
///
/// struct sqlite3_index_info {
/// /* Inputs */
/// const int nConstraint; /* Number of entries in aConstraint */
/// const struct sqlite3_index_constraint {
/// int iColumn; /* Column on left-hand side of
/// * constraint */
/// unsigned char op; /* Constraint operator */
/// unsigned char usable; /* True if this constraint is usable */
/// int iTermOffset; /* Used internally - xBestIndex should
/// * ignore */
/// } *const aConstraint; /* Table of WHERE clause constraints */
/// const int nOrderBy; /* Number of terms in the ORDER BY
/// * clause */
/// const struct sqlite3_index_orderby {
/// int iColumn; /* Column number */
/// unsigned char desc; /* True for DESC. False for ASC. */
/// } *const aOrderBy; /* The ORDER BY clause */
/// /* Outputs */
/// struct sqlite3_index_constraint_usage {
/// int argvIndex; /* if greater than zero, constraint is
/// * part of argv to xFilter */
/// unsigned char omit; /* Do not code a test for this
/// * constraint */
/// } *const aConstraintUsage;
/// int idxNum; /* Number used to identify the index */
/// char *idxStr; /* String, possibly obtained from
/// * sqlite3_malloc() */
/// int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if
/// * true */
/// int orderByConsumed; /* True if output is already ordered */
/// double estimatedCost; /* Estimated cost of using this index */
/// };
///
///
/// In addition, there are some defined constants:
///
///
/// #define SQLITE_INDEX_CONSTRAINT_EQ 2
/// #define SQLITE_INDEX_CONSTRAINT_GT 4
/// #define SQLITE_INDEX_CONSTRAINT_LE 8
/// #define SQLITE_INDEX_CONSTRAINT_LT 16
/// #define SQLITE_INDEX_CONSTRAINT_GE 32
/// #define SQLITE_INDEX_CONSTRAINT_MATCH 64
///
///
/// The SQLite core calls the xBestIndex method when it is compiling a
/// query that involves a virtual table. In other words, SQLite calls
/// this method when it is running sqlite3_prepare() or the equivalent.
/// By calling this method, the SQLite core is saying to the virtual
/// table that it needs to access some subset of the rows in the
/// virtual table and it wants to know the most efficient way to do
/// that access. The xBestIndex method replies with information that
/// the SQLite core can then use to conduct an efficient search of the
/// virtual table.
///
///
/// While compiling a single SQL query, the SQLite core might call
/// xBestIndex multiple times with different settings in
/// sqlite3_index_info. The SQLite core will then select the
/// combination that appears to give the best performance.
///
///
/// Before calling this method, the SQLite core initializes an instance
/// of the sqlite3_index_info structure with information about the
/// query that it is currently trying to process. This information
/// derives mainly from the WHERE clause and ORDER BY or GROUP BY
/// clauses of the query, but also from any ON or USING clauses if the
/// query is a join. The information that the SQLite core provides to
/// the xBestIndex method is held in the part of the structure that is
/// marked as "Inputs". The "Outputs" section is initialized to zero.
///
///
/// The information in the sqlite3_index_info structure is ephemeral
/// and may be overwritten or deallocated as soon as the xBestIndex
/// method returns. If the xBestIndex method needs to remember any part
/// of the sqlite3_index_info structure, it should make a copy. Care
/// must be take to store the copy in a place where it will be
/// deallocated, such as in the idxStr field with needToFreeIdxStr set
/// to 1.
///
///
/// Note that xBestIndex will always be called before xFilter, since
/// the idxNum and idxStr outputs from xBestIndex are required inputs
/// to xFilter. However, there is no guarantee that xFilter will be
/// called following a successful xBestIndex.
///
///
/// The xBestIndex method is required for every virtual table
/// implementation.
///
///
/// 2.3.1 Inputs
///
///
/// The main thing that the SQLite core is trying to communicate to the
/// virtual table is the constraints that are available to limit the
/// number of rows that need to be searched. The aConstraint[] array
/// contains one entry for each constraint. There will be exactly
/// nConstraint entries in that array.
///
///
/// Each constraint will correspond to a term in the WHERE clause or in
/// a USING or ON clause that is of the form
///
///
/// column OP EXPR
///
///
/// Where "column" is a column in the virtual table, OP is an operator
/// like "=" or "<", and EXPR is an arbitrary expression. So, for
/// example, if the WHERE clause contained a term like this:
///
///
/// a = 5
///
///
/// Then one of the constraints would be on the "a" column with
/// operator "=" and an expression of "5". Constraints need not have a
/// literal representation of the WHERE clause. The query optimizer
/// might make transformations to the WHERE clause in order to extract
/// as many constraints as it can. So, for example, if the WHERE clause
/// contained something like this:
///
///
/// x BETWEEN 10 AND 100 AND 999>y
///
///
/// The query optimizer might translate this into three separate
/// constraints:
///
///
/// x >= 10
/// x <= 100
/// y < 999
///
///
/// For each constraint, the aConstraint[].iColumn field indicates
/// which column appears on the left-hand side of the constraint. The
/// first column of the virtual table is column 0. The rowid of the
/// virtual table is column -1. The aConstraint[].op field indicates
/// which operator is used. The SQLITE_INDEX_CONSTRAINT_* constants map
/// integer constants into operator values. Columns occur in the order
/// they were defined by the call to sqlite3_declare_vtab() in the
/// xCreate or xConnect method. Hidden columns are counted when
/// determining the column index.
///
///
/// The aConstraint[] array contains information about all constraints
/// that apply to the virtual table. But some of the constraints might
/// not be usable because of the way tables are ordered in a join. The
/// xBestIndex method must therefore only consider constraints that
/// have an aConstraint[].usable flag which is true.
///
///
/// In addition to WHERE clause constraints, the SQLite core also tells
/// the xBestIndex method about the ORDER BY clause. (In an aggregate
/// query, the SQLite core might put in GROUP BY clause information in
/// place of the ORDER BY clause information, but this fact should not
/// make any difference to the xBestIndex method.) If all terms of the
/// ORDER BY clause are columns in the virtual table, then nOrderBy
/// will be the number of terms in the ORDER BY clause and the
/// aOrderBy[] array will identify the column for each term in the
/// order by clause and whether or not that column is ASC or DESC.
///
///
/// 2.3.2 Outputs
///
///
/// Given all of the information above, the job of the xBestIndex
/// method it to figure out the best way to search the virtual table.
///
///
/// The xBestIndex method fills the idxNum and idxStr fields with
/// information that communicates an indexing strategy to the xFilter
/// method. The information in idxNum and idxStr is arbitrary as far as
/// the SQLite core is concerned. The SQLite core just copies the
/// information through to the xFilter method. Any desired meaning can
/// be assigned to idxNum and idxStr as long as xBestIndex and xFilter
/// agree on what that meaning is.
///
///
/// The idxStr value may be a string obtained from an SQLite memory
/// allocation function such as sqlite3_mprintf(). If this is the case,
/// then the needToFreeIdxStr flag must be set to true so that the
/// SQLite core will know to call sqlite3_free() on that string when it
/// has finished with it, and thus avoid a memory leak.
///
///
/// If the virtual table will output rows in the order specified by the
/// ORDER BY clause, then the orderByConsumed flag may be set to true.
/// If the output is not automatically in the correct order then
/// orderByConsumed must be left in its default false setting. This
/// will indicate to the SQLite core that it will need to do a separate
/// sorting pass over the data after it comes out of the virtual table.
///
///
/// The estimatedCost field should be set to the estimated number of
/// disk access operations required to execute this query against the
/// virtual table. The SQLite core will often call xBestIndex multiple
/// times with different constraints, obtain multiple cost estimates,
/// then choose the query plan that gives the lowest estimate.
///
///
/// The aConstraintUsage[] array contains one element for each of the
/// nConstraint constraints in the inputs section of the
/// sqlite3_index_info structure. The aConstraintUsage[] array is used
/// by xBestIndex to tell the core how it is using the constraints.
///
///
/// The xBestIndex method may set aConstraintUsage[].argvIndex entries
/// to values greater than one. Exactly one entry should be set to 1,
/// another to 2, another to 3, and so forth up to as many or as few as
/// the xBestIndex method wants. The EXPR of the corresponding
/// constraints will then be passed in as the argv[] parameters to
/// xFilter.
///
///
/// For example, if the aConstraint[3].argvIndex is set to 1, then when
/// xFilter is called, the argv[0] passed to xFilter will have the EXPR
/// value of the aConstraint[3] constraint.
///
///
/// By default, the SQLite core double checks all constraints on each
/// row of the virtual table that it receives. If such a check is
/// redundant, the xBestFilter method can suppress that double-check by
/// setting aConstraintUsage[].omit.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The native pointer to the sqlite3_index_info structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xBestIndex(
IntPtr pVtab,
IntPtr pIndex
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method releases a connection to a virtual table. Only the
/// sqlite3_vtab object is destroyed. The virtual table is not
/// destroyed and any backing store associated with the virtual table
/// persists. This method undoes the work of xConnect.
///
///
/// This method is a destructor for a connection to the virtual table.
/// Contrast this method with xDestroy. The xDestroy is a destructor
/// for the entire virtual table.
///
///
/// The xDisconnect method is required for every virtual table
/// implementation, though it is acceptable for the xDisconnect and
/// xDestroy methods to be the same function if that makes sense for
/// the particular virtual table.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xDisconnect(
IntPtr pVtab
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method releases a connection to a virtual table, just like the
/// xDisconnect method, and it also destroys the underlying table
/// implementation. This method undoes the work of xCreate.
///
///
/// The xDisconnect method is called whenever a database connection
/// that uses a virtual table is closed. The xDestroy method is only
/// called when a DROP TABLE statement is executed against the virtual
/// table.
///
///
/// The xDestroy method is required for every virtual table
/// implementation, though it is acceptable for the xDisconnect and
/// xDestroy methods to be the same function if that makes sense for
/// the particular virtual table.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xDestroy(
IntPtr pVtab
);
///////////////////////////////////////////////////////////////////////
///
///
/// The xOpen method creates a new cursor used for accessing (read
/// and/or writing) a virtual table. A successful invocation of this
/// method will allocate the memory for the sqlite3_vtab_cursor (or a
/// subclass), initialize the new object, and make *ppCursor point to
/// the new object. The successful call then returns SQLITE_OK.
///
///
/// For every successful call to this method, the SQLite core will
/// later invoke the xClose method to destroy the allocated cursor.
///
///
/// The xOpen method need not initialize the pVtab field of the
/// sqlite3_vtab_cursor structure. The SQLite core will take care of
/// that chore automatically.
///
///
/// A virtual table implementation must be able to support an arbitrary
/// number of simultaneously open cursors.
///
///
/// When initially opened, the cursor is in an undefined state. The
/// SQLite core will invoke the xFilter method on the cursor prior to
/// any attempt to position or read from the cursor.
///
///
/// The xOpen method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// Upon success, this parameter must be modified to point to the newly
/// created native sqlite3_vtab_cursor derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xOpen(
IntPtr pVtab,
ref IntPtr pCursor
);
///////////////////////////////////////////////////////////////////////
///
///
/// The xClose method closes a cursor previously opened by xOpen. The
/// SQLite core will always call xClose once for each cursor opened
/// using xOpen.
///
///
/// This method must release all resources allocated by the
/// corresponding xOpen call. The routine will not be called again even
/// if it returns an error. The SQLite core will not use the
/// sqlite3_vtab_cursor again after it has been closed.
///
///
/// The xClose method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xClose(
IntPtr pCursor
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method begins a search of a virtual table. The first argument
/// is a cursor opened by xOpen. The next two argument define a
/// particular search index previously chosen by xBestIndex. The
/// specific meanings of idxNum and idxStr are unimportant as long as
/// xFilter and xBestIndex agree on what that meaning is.
///
///
/// The xBestIndex function may have requested the values of certain
/// expressions using the aConstraintUsage[].argvIndex values of the
/// sqlite3_index_info structure. Those values are passed to xFilter
/// using the argc and argv parameters.
///
///
/// If the virtual table contains one or more rows that match the
/// search criteria, then the cursor must be left point at the first
/// row. Subsequent calls to xEof must return false (zero). If there
/// are no rows match, then the cursor must be left in a state that
/// will cause the xEof to return true (non-zero). The SQLite engine
/// will use the xColumn and xRowid methods to access that row content.
/// The xNext method will be used to advance to the next row.
///
///
/// This method must return SQLITE_OK if successful, or an sqlite error
/// code if an error occurs.
///
///
/// The xFilter method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure.
///
///
/// Number used to help identify the selected index.
///
///
/// The native pointer to the UTF-8 encoded string containing the
/// string used to help identify the selected index.
///
///
/// The number of native pointers to sqlite3_value structures specified
/// in .
///
///
/// An array of native pointers to sqlite3_value structures containing
/// filtering criteria for the selected index.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xFilter(
IntPtr pCursor,
int idxNum,
IntPtr idxStr,
int argc,
IntPtr argv
);
///////////////////////////////////////////////////////////////////////
///
///
/// The xNext method advances a virtual table cursor to the next row of
/// a result set initiated by xFilter. If the cursor is already
/// pointing at the last row when this routine is called, then the
/// cursor no longer points to valid data and a subsequent call to the
/// xEof method must return true (non-zero). If the cursor is
/// successfully advanced to another row of content, then subsequent
/// calls to xEof must return false (zero).
///
///
/// This method must return SQLITE_OK if successful, or an sqlite error
/// code if an error occurs.
///
///
/// The xNext method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xNext(
IntPtr pCursor
);
///////////////////////////////////////////////////////////////////////
///
///
/// The xEof method must return false (zero) if the specified cursor
/// currently points to a valid row of data, or true (non-zero)
/// otherwise. This method is called by the SQL engine immediately
/// after each xFilter and xNext invocation.
///
///
/// The xEof method is required for every virtual table implementation.
///
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure.
///
///
/// Non-zero if no more rows are available; zero otherwise.
///
int xEof(
IntPtr pCursor
);
///////////////////////////////////////////////////////////////////////
///
///
/// The SQLite core invokes this method in order to find the value for
/// the N-th column of the current row. N is zero-based so the first
/// column is numbered 0. The xColumn method may return its result back
/// to SQLite using one of the following interface:
///
///
/// sqlite3_result_blob()
/// sqlite3_result_double()
/// sqlite3_result_int()
/// sqlite3_result_int64()
/// sqlite3_result_null()
/// sqlite3_result_text()
/// sqlite3_result_text16()
/// sqlite3_result_text16le()
/// sqlite3_result_text16be()
/// sqlite3_result_zeroblob()
///
///
/// If the xColumn method implementation calls none of the functions
/// above, then the value of the column defaults to an SQL NULL.
///
///
/// To raise an error, the xColumn method should use one of the
/// result_text() methods to set the error message text, then return an
/// appropriate error code. The xColumn method must return SQLITE_OK on
/// success.
///
///
/// The xColumn method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure.
///
///
/// The native pointer to the sqlite3_context structure to be used
/// for returning the specified column value to the SQLite core
/// library.
///
///
/// The zero-based index corresponding to the column containing the
/// value to be returned.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xColumn(
IntPtr pCursor,
IntPtr pContext,
int index
);
///////////////////////////////////////////////////////////////////////
///
///
/// A successful invocation of this method will cause *pRowid to be
/// filled with the rowid of row that the virtual table cursor pCur is
/// currently pointing at. This method returns SQLITE_OK on success. It
/// returns an appropriate error code on failure.
///
///
/// The xRowid method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure.
///
///
/// Upon success, this parameter must be modified to contain the unique
/// integer row identifier for the current row for the specified cursor.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xRowId(
IntPtr pCursor,
ref long rowId
);
///////////////////////////////////////////////////////////////////////
///
///
/// All changes to a virtual table are made using the xUpdate method.
/// This one method can be used to insert, delete, or update.
///
///
/// The argc parameter specifies the number of entries in the argv
/// array. The value of argc will be 1 for a pure delete operation or
/// N+2 for an insert or replace or update where N is the number of
/// columns in the table. In the previous sentence, N includes any
/// hidden columns.
///
///
/// Every argv entry will have a non-NULL value in C but may contain
/// the SQL value NULL. In other words, it is always true that
/// argv[i]!=0 for i between 0 and argc-1. However, it might be the
/// case that sqlite3_value_type(argv[i])==SQLITE_NULL.
///
///
/// The argv[0] parameter is the rowid of a row in the virtual table
/// to be deleted. If argv[0] is an SQL NULL, then no deletion occurs.
///
///
/// The argv[1] parameter is the rowid of a new row to be inserted into
/// the virtual table. If argv[1] is an SQL NULL, then the
/// implementation must choose a rowid for the newly inserted row.
/// Subsequent argv[] entries contain values of the columns of the
/// virtual table, in the order that the columns were declared. The
/// number of columns will match the table declaration that the
/// xConnect or xCreate method made using the sqlite3_declare_vtab()
/// call. All hidden columns are included.
///
///
/// When doing an insert without a rowid (argc>1, argv[1] is an SQL
/// NULL), the implementation must set *pRowid to the rowid of the
/// newly inserted row; this will become the value returned by the
/// sqlite3_last_insert_rowid() function. Setting this value in all the
/// other cases is a harmless no-op; the SQLite engine ignores the
/// *pRowid return value if argc==1 or argv[1] is not an SQL NULL.
///
///
/// Each call to xUpdate will fall into one of cases shown below. Note
/// that references to argv[i] mean the SQL value held within the
/// argv[i] object, not the argv[i] object itself.
///
///
/// argc = 1
///
///
/// The single row with rowid equal to argv[0] is deleted. No
/// insert occurs.
///
///
/// argc > 1
/// argv[0] = NULL
///
///
/// A new row is inserted with a rowid argv[1] and column
/// values in argv[2] and following. If argv[1] is an SQL NULL,
/// the a new unique rowid is generated automatically.
///
///
/// argc > 1
/// argv[0] ? NULL
/// argv[0] = argv[1]
///
///
/// The row with rowid argv[0] is updated with new values in
/// argv[2] and following parameters.
///
///
/// argc > 1
/// argv[0] ? NULL
/// argv[0] ? argv[1]
///
///
/// The row with rowid argv[0] is updated with rowid argv[1]
/// and new values in argv[2] and following parameters. This
/// will occur when an SQL statement updates a rowid, as in
/// the statement:
///
///
/// UPDATE table SET rowid=rowid+1 WHERE ...;
///
///
/// The xUpdate method must return SQLITE_OK if and only if it is
/// successful. If a failure occurs, the xUpdate must return an
/// appropriate error code. On a failure, the pVTab->zErrMsg element
/// may optionally be replaced with error message text stored in memory
/// allocated from SQLite using functions such as sqlite3_mprintf() or
/// sqlite3_malloc().
///
///
/// If the xUpdate method violates some constraint of the virtual table
/// (including, but not limited to, attempting to store a value of the
/// wrong datatype, attempting to store a value that is too large or
/// too small, or attempting to change a read-only value) then the
/// xUpdate must fail with an appropriate error code.
///
///
/// There might be one or more sqlite3_vtab_cursor objects open and in
/// use on the virtual table instance and perhaps even on the row of
/// the virtual table when the xUpdate method is invoked. The
/// implementation of xUpdate must be prepared for attempts to delete
/// or modify rows of the table out from other existing cursors. If the
/// virtual table cannot accommodate such changes, the xUpdate method
/// must return an error code.
///
///
/// The xUpdate method is optional. If the xUpdate pointer in the
/// sqlite3_module for a virtual table is a NULL pointer, then the
/// virtual table is read-only.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The number of new or modified column values contained in
/// .
///
///
/// The array of native pointers to sqlite3_value structures containing
/// the new or modified column values, if any.
///
///
/// Upon success, this parameter must be modified to contain the unique
/// integer row identifier for the row that was inserted, if any.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xUpdate(
IntPtr pVtab,
int argc,
IntPtr argv,
ref long rowId
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method begins a transaction on a virtual table. This is method
/// is optional. The xBegin pointer of sqlite3_module may be NULL.
///
///
/// This method is always followed by one call to either the xCommit or
/// xRollback method. Virtual table transactions do not nest, so the
/// xBegin method will not be invoked more than once on a single
/// virtual table without an intervening call to either xCommit or
/// xRollback. Multiple calls to other methods can and likely will
/// occur in between the xBegin and the corresponding xCommit or
/// xRollback.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xBegin(
IntPtr pVtab
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method signals the start of a two-phase commit on a virtual
/// table. This is method is optional. The xSync pointer of
/// sqlite3_module may be NULL.
///
///
/// This method is only invoked after call to the xBegin method and
/// prior to an xCommit or xRollback. In order to implement two-phase
/// commit, the xSync method on all virtual tables is invoked prior to
/// invoking the xCommit method on any virtual table. If any of the
/// xSync methods fail, the entire transaction is rolled back.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xSync(
IntPtr pVtab
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method causes a virtual table transaction to commit. This is
/// method is optional. The xCommit pointer of sqlite3_module may be
/// NULL.
///
///
/// A call to this method always follows a prior call to xBegin and
/// xSync.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xCommit(
IntPtr pVtab
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method causes a virtual table transaction to rollback. This is
/// method is optional. The xRollback pointer of sqlite3_module may be
/// NULL.
///
///
/// A call to this method always follows a prior call to xBegin.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xRollback(
IntPtr pVtab
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method provides notification that the virtual table
/// implementation that the virtual table will be given a new name. If
/// this method returns SQLITE_OK then SQLite renames the table. If
/// this method returns an error code then the renaming is prevented.
///
///
/// The xRename method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The number of arguments to the function being sought.
///
///
/// The name of the function being sought.
///
///
/// Upon success, this parameter must be modified to contain the
/// delegate responsible for implementing the specified function.
///
///
/// Upon success, this parameter must be modified to contain the
/// native user-data pointer associated with
/// .
///
///
/// Non-zero if the specified function was found; zero otherwise.
///
int xFindFunction(
IntPtr pVtab,
int nArg,
IntPtr zName,
ref SQLiteCallback callback,
ref IntPtr pClientData
);
///////////////////////////////////////////////////////////////////////
///
///
/// This method provides notification that the virtual table
/// implementation that the virtual table will be given a new name. If
/// this method returns SQLITE_OK then SQLite renames the table. If
/// this method returns an error code then the renaming is prevented.
///
///
/// The xRename method is required for every virtual table
/// implementation.
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The native pointer to the UTF-8 encoded string containing the new
/// name for the virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xRename(
IntPtr pVtab,
IntPtr zNew
);
///////////////////////////////////////////////////////////////////////
///
///
/// These methods provide the virtual table implementation an
/// opportunity to implement nested transactions. They are always
/// optional and will only be called in SQLite version 3.7.7 and later.
///
///
/// When xSavepoint(X,N) is invoked, that is a signal to the virtual
/// table X that it should save its current state as savepoint N. A
/// subsequent call to xRollbackTo(X,R) means that the state of the
/// virtual table should return to what it was when xSavepoint(X,R) was
/// last called. The call to xRollbackTo(X,R) will invalidate all
/// savepoints with N>R; none of the invalided savepoints will be
/// rolled back or released without first being reinitialized by a call
/// to xSavepoint(). A call to xRelease(X,M) invalidates all savepoints
/// where N>=M.
///
///
/// None of the xSavepoint(), xRelease(), or xRollbackTo() methods will
/// ever be called except in between calls to xBegin() and either
/// xCommit() or xRollback().
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// This is an integer identifier under which the the current state of
/// the virtual table should be saved.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xSavepoint(
IntPtr pVtab,
int iSavepoint
);
///////////////////////////////////////////////////////////////////////
///
///
/// These methods provide the virtual table implementation an
/// opportunity to implement nested transactions. They are always
/// optional and will only be called in SQLite version 3.7.7 and later.
///
///
/// When xSavepoint(X,N) is invoked, that is a signal to the virtual
/// table X that it should save its current state as savepoint N. A
/// subsequent call to xRollbackTo(X,R) means that the state of the
/// virtual table should return to what it was when xSavepoint(X,R) was
/// last called. The call to xRollbackTo(X,R) will invalidate all
/// savepoints with N>R; none of the invalided savepoints will be
/// rolled back or released without first being reinitialized by a call
/// to xSavepoint(). A call to xRelease(X,M) invalidates all savepoints
/// where N>=M.
///
///
/// None of the xSavepoint(), xRelease(), or xRollbackTo() methods will
/// ever be called except in between calls to xBegin() and either
/// xCommit() or xRollback().
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// This is an integer used to indicate that any saved states with an
/// identifier greater than or equal to this should be deleted by the
/// virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xRelease(
IntPtr pVtab,
int iSavepoint
);
///////////////////////////////////////////////////////////////////////
///
///
/// These methods provide the virtual table implementation an
/// opportunity to implement nested transactions. They are always
/// optional and will only be called in SQLite version 3.7.7 and later.
///
///
/// When xSavepoint(X,N) is invoked, that is a signal to the virtual
/// table X that it should save its current state as savepoint N. A
/// subsequent call to xRollbackTo(X,R) means that the state of the
/// virtual table should return to what it was when xSavepoint(X,R) was
/// last called. The call to xRollbackTo(X,R) will invalidate all
/// savepoints with N>R; none of the invalided savepoints will be
/// rolled back or released without first being reinitialized by a call
/// to xSavepoint(). A call to xRelease(X,M) invalidates all savepoints
/// where N>=M.
///
///
/// None of the xSavepoint(), xRelease(), or xRollbackTo() methods will
/// ever be called except in between calls to xBegin() and either
/// xCommit() or xRollback().
///
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// This is an integer identifier used to specify a specific saved
/// state for the virtual table for it to restore itself back to, which
/// should also have the effect of deleting all saved states with an
/// integer identifier greater than this one.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode xRollbackTo(
IntPtr pVtab,
int iSavepoint
);
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region ISQLiteManagedModule Interface
///
/// This interface represents a virtual table implementation written in
/// managed code.
///
public interface ISQLiteManagedModule
{
///
/// Returns non-zero if the schema for the virtual table has been
/// declared.
///
bool Declared { get; }
///////////////////////////////////////////////////////////////////////
///
/// Returns the name of the module as it was registered with the SQLite
/// core library.
///
string Name { get; }
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated with
/// the virtual table.
///
///
/// The native user-data pointer associated with this module, as it was
/// provided to the SQLite core library when the native module instance
/// was created.
///
///
/// The module name, database name, virtual table name, and all other
/// arguments passed to the CREATE VIRTUAL TABLE statement.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance associated with
/// the virtual table.
///
///
/// Upon failure, this parameter must be modified to contain an error
/// message.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Create(
SQLiteConnection connection, /* in */
IntPtr pClientData, /* in */
string[] arguments, /* in */
ref SQLiteVirtualTable table, /* out */
ref string error /* out */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated with
/// the virtual table.
///
///
/// The native user-data pointer associated with this module, as it was
/// provided to the SQLite core library when the native module instance
/// was created.
///
///
/// The module name, database name, virtual table name, and all other
/// arguments passed to the CREATE VIRTUAL TABLE statement.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance associated with
/// the virtual table.
///
///
/// Upon failure, this parameter must be modified to contain an error
/// message.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Connect(
SQLiteConnection connection, /* in */
IntPtr pClientData, /* in */
string[] arguments, /* in */
ref SQLiteVirtualTable table, /* out */
ref string error /* out */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The object instance containing all the
/// data for the inputs and outputs relating to index selection.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode BestIndex(
SQLiteVirtualTable table, /* in */
SQLiteIndex index /* in, out */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Disconnect(
SQLiteVirtualTable table /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Destroy(
SQLiteVirtualTable table /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance associated
/// with the newly opened virtual table cursor.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Open(
SQLiteVirtualTable table, /* in */
ref SQLiteVirtualTableCursor cursor /* out */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Close(
SQLiteVirtualTableCursor cursor /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// Number used to help identify the selected index.
///
///
/// String used to help identify the selected index.
///
///
/// The values corresponding to each column in the selected index.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Filter(
SQLiteVirtualTableCursor cursor, /* in */
int indexNumber, /* in */
string indexString, /* in */
SQLiteValue[] values /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Next(
SQLiteVirtualTableCursor cursor /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// Non-zero if no more rows are available; zero otherwise.
///
bool Eof(
SQLiteVirtualTableCursor cursor /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// The object instance to be used for
/// returning the specified column value to the SQLite core library.
///
///
/// The zero-based index corresponding to the column containing the
/// value to be returned.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Column(
SQLiteVirtualTableCursor cursor, /* in */
SQLiteContext context, /* in */
int index /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// Upon success, this parameter must be modified to contain the unique
/// integer row identifier for the current row for the specified cursor.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode RowId(
SQLiteVirtualTableCursor cursor, /* in */
ref long rowId /* out */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The array of object instances containing
/// the new or modified column values, if any.
///
///
/// Upon success, this parameter must be modified to contain the unique
/// integer row identifier for the row that was inserted, if any.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Update(
SQLiteVirtualTable table, /* in */
SQLiteValue[] values, /* in */
ref long rowId /* out */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Begin(
SQLiteVirtualTable table /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Sync(
SQLiteVirtualTable table /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Commit(
SQLiteVirtualTable table /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Rollback(
SQLiteVirtualTable table /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The number of arguments to the function being sought.
///
///
/// The name of the function being sought.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance responsible for
/// implementing the specified function.
///
///
/// Upon success, this parameter must be modified to contain the
/// native user-data pointer associated with
/// .
///
///
/// Non-zero if the specified function was found; zero otherwise.
///
bool FindFunction(
SQLiteVirtualTable table, /* in */
int argumentCount, /* in */
string name, /* in */
ref SQLiteFunction function, /* out */
ref IntPtr pClientData /* out */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The new name for the virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Rename(
SQLiteVirtualTable table, /* in */
string newName /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// This is an integer identifier under which the the current state of
/// the virtual table should be saved.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Savepoint(
SQLiteVirtualTable table, /* in */
int savepoint /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// This is an integer used to indicate that any saved states with an
/// identifier greater than or equal to this should be deleted by the
/// virtual table.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode Release(
SQLiteVirtualTable table, /* in */
int savepoint /* in */
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// This is an integer identifier used to specify a specific saved
/// state for the virtual table for it to restore itself back to, which
/// should also have the effect of deleting all saved states with an
/// integer identifier greater than this one.
///
///
/// A standard SQLite return code.
///
SQLiteErrorCode RollbackTo(
SQLiteVirtualTable table, /* in */
int savepoint /* in */
);
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteMemory Static Class
///
/// This class contains static methods that are used to allocate,
/// manipulate, and free native memory provided by the SQLite core library.
///
internal static class SQLiteMemory
{
#region Private Data
#if TRACK_MEMORY_BYTES
///
/// This object instance is used to synchronize access to the other
/// static fields of this class.
///
private static object syncRoot = new object();
///////////////////////////////////////////////////////////////////////
///
/// The total number of outstanding memory bytes allocated by this
/// class using the SQLite core library.
///
private static int bytesAllocated;
///////////////////////////////////////////////////////////////////////
///
/// The maximum number of outstanding memory bytes ever allocated by
/// this class using the SQLite core library.
///
private static int maximumBytesAllocated;
#endif
#endregion
///////////////////////////////////////////////////////////////////////
#region Memory Allocation Helper Methods
///
/// Allocates at least the specified number of bytes of native memory
/// via the SQLite core library sqlite3_malloc() function and returns
/// the resulting native pointer.
///
///
/// The number of bytes to allocate.
///
///
/// The native pointer that points to a block of memory of at least the
/// specified size -OR- if the memory could
/// not be allocated.
///
public static IntPtr Allocate(int size)
{
IntPtr pMemory = UnsafeNativeMethods.sqlite3_malloc(size);
#if TRACK_MEMORY_BYTES
if (pMemory != IntPtr.Zero)
{
int blockSize = Size(pMemory);
if (blockSize > 0)
{
lock (syncRoot)
{
bytesAllocated += blockSize;
if (bytesAllocated > maximumBytesAllocated)
maximumBytesAllocated = bytesAllocated;
}
}
}
#endif
return pMemory;
}
///////////////////////////////////////////////////////////////////////
///
/// Gets and returns the actual size of the specified memory block that
/// was previously obtained from the method.
///
///
/// The native pointer to the memory block previously obtained from the
/// method.
///
///
/// The actual size, in bytes, of the memory block specified via the
/// native pointer.
///
public static int Size(IntPtr pMemory)
{
#if !SQLITE_STANDARD
return UnsafeNativeMethods.sqlite3_malloc_size_interop(pMemory);
#elif TRACK_MEMORY_BYTES
//
// HACK: Ok, we cannot determine the size of the memory block;
// therefore, just track number of allocations instead.
//
return (pMemory != IntPtr.Zero) ? 1 : 0;
#else
return 0;
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Frees a memory block previously obtained from the
/// method.
///
///
/// The native pointer to the memory block previously obtained from the
/// method.
///
public static void Free(IntPtr pMemory)
{
#if TRACK_MEMORY_BYTES
if (pMemory != IntPtr.Zero)
{
int blockSize = Size(pMemory);
if (blockSize > 0)
{
lock (syncRoot)
{
bytesAllocated -= blockSize;
}
}
}
#endif
UnsafeNativeMethods.sqlite3_free(pMemory);
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteString Static Class
///
/// This class contains static methods that are used to deal with native
/// UTF-8 string pointers to be used with the SQLite core library.
///
internal static class SQLiteString
{
#region Private Constants
///
/// This is the maximum possible length for the native UTF-8 encoded
/// strings used with the SQLite core library.
///
private static int ThirtyBits = 0x3fffffff;
///////////////////////////////////////////////////////////////////////
///
/// This is the object instance used to handle
/// conversions from/to UTF-8.
///
private static readonly Encoding Utf8Encoding = Encoding.UTF8;
#endregion
///////////////////////////////////////////////////////////////////////
#region UTF-8 Encoding Helper Methods
///
/// Converts the specified managed string into the UTF-8 encoding and
/// returns the array of bytes containing its representation in that
/// encoding.
///
///
/// The managed string to convert.
///
///
/// The array of bytes containing the representation of the managed
/// string in the UTF-8 encoding or null upon failure.
///
public static byte[] GetUtf8BytesFromString(
string value
)
{
if (value == null)
return null;
return Utf8Encoding.GetBytes(value);
}
///////////////////////////////////////////////////////////////////////
///
/// Converts the specified array of bytes representing a string in the
/// UTF-8 encoding and returns a managed string.
///
///
/// The array of bytes to convert.
///
///
/// The managed string or null upon failure.
///
public static string GetStringFromUtf8Bytes(
byte[] bytes
)
{
if (bytes == null)
return null;
#if !PLATFORM_COMPACTFRAMEWORK
return Utf8Encoding.GetString(bytes);
#else
return Utf8Encoding.GetString(bytes, 0, bytes.Length);
#endif
}
#endregion
///////////////////////////////////////////////////////////////////////
#region UTF-8 String Helper Methods
///
/// Probes a native pointer to a string in the UTF-8 encoding for its
/// terminating NUL character, within the specified length limit.
///
///
/// The native NUL-terminated string pointer.
///
///
/// The maximum length of the native string, in bytes.
///
///
/// The length of the native string, in bytes -OR- zero if the length
/// could not be determined.
///
public static int ProbeForUtf8ByteLength(
IntPtr pValue,
int limit
)
{
int length = 0;
if ((pValue != IntPtr.Zero) && (limit > 0))
{
do
{
if (Marshal.ReadByte(pValue, length) == 0)
break;
if (length >= limit)
break;
length++;
} while (true);
}
return length;
}
///////////////////////////////////////////////////////////////////////
///
/// Converts the specified native NUL-terminated UTF-8 string pointer
/// into a managed string.
///
///
/// The native NUL-terminated UTF-8 string pointer.
///
///
/// The managed string or null upon failure.
///
public static string StringFromUtf8IntPtr(
IntPtr pValue
)
{
return StringFromUtf8IntPtr(pValue,
ProbeForUtf8ByteLength(pValue, ThirtyBits));
}
///////////////////////////////////////////////////////////////////////
///
/// Converts the specified native UTF-8 string pointer of the specified
/// length into a managed string.
///
///
/// The native UTF-8 string pointer.
///
///
/// The length of the native string, in bytes.
///
///
/// The managed string or null upon failure.
///
public static string StringFromUtf8IntPtr(
IntPtr pValue,
int length
)
{
if (pValue == IntPtr.Zero)
return null;
if (length > 0)
{
byte[] bytes = new byte[length];
Marshal.Copy(pValue, bytes, 0, length);
return GetStringFromUtf8Bytes(bytes);
}
return String.Empty;
}
///////////////////////////////////////////////////////////////////////
///
/// Converts the specified managed string into a native NUL-terminated
/// UTF-8 string pointer using memory obtained from the SQLite core
/// library.
///
///
/// The managed string to convert.
///
///
/// The native NUL-terminated UTF-8 string pointer or
/// upon failure.
///
public static IntPtr Utf8IntPtrFromString(
string value
)
{
if (value == null)
return IntPtr.Zero;
IntPtr result = IntPtr.Zero;
byte[] bytes = GetUtf8BytesFromString(value);
if (bytes == null)
return IntPtr.Zero;
int length = bytes.Length;
result = SQLiteMemory.Allocate(length + 1);
if (result == IntPtr.Zero)
return IntPtr.Zero;
Marshal.Copy(bytes, 0, result, length);
Marshal.WriteByte(result, length, 0);
return result;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region UTF-8 String Array Helper Methods
///
/// Converts a logical array of native NUL-terminated UTF-8 string
/// pointers into an array of managed strings.
///
///
/// The number of elements in the logical array of native
/// NUL-terminated UTF-8 string pointers.
///
///
/// The native pointer to the logical array of native NUL-terminated
/// UTF-8 string pointers to convert.
///
///
/// The array of managed strings or null upon failure.
///
public static string[] StringArrayFromUtf8SizeAndIntPtr(
int argc,
IntPtr argv
)
{
if (argc < 0)
return null;
if (argv == IntPtr.Zero)
return null;
string[] result = new string[argc];
for (int index = 0, offset = 0;
index < result.Length;
index++, offset += IntPtr.Size)
{
IntPtr pArg = SQLiteMarshal.ReadIntPtr(argv, offset);
result[index] = (pArg != IntPtr.Zero) ?
StringFromUtf8IntPtr(pArg) : null;
}
return result;
}
///////////////////////////////////////////////////////////////////////
///
/// Converts an array of managed strings into an array of native
/// NUL-terminated UTF-8 string pointers.
///
///
/// The array of managed strings to convert.
///
///
/// The array of native NUL-terminated UTF-8 string pointers or null
/// upon failure.
///
public static IntPtr[] Utf8IntPtrArrayFromStringArray(
string[] values
)
{
if (values == null)
return null;
IntPtr[] result = new IntPtr[values.Length];
for (int index = 0; index < result.Length; index++)
result[index] = Utf8IntPtrFromString(values[index]);
return result;
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteBytes Static Class
///
/// This class contains static methods that are used to deal with native
/// pointers to memory blocks that logically contain arrays of bytes to be
/// used with the SQLite core library.
///
internal static class SQLiteBytes
{
#region Byte Array Helper Methods
///
/// Converts a native pointer to a logical array of bytes of the
/// specified length into a managed byte array.
///
///
/// The native pointer to the logical array of bytes to convert.
///
///
/// The length, in bytes, of the logical array of bytes to convert.
///
///
/// The managed byte array or null upon failure.
///
public static byte[] FromIntPtr(
IntPtr pValue,
int length
)
{
if (pValue == IntPtr.Zero)
return null;
if (length == 0)
return new byte[0];
byte[] result = new byte[length];
Marshal.Copy(pValue, result, 0, length);
return result;
}
///////////////////////////////////////////////////////////////////////
///
/// Converts a managed byte array into a native pointer to a logical
/// array of bytes.
///
///
/// The managed byte array to convert.
///
///
/// The native pointer to a logical byte array or null upon failure.
///
public static IntPtr ToIntPtr(
byte[] value
)
{
if (value == null)
return IntPtr.Zero;
int length = value.Length;
if (length == 0)
return IntPtr.Zero;
IntPtr result = SQLiteMemory.Allocate(length);
if (result == IntPtr.Zero)
return IntPtr.Zero;
Marshal.Copy(value, 0, result, length);
return result;
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteMarshal Static Class
///
/// This class contains static methods that are used to perform several
/// low-level data marshalling tasks between native and managed code.
///
internal static class SQLiteMarshal
{
#region IntPtr Helper Methods
///
/// Returns a new object instance based on the
/// specified object instance and an integer
/// offset.
///
///
/// The object instance representing the base
/// memory location.
///
///
/// The integer offset from the base memory location that the new
/// object instance should point to.
///
///
/// The new object instance.
///
public static IntPtr IntPtrForOffset(
IntPtr pointer,
int offset
)
{
return new IntPtr(pointer.ToInt64() + offset);
}
///////////////////////////////////////////////////////////////////////
///
/// Rounds up an integer size to the next multiple of the alignment.
///
///
/// The size, in bytes, to be rounded up.
///
///
/// The required alignment for the return value.
///
///
/// The size, in bytes, rounded up to the next multiple of the
/// alignment. This value may end up being the same as the original
/// size.
///
public static int RoundUp(
int size,
int alignment
)
{
int alignmentMinusOne = alignment - 1;
return ((size + alignmentMinusOne) & ~alignmentMinusOne);
}
///////////////////////////////////////////////////////////////////////
///
/// Determines the offset, in bytes, of the next structure member.
///
///
/// The offset, in bytes, of the current structure member.
///
///
/// The size, in bytes, of the current structure member.
///
///
/// The alignment, in bytes, of the next structure member.
///
///
/// The offset, in bytes, of the next structure member.
///
public static int NextOffsetOf(
int offset,
int size,
int alignment
)
{
return RoundUp(offset + size, alignment);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Marshal Read Helper Methods
///
/// Reads a value from the specified memory
/// location.
///
///
/// The object instance representing the base
/// memory location.
///
///
/// The integer offset from the base memory location where the
/// value to be read is located.
///
///
/// The value at the specified memory location.
///
public static int ReadInt32(
IntPtr pointer,
int offset
)
{
#if !PLATFORM_COMPACTFRAMEWORK
return Marshal.ReadInt32(pointer, offset);
#else
return Marshal.ReadInt32(IntPtrForOffset(pointer, offset));
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Reads a value from the specified memory
/// location.
///
///
/// The object instance representing the base
/// memory location.
///
///
/// The integer offset from the base memory location where the
/// to be read is located.
///
///
/// The value at the specified memory location.
///
public static double ReadDouble(
IntPtr pointer,
int offset
)
{
#if !PLATFORM_COMPACTFRAMEWORK
return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(
pointer, offset));
#else
return BitConverter.ToDouble(BitConverter.GetBytes(
Marshal.ReadInt64(IntPtrForOffset(pointer, offset))), 0);
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Reads an value from the specified memory
/// location.
///
///
/// The object instance representing the base
/// memory location.
///
///
/// The integer offset from the base memory location where the
/// value to be read is located.
///
///
/// The value at the specified memory location.
///
public static IntPtr ReadIntPtr(
IntPtr pointer,
int offset
)
{
#if !PLATFORM_COMPACTFRAMEWORK
return Marshal.ReadIntPtr(pointer, offset);
#else
return Marshal.ReadIntPtr(IntPtrForOffset(pointer, offset));
#endif
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Marshal Write Helper Methods
///
/// Writes an value to the specified memory
/// location.
///
///
/// The object instance representing the base
/// memory location.
///
///
/// The integer offset from the base memory location where the
/// value to be written is located.
///
///
/// The value to write.
///
public static void WriteInt32(
IntPtr pointer,
int offset,
int value
)
{
#if !PLATFORM_COMPACTFRAMEWORK
Marshal.WriteInt32(pointer, offset, value);
#else
Marshal.WriteInt32(IntPtrForOffset(pointer, offset), value);
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Writes a value to the specified memory
/// location.
///
///
/// The object instance representing the base
/// memory location.
///
///
/// The integer offset from the base memory location where the
/// value to be written is located.
///
///
/// The value to write.
///
public static void WriteDouble(
IntPtr pointer,
int offset,
double value
)
{
#if !PLATFORM_COMPACTFRAMEWORK
Marshal.WriteInt64(pointer, offset,
BitConverter.DoubleToInt64Bits(value));
#else
Marshal.WriteInt64(IntPtrForOffset(pointer, offset),
BitConverter.ToInt64(BitConverter.GetBytes(value), 0));
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Writes a value to the specified memory
/// location.
///
///
/// The object instance representing the base
/// memory location.
///
///
/// The integer offset from the base memory location where the
/// value to be written is located.
///
///
/// The value to write.
///
public static void WriteIntPtr(
IntPtr pointer,
int offset,
IntPtr value
)
{
#if !PLATFORM_COMPACTFRAMEWORK
Marshal.WriteIntPtr(pointer, offset, value);
#else
Marshal.WriteIntPtr(IntPtrForOffset(pointer, offset), value);
#endif
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region SQLiteModule Base Class
///
/// This class represents a managed virtual table module implementation.
/// It is not sealed and must be used as the base class for any
/// user-defined virtual table module classes implemented in managed code.
///
public abstract class SQLiteModule :
ISQLiteManagedModule, /*ISQLiteNativeModule,*/
IDisposable /* NOT SEALED */
{
#region SQLiteNativeModule Private Class
///
/// This class implements the
/// interface by forwarding those method calls to the
/// object instance it contains. If the
/// contained object instance is null, all
/// the methods simply generate an
/// error.
///
private sealed class SQLiteNativeModule :
ISQLiteNativeModule, IDisposable
{
#region Private Constants
///
/// This is the value that is always used for the "logErrors"
/// parameter to the various static error handling methods provided
/// by the class.
///
private const bool DefaultLogErrors = true;
///////////////////////////////////////////////////////////////////
///
/// This is the error message text used when the contained
/// object instance is not available
/// for any reason.
///
private const string ModuleNotAvailableErrorMessage =
"native module implementation not available";
#endregion
///////////////////////////////////////////////////////////////////
#region Private Data
///
/// The object instance used to provide
/// an implementation of the
/// interface.
///
private SQLiteModule module;
#endregion
///////////////////////////////////////////////////////////////////
#region Public Constructors
///
/// Constructs an instance of this class.
///
///
/// The object instance used to provide
/// an implementation of the
/// interface.
///
public SQLiteNativeModule(
SQLiteModule module
)
{
this.module = module;
}
#endregion
///////////////////////////////////////////////////////////////////
#region Private Static Methods
///
/// Sets the table error message to one that indicates the native
/// module implementation is not available.
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The value of .
///
private static SQLiteErrorCode ModuleNotAvailableTableError(
IntPtr pVtab
)
{
SetTableError(null, pVtab, DefaultLogErrors,
ModuleNotAvailableErrorMessage);
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////
///
/// Sets the table error message to one that indicates the native
/// module implementation is not available.
///
///
/// The native pointer to the sqlite3_vtab_cursor derived
/// structure.
///
///
/// The value of .
///
private static SQLiteErrorCode ModuleNotAvailableCursorError(
IntPtr pCursor
)
{
SetCursorError(null, pCursor, DefaultLogErrors,
ModuleNotAvailableErrorMessage);
return SQLiteErrorCode.Error;
}
#endregion
///////////////////////////////////////////////////////////////////
#region ISQLiteNativeModule Members
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xCreate(
IntPtr pDb,
IntPtr pAux,
int argc,
IntPtr argv,
ref IntPtr pVtab,
ref IntPtr pError
)
{
// CheckDisposed();
if (module == null)
{
pError = SQLiteString.Utf8IntPtrFromString(
ModuleNotAvailableErrorMessage);
return SQLiteErrorCode.Error;
}
return module.xCreate(
pDb, pAux, argc, argv, ref pVtab, ref pError);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xConnect(
IntPtr pDb,
IntPtr pAux,
int argc,
IntPtr argv,
ref IntPtr pVtab,
ref IntPtr pError
)
{
// CheckDisposed();
if (module == null)
{
pError = SQLiteString.Utf8IntPtrFromString(
ModuleNotAvailableErrorMessage);
return SQLiteErrorCode.Error;
}
return module.xConnect(
pDb, pAux, argc, argv, ref pVtab, ref pError);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xBestIndex(
IntPtr pVtab,
IntPtr pIndex
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xBestIndex(pVtab, pIndex);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xDisconnect(
IntPtr pVtab
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xDisconnect(pVtab);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xDestroy(
IntPtr pVtab
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xDestroy(pVtab);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xOpen(
IntPtr pVtab,
ref IntPtr pCursor
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xOpen(pVtab, ref pCursor);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xClose(
IntPtr pCursor
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableCursorError(pCursor);
return module.xClose(pCursor);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xFilter(
IntPtr pCursor,
int idxNum,
IntPtr idxStr,
int argc,
IntPtr argv
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableCursorError(pCursor);
return module.xFilter(pCursor, idxNum, idxStr, argc, argv);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xNext(
IntPtr pCursor
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableCursorError(pCursor);
return module.xNext(pCursor);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public int xEof(
IntPtr pCursor
)
{
// CheckDisposed();
if (module == null)
{
ModuleNotAvailableCursorError(pCursor);
return 1;
}
return module.xEof(pCursor);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xColumn(
IntPtr pCursor,
IntPtr pContext,
int index
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableCursorError(pCursor);
return module.xColumn(pCursor, pContext, index);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xRowId(
IntPtr pCursor,
ref long rowId
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableCursorError(pCursor);
return module.xRowId(pCursor, ref rowId);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xUpdate(
IntPtr pVtab,
int argc,
IntPtr argv,
ref long rowId
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xUpdate(pVtab, argc, argv, ref rowId);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xBegin(
IntPtr pVtab
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xBegin(pVtab);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xSync(
IntPtr pVtab
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xSync(pVtab);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xCommit(
IntPtr pVtab
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xCommit(pVtab);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xRollback(
IntPtr pVtab
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xRollback(pVtab);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public int xFindFunction(
IntPtr pVtab,
int nArg,
IntPtr zName,
ref SQLiteCallback callback,
ref IntPtr pClientData
)
{
// CheckDisposed();
if (module == null)
{
ModuleNotAvailableTableError(pVtab);
return 0;
}
return module.xFindFunction(
pVtab, nArg, zName, ref callback, ref pClientData);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xRename(
IntPtr pVtab,
IntPtr zNew
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xRename(pVtab, zNew);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xSavepoint(
IntPtr pVtab,
int iSavepoint
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xSavepoint(pVtab, iSavepoint);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xRelease(
IntPtr pVtab,
int iSavepoint
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xRelease(pVtab, iSavepoint);
}
///////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
public SQLiteErrorCode xRollbackTo(
IntPtr pVtab,
int iSavepoint
)
{
// CheckDisposed();
if (module == null)
return ModuleNotAvailableTableError(pVtab);
return module.xRollbackTo(pVtab, iSavepoint);
}
#endregion
///////////////////////////////////////////////////////////////////
#region IDisposable Members
///
/// Disposes of this object instance.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
///////////////////////////////////////////////////////////////////
#region IDisposable "Pattern" Members
private bool disposed;
///
/// Throws an if this object
/// instance has been disposed.
///
private void CheckDisposed() /* throw */
{
#if THROW_ON_DISPOSED
if (disposed)
{
throw new ObjectDisposedException(
typeof(SQLiteNativeModule).Name);
}
#endif
}
///////////////////////////////////////////////////////////////////
///
/// Disposes of this object instance.
///
///
/// Non-zero if this method is being called from the
/// method. Zero if this method is being
/// called from the finalizer.
///
private /* protected virtual */ void Dispose(bool disposing)
{
if (!disposed)
{
//if (disposing)
//{
// ////////////////////////////////////
// // dispose managed resources here...
// ////////////////////////////////////
//}
//////////////////////////////////////
// release unmanaged resources here...
//////////////////////////////////////
//
// NOTE: The module is not owned by us; therefore, do not
// dispose it.
//
if (module != null)
module = null;
disposed = true;
}
}
#endregion
///////////////////////////////////////////////////////////////////
#region Destructor
///
/// Finalizes this object instance.
///
~SQLiteNativeModule()
{
Dispose(false);
}
#endregion
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Constants
///
/// The default version of the native sqlite3_module structure in use.
///
private static readonly int DefaultModuleVersion = 2;
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Data
///
/// This field is used to store the native sqlite3_module structure
/// associated with this object instance.
///
private UnsafeNativeMethods.sqlite3_module nativeModule;
///////////////////////////////////////////////////////////////////////
///
/// This field is used to store a pointer to the native sqlite3_module
/// structure returned by the sqlite3_create_disposable_module
/// function.
///
private IntPtr disposableModule;
///////////////////////////////////////////////////////////////////////
#if PLATFORM_COMPACTFRAMEWORK
///
/// This field is used to hold the block of native memory that contains
/// the native sqlite3_module structure associated with this object
/// instance when running on the .NET Compact Framework.
///
private IntPtr pNativeModule;
#endif
///////////////////////////////////////////////////////////////////////
///
/// This field is used to store the virtual table instances associated
/// with this module. The native pointer to the sqlite3_vtab derived
/// structure is used to key into this collection.
///
private Dictionary tables;
///////////////////////////////////////////////////////////////////////
///
/// This field is used to store the virtual table cursor instances
/// associated with this module. The native pointer to the
/// sqlite3_vtab_cursor derived structure is used to key into this
/// collection.
///
private Dictionary cursors;
///////////////////////////////////////////////////////////////////////
///
/// This field is used to store the virtual table function instances
/// associated with this module. The case-insensitive function name
/// and the number of arguments (with -1 meaning "any") are used to
/// construct the string that is used to key into this collection.
///
private Dictionary functions;
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Constructors
///
/// Constructs an instance of this class.
///
///
/// The name of the module. This parameter cannot be null.
///
public SQLiteModule(string name)
{
if (name == null)
throw new ArgumentNullException("name");
this.name = name;
this.tables = new Dictionary();
this.cursors = new Dictionary();
this.functions = new Dictionary();
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Internal Methods
///
/// Calls the native SQLite core library in order to create a new
/// disposable module containing the implementation of a virtual table.
///
///
/// The native database connection pointer to use.
///
///
/// Non-zero upon success.
///
internal bool CreateDisposableModule(
IntPtr pDb
)
{
if (disposableModule != IntPtr.Zero)
return true;
IntPtr pName = IntPtr.Zero;
try
{
pName = SQLiteString.Utf8IntPtrFromString(name);
UnsafeNativeMethods.sqlite3_module nativeModule =
AllocateNativeModule();
#if !PLATFORM_COMPACTFRAMEWORK
disposableModule =
UnsafeNativeMethods.sqlite3_create_disposable_module(
pDb, pName, ref nativeModule, IntPtr.Zero, null);
return (disposableModule != IntPtr.Zero);
#elif !SQLITE_STANDARD
disposableModule =
UnsafeNativeMethods.sqlite3_create_disposable_module_interop(
pDb, pName, AllocateNativeModuleInterop(),
nativeModule.iVersion, nativeModule.xCreate,
nativeModule.xConnect, nativeModule.xBestIndex,
nativeModule.xDisconnect, nativeModule.xDestroy,
nativeModule.xOpen, nativeModule.xClose,
nativeModule.xFilter, nativeModule.xNext,
nativeModule.xEof, nativeModule.xColumn,
nativeModule.xRowId, nativeModule.xUpdate,
nativeModule.xBegin, nativeModule.xSync,
nativeModule.xCommit, nativeModule.xRollback,
nativeModule.xFindFunction, nativeModule.xRename,
nativeModule.xSavepoint, nativeModule.xRelease,
nativeModule.xRollbackTo, IntPtr.Zero, null);
return (disposableModule != IntPtr.Zero);
#else
throw new NotImplementedException();
#endif
}
finally
{
if (pName != IntPtr.Zero)
{
SQLiteMemory.Free(pName);
pName = IntPtr.Zero;
}
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Methods
///
/// Creates and returns the native sqlite_module structure using the
/// configured (or default)
/// interface implementation.
///
///
/// The native sqlite_module structure using the configured (or
/// default) interface
/// implementation.
///
private UnsafeNativeMethods.sqlite3_module AllocateNativeModule()
{
return AllocateNativeModule(GetNativeModuleImpl());
}
///////////////////////////////////////////////////////////////////////
#if PLATFORM_COMPACTFRAMEWORK
///
/// Creates and returns a memory block obtained from the SQLite core
/// library used to store the native sqlite3_module structure for this
/// object instance when running on the .NET Compact Framework.
///
///
/// The native pointer to the native sqlite3_module structure.
///
private IntPtr AllocateNativeModuleInterop()
{
if (pNativeModule == IntPtr.Zero)
{
//
// HACK: No easy way to determine the size of the native
// sqlite_module structure when running on the .NET
// Compact Framework; therefore, just base the size
// on what we know:
//
// There is one integer member.
// There are 22 function pointer members.
//
pNativeModule = SQLiteMemory.Allocate(23 * IntPtr.Size);
if (pNativeModule == IntPtr.Zero)
throw new OutOfMemoryException("sqlite3_module");
}
return pNativeModule;
}
#endif
///////////////////////////////////////////////////////////////////////
///
/// Creates and returns the native sqlite_module structure using the
/// specified interface
/// implementation.
///
///
/// The interface implementation to
/// use.
///
///
/// The native sqlite_module structure using the specified
/// interface implementation.
///
private UnsafeNativeMethods.sqlite3_module AllocateNativeModule(
ISQLiteNativeModule module
)
{
nativeModule = new UnsafeNativeMethods.sqlite3_module();
nativeModule.iVersion = DefaultModuleVersion;
if (module != null)
{
nativeModule.xCreate = new UnsafeNativeMethods.xCreate(
module.xCreate);
nativeModule.xConnect = new UnsafeNativeMethods.xConnect(
module.xConnect);
nativeModule.xBestIndex = new UnsafeNativeMethods.xBestIndex(
module.xBestIndex);
nativeModule.xDisconnect = new UnsafeNativeMethods.xDisconnect(
module.xDisconnect);
nativeModule.xDestroy = new UnsafeNativeMethods.xDestroy(
module.xDestroy);
nativeModule.xOpen = new UnsafeNativeMethods.xOpen(
module.xOpen);
nativeModule.xClose = new UnsafeNativeMethods.xClose(
module.xClose);
nativeModule.xFilter = new UnsafeNativeMethods.xFilter(
module.xFilter);
nativeModule.xNext = new UnsafeNativeMethods.xNext(
module.xNext);
nativeModule.xEof = new UnsafeNativeMethods.xEof(module.xEof);
nativeModule.xColumn = new UnsafeNativeMethods.xColumn(
module.xColumn);
nativeModule.xRowId = new UnsafeNativeMethods.xRowId(
module.xRowId);
nativeModule.xUpdate = new UnsafeNativeMethods.xUpdate(
module.xUpdate);
nativeModule.xBegin = new UnsafeNativeMethods.xBegin(
module.xBegin);
nativeModule.xSync = new UnsafeNativeMethods.xSync(
module.xSync);
nativeModule.xCommit = new UnsafeNativeMethods.xCommit(
module.xCommit);
nativeModule.xRollback = new UnsafeNativeMethods.xRollback(
module.xRollback);
nativeModule.xFindFunction = new UnsafeNativeMethods.xFindFunction(
module.xFindFunction);
nativeModule.xRename = new UnsafeNativeMethods.xRename(
module.xRename);
nativeModule.xSavepoint = new UnsafeNativeMethods.xSavepoint(
module.xSavepoint);
nativeModule.xRelease = new UnsafeNativeMethods.xRelease(
module.xRelease);
nativeModule.xRollbackTo = new UnsafeNativeMethods.xRollbackTo(
module.xRollbackTo);
}
else
{
nativeModule.xCreate = new UnsafeNativeMethods.xCreate(
xCreate);
nativeModule.xConnect = new UnsafeNativeMethods.xConnect(
xConnect);
nativeModule.xBestIndex = new UnsafeNativeMethods.xBestIndex(
xBestIndex);
nativeModule.xDisconnect = new UnsafeNativeMethods.xDisconnect(
xDisconnect);
nativeModule.xDestroy = new UnsafeNativeMethods.xDestroy(
xDestroy);
nativeModule.xOpen = new UnsafeNativeMethods.xOpen(xOpen);
nativeModule.xClose = new UnsafeNativeMethods.xClose(xClose);
nativeModule.xFilter = new UnsafeNativeMethods.xFilter(
xFilter);
nativeModule.xNext = new UnsafeNativeMethods.xNext(xNext);
nativeModule.xEof = new UnsafeNativeMethods.xEof(xEof);
nativeModule.xColumn = new UnsafeNativeMethods.xColumn(
xColumn);
nativeModule.xRowId = new UnsafeNativeMethods.xRowId(xRowId);
nativeModule.xUpdate = new UnsafeNativeMethods.xUpdate(
xUpdate);
nativeModule.xBegin = new UnsafeNativeMethods.xBegin(xBegin);
nativeModule.xSync = new UnsafeNativeMethods.xSync(xSync);
nativeModule.xCommit = new UnsafeNativeMethods.xCommit(
xCommit);
nativeModule.xRollback = new UnsafeNativeMethods.xRollback(
xRollback);
nativeModule.xFindFunction = new UnsafeNativeMethods.xFindFunction(
xFindFunction);
nativeModule.xRename = new UnsafeNativeMethods.xRename(
xRename);
nativeModule.xSavepoint = new UnsafeNativeMethods.xSavepoint(
xSavepoint);
nativeModule.xRelease = new UnsafeNativeMethods.xRelease(
xRelease);
nativeModule.xRollbackTo = new UnsafeNativeMethods.xRollbackTo(
xRollbackTo);
}
return nativeModule;
}
///////////////////////////////////////////////////////////////////////
///
/// Creates a copy of the specified
/// object instance,
/// using default implementations for the contained delegates when
/// necessary.
///
///
/// The object
/// instance to copy.
///
///
/// The new object
/// instance.
///
private UnsafeNativeMethods.sqlite3_module CopyNativeModule(
UnsafeNativeMethods.sqlite3_module module
)
{
UnsafeNativeMethods.sqlite3_module newModule =
new UnsafeNativeMethods.sqlite3_module();
newModule.iVersion = module.iVersion;
newModule.xCreate = new UnsafeNativeMethods.xCreate(
(module.xCreate != null) ? module.xCreate : xCreate);
newModule.xConnect = new UnsafeNativeMethods.xConnect(
(module.xConnect != null) ? module.xConnect : xConnect);
newModule.xBestIndex = new UnsafeNativeMethods.xBestIndex(
(module.xBestIndex != null) ? module.xBestIndex : xBestIndex);
newModule.xDisconnect = new UnsafeNativeMethods.xDisconnect(
(module.xDisconnect != null) ? module.xDisconnect :
xDisconnect);
newModule.xDestroy = new UnsafeNativeMethods.xDestroy(
(module.xDestroy != null) ? module.xDestroy : xDestroy);
newModule.xOpen = new UnsafeNativeMethods.xOpen(
(module.xOpen != null) ? module.xOpen : xOpen);
newModule.xClose = new UnsafeNativeMethods.xClose(
(module.xClose != null) ? module.xClose : xClose);
newModule.xFilter = new UnsafeNativeMethods.xFilter(
(module.xFilter != null) ? module.xFilter : xFilter);
newModule.xNext = new UnsafeNativeMethods.xNext(
(module.xNext != null) ? module.xNext : xNext);
newModule.xEof = new UnsafeNativeMethods.xEof(
(module.xEof != null) ? module.xEof : xEof);
newModule.xColumn = new UnsafeNativeMethods.xColumn(
(module.xColumn != null) ? module.xColumn : xColumn);
newModule.xRowId = new UnsafeNativeMethods.xRowId(
(module.xRowId != null) ? module.xRowId : xRowId);
newModule.xUpdate = new UnsafeNativeMethods.xUpdate(
(module.xUpdate != null) ? module.xUpdate : xUpdate);
newModule.xBegin = new UnsafeNativeMethods.xBegin(
(module.xBegin != null) ? module.xBegin : xBegin);
newModule.xSync = new UnsafeNativeMethods.xSync(
(module.xSync != null) ? module.xSync : xSync);
newModule.xCommit = new UnsafeNativeMethods.xCommit(
(module.xCommit != null) ? module.xCommit : xCommit);
newModule.xRollback = new UnsafeNativeMethods.xRollback(
(module.xRollback != null) ? module.xRollback : xRollback);
newModule.xFindFunction = new UnsafeNativeMethods.xFindFunction(
(module.xFindFunction != null) ? module.xFindFunction :
xFindFunction);
newModule.xRename = new UnsafeNativeMethods.xRename(
(module.xRename != null) ? module.xRename : xRename);
newModule.xSavepoint = new UnsafeNativeMethods.xSavepoint(
(module.xSavepoint != null) ? module.xSavepoint : xSavepoint);
newModule.xRelease = new UnsafeNativeMethods.xRelease(
(module.xRelease != null) ? module.xRelease : xRelease);
newModule.xRollbackTo = new UnsafeNativeMethods.xRollbackTo(
(module.xRollbackTo != null) ? module.xRollbackTo :
xRollbackTo);
return newModule;
}
///////////////////////////////////////////////////////////////////////
///
/// Calls one of the virtual table initialization methods.
///
///
/// Non-zero to call the
/// method; otherwise, the
/// method will be called.
///
///
/// The native database connection handle.
///
///
/// The original native pointer value that was provided to the
/// sqlite3_create_module(), sqlite3_create_module_v2() or
/// sqlite3_create_disposable_module() functions.
///
///
/// The number of arguments from the CREATE VIRTUAL TABLE statement.
///
///
/// The array of string arguments from the CREATE VIRTUAL TABLE
/// statement.
///
///
/// Upon success, this parameter must be modified to point to the newly
/// created native sqlite3_vtab derived structure.
///
///
/// Upon failure, this parameter must be modified to point to the error
/// message, with the underlying memory having been obtained from the
/// sqlite3_malloc() function.
///
///
/// A standard SQLite return code.
///
private SQLiteErrorCode CreateOrConnect(
bool create,
IntPtr pDb,
IntPtr pAux,
int argc,
IntPtr argv,
ref IntPtr pVtab,
ref IntPtr pError
)
{
try
{
string fileName = SQLiteString.StringFromUtf8IntPtr(
UnsafeNativeMethods.sqlite3_db_filename(pDb, IntPtr.Zero));
using (SQLiteConnection connection = new SQLiteConnection(
pDb, fileName, false))
{
SQLiteVirtualTable table = null;
string error = null;
if ((create && Create(connection, pAux,
SQLiteString.StringArrayFromUtf8SizeAndIntPtr(argc,
argv), ref table, ref error) == SQLiteErrorCode.Ok) ||
(!create && Connect(connection, pAux,
SQLiteString.StringArrayFromUtf8SizeAndIntPtr(argc,
argv), ref table, ref error) == SQLiteErrorCode.Ok))
{
if (table != null)
{
pVtab = TableToIntPtr(table);
return SQLiteErrorCode.Ok;
}
else
{
pError = SQLiteString.Utf8IntPtrFromString(
"no table was created");
}
}
else
{
pError = SQLiteString.Utf8IntPtrFromString(error);
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
pError = SQLiteString.Utf8IntPtrFromString(e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// Calls one of the virtual table finalization methods.
///
///
/// Non-zero to call the
/// method; otherwise, the
/// method will be
/// called.
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// A standard SQLite return code.
///
private SQLiteErrorCode DestroyOrDisconnect(
bool destroy,
IntPtr pVtab
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
{
if ((destroy && (Destroy(table) == SQLiteErrorCode.Ok)) ||
(!destroy && (Disconnect(table) == SQLiteErrorCode.Ok)))
{
if (tables != null)
tables.Remove(pVtab);
return SQLiteErrorCode.Ok;
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
//
// NOTE: At this point, there is no way to report the error
// condition back to the caller; therefore, use the
// logging facility instead.
//
try
{
if (LogExceptionsNoThrow)
{
/* throw */
SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
String.Format(CultureInfo.CurrentCulture,
"Caught exception in \"{0}\" method: {1}",
destroy ? "xDestroy" : "xDisconnect", e));
}
}
catch
{
// do nothing.
}
}
finally
{
FreeTable(pVtab);
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
#region Static Error Handling Helper Methods
///
/// Arranges for the specified error message to be placed into the
/// zErrMsg field of a sqlite3_vtab derived structure, freeing the
/// existing error message, if any.
///
///
/// The object instance to be used.
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// Non-zero if this error message should also be logged using the
/// class.
///
///
/// The error message.
///
///
/// Non-zero upon success.
///
private static bool SetTableError(
SQLiteModule module,
IntPtr pVtab,
bool logErrors,
string error
)
{
try
{
if (logErrors)
{
SQLiteLog.LogMessage(SQLiteErrorCode.Error,
String.Format(CultureInfo.CurrentCulture,
"Virtual table error: {0}", error)); /* throw */
}
}
catch
{
// do nothing.
}
if (pVtab == IntPtr.Zero)
return false;
int offset = 0;
offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
sizeof(int));
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
IntPtr.Size);
IntPtr pError = SQLiteMarshal.ReadIntPtr(pVtab, offset);
if (pError != IntPtr.Zero)
{
SQLiteMemory.Free(pError); pError = IntPtr.Zero;
SQLiteMarshal.WriteIntPtr(pVtab, offset, pError);
}
if (error == null)
return true;
bool success = false;
try
{
pError = SQLiteString.Utf8IntPtrFromString(error);
SQLiteMarshal.WriteIntPtr(pVtab, offset, pError);
success = true;
}
finally
{
if (!success && (pError != IntPtr.Zero))
{
SQLiteMemory.Free(pError);
pError = IntPtr.Zero;
}
}
return success;
}
///////////////////////////////////////////////////////////////////////
///
/// Arranges for the specified error message to be placed into the
/// zErrMsg field of a sqlite3_vtab derived structure, freeing the
/// existing error message, if any.
///
///
/// The object instance to be used.
///
///
/// The object instance used to
/// lookup the native pointer to the sqlite3_vtab derived structure.
///
///
/// Non-zero if this error message should also be logged using the
/// class.
///
///
/// The error message.
///
///
/// Non-zero upon success.
///
private static bool SetTableError(
SQLiteModule module,
SQLiteVirtualTable table,
bool logErrors,
string error
)
{
if (table == null)
return false;
IntPtr pVtab = table.NativeHandle;
if (pVtab == IntPtr.Zero)
return false;
return SetTableError(module, pVtab, logErrors, error);
}
///////////////////////////////////////////////////////////////////////
///
/// Arranges for the specified error message to be placed into the
/// zErrMsg field of a sqlite3_vtab derived structure, freeing the
/// existing error message, if any.
///
///
/// The object instance to be used.
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure
/// used to get the native pointer to the sqlite3_vtab derived
/// structure.
///
///
/// Non-zero if this error message should also be logged using the
/// class.
///
///
/// The error message.
///
///
/// Non-zero upon success.
///
private static bool SetCursorError(
SQLiteModule module,
IntPtr pCursor,
bool logErrors,
string error
)
{
if (pCursor == IntPtr.Zero)
return false;
IntPtr pVtab = TableFromCursor(module, pCursor);
if (pVtab == IntPtr.Zero)
return false;
return SetTableError(module, pVtab, logErrors, error);
}
///////////////////////////////////////////////////////////////////////
///
/// Arranges for the specified error message to be placed into the
/// zErrMsg field of a sqlite3_vtab derived structure, freeing the
/// existing error message, if any.
///
///
/// The object instance to be used.
///
///
/// The object instance used to
/// lookup the native pointer to the sqlite3_vtab derived structure.
///
///
/// Non-zero if this error message should also be logged using the
/// class.
///
///
/// The error message.
///
///
/// Non-zero upon success.
///
private static bool SetCursorError(
SQLiteModule module,
SQLiteVirtualTableCursor cursor,
bool logErrors,
string error
)
{
if (cursor == null)
return false;
IntPtr pCursor = cursor.NativeHandle;
if (pCursor == IntPtr.Zero)
return false;
return SetCursorError(module, pCursor, logErrors, error);
}
#endregion
#endregion
///////////////////////////////////////////////////////////////////////
#region Protected Members
#region Module Helper Methods
///
/// Gets and returns the interface
/// implementation to be used when creating the native sqlite3_module
/// structure. Derived classes may override this method to supply an
/// alternate implementation for the
/// interface.
///
///
/// The interface implementation to
/// be used when populating the native sqlite3_module structure. If
/// the returned value is null, the private methods provided by the
/// class and relating to the
/// interface will be used to
/// create the necessary delegates.
///
protected virtual ISQLiteNativeModule GetNativeModuleImpl()
{
return null; /* NOTE: Use the built-in default delegates. */
}
///////////////////////////////////////////////////////////////////////
///
/// Creates and returns the
/// interface implementation corresponding to the current
/// object instance.
///
///
/// The interface implementation
/// corresponding to the current object
/// instance.
///
protected virtual ISQLiteNativeModule CreateNativeModuleImpl()
{
return new SQLiteNativeModule(this);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Native Table Helper Methods
///
/// Allocates a native sqlite3_vtab derived structure and returns a
/// native pointer to it.
///
///
/// A native pointer to a native sqlite3_vtab derived structure.
///
protected virtual IntPtr AllocateTable()
{
int size = Marshal.SizeOf(typeof(
UnsafeNativeMethods.sqlite3_vtab));
return SQLiteMemory.Allocate(size);
}
///////////////////////////////////////////////////////////////////////
///
/// Zeros out the fields of a native sqlite3_vtab derived structure.
///
///
/// The native pointer to the native sqlite3_vtab derived structure to
/// zero.
///
protected virtual void ZeroTable(
IntPtr pVtab
)
{
if (pVtab == IntPtr.Zero)
return;
int offset = 0;
SQLiteMarshal.WriteIntPtr(pVtab, offset, IntPtr.Zero);
offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
sizeof(int));
SQLiteMarshal.WriteInt32(pVtab, offset, 0);
offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
IntPtr.Size);
SQLiteMarshal.WriteIntPtr(pVtab, offset, IntPtr.Zero);
}
///////////////////////////////////////////////////////////////////////
///
/// Frees a native sqlite3_vtab structure using the provided native
/// pointer to it.
///
///
/// A native pointer to a native sqlite3_vtab derived structure.
///
protected virtual void FreeTable(
IntPtr pVtab
)
{
SetTableError(pVtab, null);
SQLiteMemory.Free(pVtab);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Native Cursor Helper Methods
///
/// Allocates a native sqlite3_vtab_cursor derived structure and
/// returns a native pointer to it.
///
///
/// A native pointer to a native sqlite3_vtab_cursor derived structure.
///
protected virtual IntPtr AllocateCursor()
{
int size = Marshal.SizeOf(typeof(
UnsafeNativeMethods.sqlite3_vtab_cursor));
return SQLiteMemory.Allocate(size);
}
///////////////////////////////////////////////////////////////////////
///
/// Frees a native sqlite3_vtab_cursor structure using the provided
/// native pointer to it.
///
///
/// A native pointer to a native sqlite3_vtab_cursor derived structure.
///
protected virtual void FreeCursor(
IntPtr pCursor
)
{
SQLiteMemory.Free(pCursor);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Static Table Lookup Methods
///
/// Reads and returns the native pointer to the sqlite3_vtab derived
/// structure based on the native pointer to the sqlite3_vtab_cursor
/// derived structure.
///
///
/// The object instance to be used.
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure
/// from which to read the native pointer to the sqlite3_vtab derived
/// structure.
///
///
/// The native pointer to the sqlite3_vtab derived structure -OR-
/// if it cannot be determined.
///
private static IntPtr TableFromCursor(
SQLiteModule module,
IntPtr pCursor
)
{
if (pCursor == IntPtr.Zero)
return IntPtr.Zero;
return Marshal.ReadIntPtr(pCursor);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Table Lookup Methods
///
/// Reads and returns the native pointer to the sqlite3_vtab derived
/// structure based on the native pointer to the sqlite3_vtab_cursor
/// derived structure.
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure
/// from which to read the native pointer to the sqlite3_vtab derived
/// structure.
///
///
/// The native pointer to the sqlite3_vtab derived structure -OR-
/// if it cannot be determined.
///
protected virtual IntPtr TableFromCursor(
IntPtr pCursor
)
{
return TableFromCursor(this, pCursor);
}
///////////////////////////////////////////////////////////////////////
///
/// Looks up and returns the object
/// instance based on the native pointer to the sqlite3_vtab derived
/// structure.
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The object instance or null if
/// the corresponding one cannot be found.
///
protected virtual SQLiteVirtualTable TableFromIntPtr(
IntPtr pVtab
)
{
if (pVtab == IntPtr.Zero)
{
SetTableError(pVtab, "invalid native table");
return null;
}
SQLiteVirtualTable table;
if ((tables != null) &&
tables.TryGetValue(pVtab, out table))
{
return table;
}
SetTableError(pVtab, String.Format(
CultureInfo.CurrentCulture,
"managed table for {0} not found", pVtab));
return null;
}
///////////////////////////////////////////////////////////////////////
///
/// Allocates and returns a native pointer to a sqlite3_vtab derived
/// structure and creates an association between it and the specified
/// object instance.
///
///
/// The object instance to be used
/// when creating the association.
///
///
/// The native pointer to a sqlite3_vtab derived structure or
/// if the method fails for any reason.
///
protected virtual IntPtr TableToIntPtr(
SQLiteVirtualTable table
)
{
if ((table == null) || (tables == null))
return IntPtr.Zero;
IntPtr pVtab = IntPtr.Zero;
bool success = false;
try
{
pVtab = AllocateTable();
if (pVtab != IntPtr.Zero)
{
ZeroTable(pVtab);
table.NativeHandle = pVtab;
tables.Add(pVtab, table);
success = true;
}
}
finally
{
if (!success && (pVtab != IntPtr.Zero))
{
FreeTable(pVtab);
pVtab = IntPtr.Zero;
}
}
return pVtab;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Cursor Lookup Methods
///
/// Looks up and returns the
/// object instance based on the native pointer to the
/// sqlite3_vtab_cursor derived structure.
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The native pointer to the sqlite3_vtab_cursor derived structure.
///
///
/// The object instance or null
/// if the corresponding one cannot be found.
///
protected virtual SQLiteVirtualTableCursor CursorFromIntPtr(
IntPtr pVtab,
IntPtr pCursor
)
{
if (pCursor == IntPtr.Zero)
{
SetTableError(pVtab, "invalid native cursor");
return null;
}
SQLiteVirtualTableCursor cursor;
if ((cursors != null) &&
cursors.TryGetValue(pCursor, out cursor))
{
return cursor;
}
SetTableError(pVtab, String.Format(
CultureInfo.CurrentCulture,
"managed cursor for {0} not found", pCursor));
return null;
}
///////////////////////////////////////////////////////////////////////
///
/// Allocates and returns a native pointer to a sqlite3_vtab_cursor
/// derived structure and creates an association between it and the
/// specified object instance.
///
///
/// The object instance to be
/// used when creating the association.
///
///
/// The native pointer to a sqlite3_vtab_cursor derived structure or
/// if the method fails for any reason.
///
protected virtual IntPtr CursorToIntPtr(
SQLiteVirtualTableCursor cursor
)
{
if ((cursor == null) || (cursors == null))
return IntPtr.Zero;
IntPtr pCursor = IntPtr.Zero;
bool success = false;
try
{
pCursor = AllocateCursor();
if (pCursor != IntPtr.Zero)
{
cursor.NativeHandle = pCursor;
cursors.Add(pCursor, cursor);
success = true;
}
}
finally
{
if (!success && (pCursor != IntPtr.Zero))
{
FreeCursor(pCursor);
pCursor = IntPtr.Zero;
}
}
return pCursor;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Function Lookup Methods
///
/// Deterimines the key that should be used to identify and store the
/// object instance for the virtual table
/// (i.e. to be returned via the
/// method).
///
///
/// The number of arguments to the virtual table function.
///
///
/// The name of the virtual table function.
///
///
/// The object instance associated with
/// this virtual table function.
///
///
/// The string that should be used to identify and store the virtual
/// table function instance. This method cannot return null. If null
/// is returned from this method, the behavior is undefined.
///
protected virtual string GetFunctionKey(
int argumentCount,
string name,
SQLiteFunction function
)
{
return String.Format("{0}:{1}", argumentCount, name);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Table Declaration Helper Methods
///
/// Attempts to declare the schema for the virtual table using the
/// specified database connection.
///
///
/// The object instance to use when
/// declaring the schema of the virtual table. This parameter may not
/// be null.
///
///
/// The string containing the CREATE TABLE statement that completely
/// describes the schema for the virtual table. This parameter may not
/// be null.
///
///
/// Upon failure, this parameter must be modified to contain an error
/// message.
///
///
/// A standard SQLite return code.
///
protected virtual SQLiteErrorCode DeclareTable(
SQLiteConnection connection,
string sql,
ref string error
)
{
if (connection == null)
{
error = "invalid connection";
return SQLiteErrorCode.Error;
}
SQLiteBase sqliteBase = connection._sql;
if (sqliteBase == null)
{
error = "connection has invalid handle";
return SQLiteErrorCode.Error;
}
if (sql == null)
{
error = "invalid SQL statement";
return SQLiteErrorCode.Error;
}
return sqliteBase.DeclareVirtualTable(this, sql, ref error);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Function Declaration Helper Methods
///
/// Calls the native SQLite core library in order to declare a virtual
/// table function in response to a call into the
///
/// or virtual table
/// methods.
///
///
/// The object instance to use when
/// declaring the schema of the virtual table.
///
///
/// The number of arguments to the function being declared.
///
///
/// The name of the function being declared.
///
///
/// Upon success, the contents of this parameter are undefined. Upon
/// failure, it should contain an appropriate error message.
///
///
/// A standard SQLite return code.
///
protected virtual SQLiteErrorCode DeclareFunction(
SQLiteConnection connection,
int argumentCount,
string name,
ref string error
)
{
if (connection == null)
{
error = "invalid connection";
return SQLiteErrorCode.Error;
}
SQLiteBase sqliteBase = connection._sql;
if (sqliteBase == null)
{
error = "connection has invalid handle";
return SQLiteErrorCode.Error;
}
return sqliteBase.DeclareVirtualFunction(
this, argumentCount, name, ref error);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Error Handling Properties
private bool logErrors;
///
/// Returns or sets a boolean value indicating whether virtual table
/// errors should be logged using the class.
///
protected virtual bool LogErrorsNoThrow
{
get { return logErrors; }
set { logErrors = value; }
}
///////////////////////////////////////////////////////////////////////
private bool logExceptions;
///
/// Returns or sets a boolean value indicating whether exceptions
/// caught in the
/// method,
/// method, and the
/// method should be logged using the
/// class.
///
protected virtual bool LogExceptionsNoThrow
{
get { return logExceptions; }
set { logExceptions = value; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Error Handling Helper Methods
///
/// Arranges for the specified error message to be placed into the
/// zErrMsg field of a sqlite3_vtab derived structure, freeing the
/// existing error message, if any.
///
///
/// The native pointer to the sqlite3_vtab derived structure.
///
///
/// The error message.
///
///
/// Non-zero upon success.
///
protected virtual bool SetTableError(
IntPtr pVtab,
string error
)
{
return SetTableError(this, pVtab, LogErrorsNoThrow, error);
}
///////////////////////////////////////////////////////////////////////
///
/// Arranges for the specified error message to be placed into the
/// zErrMsg field of a sqlite3_vtab derived structure, freeing the
/// existing error message, if any.
///
///
/// The object instance used to
/// lookup the native pointer to the sqlite3_vtab derived structure.
///
///
/// The error message.
///
///
/// Non-zero upon success.
///
protected virtual bool SetTableError(
SQLiteVirtualTable table,
string error
)
{
return SetTableError(this, table, LogErrorsNoThrow, error);
}
///////////////////////////////////////////////////////////////////////
///
/// Arranges for the specified error message to be placed into the
/// zErrMsg field of a sqlite3_vtab derived structure, freeing the
/// existing error message, if any.
///
///
/// The object instance used to
/// lookup the native pointer to the sqlite3_vtab derived structure.
///
///
/// The error message.
///
///
/// Non-zero upon success.
///
protected virtual bool SetCursorError(
SQLiteVirtualTableCursor cursor,
string error
)
{
return SetCursorError(this, cursor, LogErrorsNoThrow, error);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Index Handling Helper Methods
///
/// Modifies the specified object instance
/// to contain the specified estimated cost.
///
///
/// The object instance to modify.
///
///
/// The estimated cost value to use.
///
///
/// Non-zero upon success.
///
protected virtual bool SetEstimatedCost(
SQLiteIndex index,
double estimatedCost
)
{
if ((index == null) || (index.Outputs == null))
return false;
index.Outputs.EstimatedCost = estimatedCost;
return true;
}
///////////////////////////////////////////////////////////////////////
///
/// Modifies the specified object instance
/// to contain the default estimated cost.
///
///
/// The object instance to modify.
///
///
/// Non-zero upon success.
///
protected virtual bool SetEstimatedCost(
SQLiteIndex index
)
{
return SetEstimatedCost(index, SQLiteIndex.DefaultEstimatedCost);
}
#endregion
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
///
/// Returns or sets a boolean value indicating whether virtual table
/// errors should be logged using the class.
///
public virtual bool LogErrors
{
get { CheckDisposed(); return LogErrorsNoThrow; }
set { CheckDisposed(); LogErrorsNoThrow = value; }
}
///////////////////////////////////////////////////////////////////////
///
/// Returns or sets a boolean value indicating whether exceptions
/// caught in the
/// method,
/// method, and the
/// method should be logged using the
/// class.
///
public virtual bool LogExceptions
{
get { CheckDisposed(); return LogExceptionsNoThrow; }
set { CheckDisposed(); LogExceptionsNoThrow = value; }
}
#endregion
///////////////////////////////////////////////////////////////////////
#region ISQLiteNativeModule Members
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xCreate(
IntPtr pDb,
IntPtr pAux,
int argc,
IntPtr argv,
ref IntPtr pVtab,
ref IntPtr pError
)
{
return CreateOrConnect(
true, pDb, pAux, argc, argv, ref pVtab, ref pError);
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xConnect(
IntPtr pDb,
IntPtr pAux,
int argc,
IntPtr argv,
ref IntPtr pVtab,
ref IntPtr pError
)
{
return CreateOrConnect(
false, pDb, pAux, argc, argv, ref pVtab, ref pError);
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xBestIndex(
IntPtr pVtab,
IntPtr pIndex
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
{
SQLiteIndex index = null;
SQLiteIndex.FromIntPtr(pIndex, ref index);
if (BestIndex(table, index) == SQLiteErrorCode.Ok)
{
SQLiteIndex.ToIntPtr(index, pIndex);
return SQLiteErrorCode.Ok;
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xDisconnect(
IntPtr pVtab
)
{
return DestroyOrDisconnect(false, pVtab);
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xDestroy(
IntPtr pVtab
)
{
return DestroyOrDisconnect(true, pVtab);
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xOpen(
IntPtr pVtab,
ref IntPtr pCursor
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
{
SQLiteVirtualTableCursor cursor = null;
if (Open(table, ref cursor) == SQLiteErrorCode.Ok)
{
if (cursor != null)
{
pCursor = CursorToIntPtr(cursor);
if (pCursor != IntPtr.Zero)
{
return SQLiteErrorCode.Ok;
}
else
{
SetTableError(pVtab,
"no native cursor was created");
}
}
else
{
SetTableError(pVtab,
"no managed cursor was created");
}
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xClose(
IntPtr pCursor
)
{
IntPtr pVtab = IntPtr.Zero;
try
{
pVtab = TableFromCursor(pCursor);
SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
pVtab, pCursor);
if (cursor != null)
{
if (Close(cursor) == SQLiteErrorCode.Ok)
{
if (cursors != null)
cursors.Remove(pCursor);
return SQLiteErrorCode.Ok;
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
finally
{
FreeCursor(pCursor);
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xFilter(
IntPtr pCursor,
int idxNum,
IntPtr idxStr,
int argc,
IntPtr argv
)
{
IntPtr pVtab = IntPtr.Zero;
try
{
pVtab = TableFromCursor(pCursor);
SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
pVtab, pCursor);
if (cursor != null)
{
if (Filter(cursor, idxNum,
SQLiteString.StringFromUtf8IntPtr(idxStr),
SQLiteValue.ArrayFromSizeAndIntPtr(argc,
argv)) == SQLiteErrorCode.Ok)
{
return SQLiteErrorCode.Ok;
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xNext(
IntPtr pCursor
)
{
IntPtr pVtab = IntPtr.Zero;
try
{
pVtab = TableFromCursor(pCursor);
SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
pVtab, pCursor);
if (cursor != null)
{
if (Next(cursor) == SQLiteErrorCode.Ok)
return SQLiteErrorCode.Ok;
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private int xEof(
IntPtr pCursor
)
{
IntPtr pVtab = IntPtr.Zero;
try
{
pVtab = TableFromCursor(pCursor);
SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
pVtab, pCursor);
if (cursor != null)
return Eof(cursor) ? 1 : 0;
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return 1; /* NOTE: On any error, return "no more rows". */
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xColumn(
IntPtr pCursor,
IntPtr pContext,
int index
)
{
IntPtr pVtab = IntPtr.Zero;
try
{
pVtab = TableFromCursor(pCursor);
SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
pVtab, pCursor);
if (cursor != null)
{
SQLiteContext context = new SQLiteContext(pContext);
return Column(cursor, context, index);
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xRowId(
IntPtr pCursor,
ref long rowId
)
{
IntPtr pVtab = IntPtr.Zero;
try
{
pVtab = TableFromCursor(pCursor);
SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
pVtab, pCursor);
if (cursor != null)
return RowId(cursor, ref rowId);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xUpdate(
IntPtr pVtab,
int argc,
IntPtr argv,
ref long rowId
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
{
return Update(table,
SQLiteValue.ArrayFromSizeAndIntPtr(argc, argv),
ref rowId);
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xBegin(
IntPtr pVtab
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
return Begin(table);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xSync(
IntPtr pVtab
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
return Sync(table);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xCommit(
IntPtr pVtab
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
return Commit(table);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xRollback(
IntPtr pVtab
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
return Rollback(table);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private int xFindFunction(
IntPtr pVtab,
int nArg,
IntPtr zName,
ref SQLiteCallback callback,
ref IntPtr pClientData
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
{
string name = SQLiteString.StringFromUtf8IntPtr(zName);
SQLiteFunction function = null;
if (FindFunction(
table, nArg, name, ref function, ref pClientData))
{
if (function != null)
{
string key = GetFunctionKey(nArg, name, function);
functions[key] = function;
callback = function.ScalarCallback;
return 1;
}
else
{
SetTableError(pVtab, "no function was created");
}
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return 0; /* NOTE: On any error, return "no such function". */
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xRename(
IntPtr pVtab,
IntPtr zNew
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
{
return Rename(table,
SQLiteString.StringFromUtf8IntPtr(zNew));
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xSavepoint(
IntPtr pVtab,
int iSavepoint
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
return Savepoint(table, iSavepoint);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xRelease(
IntPtr pVtab,
int iSavepoint
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
return Release(table, iSavepoint);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
///////////////////////////////////////////////////////////////////////
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
///
/// See the method.
///
private SQLiteErrorCode xRollbackTo(
IntPtr pVtab,
int iSavepoint
)
{
try
{
SQLiteVirtualTable table = TableFromIntPtr(pVtab);
if (table != null)
return RollbackTo(table, iSavepoint);
}
catch (Exception e) /* NOTE: Must catch ALL. */
{
SetTableError(pVtab, e.ToString());
}
return SQLiteErrorCode.Error;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region ISQLiteManagedModule Members
private bool declared;
///
/// Returns non-zero if the schema for the virtual table has been
/// declared.
///
public virtual bool Declared
{
get { CheckDisposed(); return declared; }
internal set { declared = value; }
}
///////////////////////////////////////////////////////////////////////
private string name;
///
/// Returns the name of the module as it was registered with the SQLite
/// core library.
///
public virtual string Name
{
get { CheckDisposed(); return name; }
}
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated with
/// the virtual table.
///
///
/// The native user-data pointer associated with this module, as it was
/// provided to the SQLite core library when the native module instance
/// was created.
///
///
/// The module name, database name, virtual table name, and all other
/// arguments passed to the CREATE VIRTUAL TABLE statement.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance associated with
/// the virtual table.
///
///
/// Upon failure, this parameter must be modified to contain an error
/// message.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Create(
SQLiteConnection connection,
IntPtr pClientData,
string[] arguments,
ref SQLiteVirtualTable table,
ref string error
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated with
/// the virtual table.
///
///
/// The native user-data pointer associated with this module, as it was
/// provided to the SQLite core library when the native module instance
/// was created.
///
///
/// The module name, database name, virtual table name, and all other
/// arguments passed to the CREATE VIRTUAL TABLE statement.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance associated with
/// the virtual table.
///
///
/// Upon failure, this parameter must be modified to contain an error
/// message.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Connect(
SQLiteConnection connection,
IntPtr pClientData,
string[] arguments,
ref SQLiteVirtualTable table,
ref string error
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The object instance containing all the
/// data for the inputs and outputs relating to index selection.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode BestIndex(
SQLiteVirtualTable table,
SQLiteIndex index
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Disconnect(
SQLiteVirtualTable table
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Destroy(
SQLiteVirtualTable table
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance associated
/// with the newly opened virtual table cursor.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Open(
SQLiteVirtualTable table,
ref SQLiteVirtualTableCursor cursor
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Close(
SQLiteVirtualTableCursor cursor
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// Number used to help identify the selected index.
///
///
/// String used to help identify the selected index.
///
///
/// The values corresponding to each column in the selected index.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Filter(
SQLiteVirtualTableCursor cursor,
int indexNumber,
string indexString,
SQLiteValue[] values
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Next(
SQLiteVirtualTableCursor cursor
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// Non-zero if no more rows are available; zero otherwise.
///
public abstract bool Eof(
SQLiteVirtualTableCursor cursor
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// The object instance to be used for
/// returning the specified column value to the SQLite core library.
///
///
/// The zero-based index corresponding to the column containing the
/// value to be returned.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Column(
SQLiteVirtualTableCursor cursor,
SQLiteContext context,
int index
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance
/// associated with the previously opened virtual table cursor to be
/// used.
///
///
/// Upon success, this parameter must be modified to contain the unique
/// integer row identifier for the current row for the specified cursor.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode RowId(
SQLiteVirtualTableCursor cursor,
ref long rowId
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The array of object instances containing
/// the new or modified column values, if any.
///
///
/// Upon success, this parameter must be modified to contain the unique
/// integer row identifier for the row that was inserted, if any.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Update(
SQLiteVirtualTable table,
SQLiteValue[] values,
ref long rowId
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Begin(
SQLiteVirtualTable table
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Sync(
SQLiteVirtualTable table
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Commit(
SQLiteVirtualTable table
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Rollback(
SQLiteVirtualTable table
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The number of arguments to the function being sought.
///
///
/// The name of the function being sought.
///
///
/// Upon success, this parameter must be modified to contain the
/// object instance responsible for
/// implementing the specified function.
///
///
/// Upon success, this parameter must be modified to contain the
/// native user-data pointer associated with
/// .
///
///
/// Non-zero if the specified function was found; zero otherwise.
///
public abstract bool FindFunction(
SQLiteVirtualTable table,
int argumentCount,
string name,
ref SQLiteFunction function,
ref IntPtr pClientData
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// The new name for the virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Rename(
SQLiteVirtualTable table,
string newName
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// This is an integer identifier under which the the current state of
/// the virtual table should be saved.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Savepoint(
SQLiteVirtualTable table,
int savepoint
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// This is an integer used to indicate that any saved states with an
/// identifier greater than or equal to this should be deleted by the
/// virtual table.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode Release(
SQLiteVirtualTable table,
int savepoint
);
///////////////////////////////////////////////////////////////////////
///
/// This method is called in response to the
/// method.
///
///
/// The object instance associated
/// with this virtual table.
///
///
/// This is an integer identifier used to specify a specific saved
/// state for the virtual table for it to restore itself back to, which
/// should also have the effect of deleting all saved states with an
/// integer identifier greater than this one.
///
///
/// A standard SQLite return code.
///
public abstract SQLiteErrorCode RollbackTo(
SQLiteVirtualTable table,
int savepoint
);
#endregion
///////////////////////////////////////////////////////////////////////
#region IDisposable Members
///
/// Disposes of this object instance.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
///////////////////////////////////////////////////////////////////////
#region IDisposable "Pattern" Members
private bool disposed;
///
/// Throws an if this object
/// instance has been disposed.
///
private void CheckDisposed() /* throw */
{
#if THROW_ON_DISPOSED
if (disposed)
{
throw new ObjectDisposedException(
typeof(SQLiteModule).Name);
}
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// Disposes of this object instance.
///
///
/// Non-zero if this method is being called from the
/// method. Zero if this method is being
/// called from the finalizer.
///
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
////////////////////////////////////
// dispose managed resources here...
////////////////////////////////////
if (functions != null)
functions.Clear();
}
//////////////////////////////////////
// release unmanaged resources here...
//////////////////////////////////////
try
{
if (disposableModule != IntPtr.Zero)
{
UnsafeNativeMethods.sqlite3_dispose_module(
disposableModule);
disposableModule = IntPtr.Zero;
}
}
catch (Exception e)
{
try
{
if (LogExceptionsNoThrow)
{
SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
String.Format(CultureInfo.CurrentCulture,
"Caught exception in \"Dispose\" method: {0}",
e)); /* throw */
}
}
catch
{
// do nothing.
}
}
#if PLATFORM_COMPACTFRAMEWORK
finally
{
if (pNativeModule != IntPtr.Zero)
{
SQLiteMemory.Free(pNativeModule);
pNativeModule = IntPtr.Zero;
}
}
#endif
disposed = true;
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Destructor
///
/// Finalizes this object instance.
///
~SQLiteModule()
{
Dispose(false);
}
#endregion
}
#endregion
}