Index: Doc/Extra/Provider/version.html
==================================================================
--- Doc/Extra/Provider/version.html
+++ Doc/Extra/Provider/version.html
@@ -44,10 +44,12 @@
Version History
1.0.99.0 - December XX, 2015 (release scheduled)
- Updated to SQLite 3.9.2.
+ - Add preliminary support for the .NET Framework 4.6.1.
+ - Fix virtual table handling of sqlite3_index_info members not available with older versions of the SQLite core library. ** Potentially Incompatible Change **
- Update and improve documentation comments for the native virtual table methods.
- Permit an existing registered function to be replaced. Fix for [2556655d1b].
- Make GetValue work for boolean columns with textual "True" and "False" values. Fix for [7714b60d61].
- Add Reset method to the SQLiteCommand class.
- Add FileName property to the SQLiteConnection class.
Index: System.Data.SQLite/SQLiteModule.cs
==================================================================
--- System.Data.SQLite/SQLiteModule.cs
+++ System.Data.SQLite/SQLiteModule.cs
@@ -1043,14 +1043,14 @@
///
/// Determines if the native flags field can be used, based on the
/// available version of the SQLite core library.
///
///
- /// Non-zero if the property is supported by
+ /// Non-zero if the property is supported by
/// the SQLite core library.
///
- public bool CanUseIdxFlags()
+ public bool CanUseIndexFlags()
{
if (UnsafeNativeMethods.sqlite3_libversion_number() >= 3009000)
return true;
return false;
@@ -1061,14 +1061,14 @@
///
/// Determines if the native flags field can be used, based on the
/// available version of the SQLite core library.
///
///
- /// Non-zero if the property is supported by
+ /// Non-zero if the property is supported by
/// the SQLite core library.
///
- public bool CanUseColUsed()
+ public bool CanUseColumnsUsed()
{
if (UnsafeNativeMethods.sqlite3_libversion_number() >= 3010000)
return true;
return false;
@@ -1169,45 +1169,51 @@
set { estimatedRows = value; }
}
///////////////////////////////////////////////////////////////////////
- private SQLiteIndexFlags? idxFlags;
+ private SQLiteIndexFlags? indexFlags;
///
/// The flags that should be used with this index. Using a null value
/// here indicates that a default flags value should be used. This
/// property has no effect if the SQLite core library is not at least
/// version 3.9.0.
///
- public SQLiteIndexFlags? IdxFlags
+ public SQLiteIndexFlags? IndexFlags
{
- get { return idxFlags; }
- set { idxFlags = value; }
+ get { return indexFlags; }
+ set { indexFlags = value; }
}
///////////////////////////////////////////////////////////////////////
- private long? colUsed;
+ private long? columnsUsed;
///
- /// The colUsed field indicates which columns of the virtual table
- /// may be required by the current scan. Virtual table columns are
- /// numbered from zero in the order in which they appear within the
- /// CREATE TABLE statement passed to sqlite3_declare_vtab(). For the
- /// first 63 columns (columns 0-62), the corresponding bit is set
- /// within the colUsed mask if the column may be required by SQLite.
- /// If the table has at least 64 columns and any column to the right
- /// of the first 63 is required, then bit 63 of colUsed is also set.
- /// In other words, column iCol may be required if the expression
- /// (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+ ///
+ /// Indicates which columns of the virtual table may be required by the
+ /// current scan. Virtual table columns are numbered from zero in the
+ /// order in which they appear within the CREATE TABLE statement passed
+ /// to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62),
+ /// the corresponding bit is set within the bit mask if the column may
+ /// be required by SQLite. If the table has at least 64 columns and
+ /// any column to the right of the first 63 is required, then bit 63 of
+ /// colUsed is also set. In other words, column iCol may be required
+ /// if the expression
+ ///
+ ///
+ /// (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+ ///
+ ///
/// evaluates to non-zero. Using a null value here indicates that a
- /// default flags value should be used. This property has no effect
- /// if the SQLite core library is not at least version 3.10.0.
+ /// default flags value should be used. This property has no effect if
+ /// the SQLite core library is not at least version 3.10.0.
+ ///
///
- public long? ColUsed
+ public long? ColumnsUsed
{
- get { return colUsed; }
- set { colUsed = value; }
+ get { return columnsUsed; }
+ set { columnsUsed = value; }
}
#endregion
}
#endregion
@@ -1243,86 +1249,454 @@
}
#endregion
///////////////////////////////////////////////////////////////////////
+ #region Private Marshal Helper Methods (For Test Use Only)
+ ///
+ /// Attempts to determine the structure sizes needed to create and
+ /// populate a native
+ ///
+ /// structure.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ private static void SizeOfNative(
+ out int sizeOfInfoType,
+ out int sizeOfConstraintType,
+ out int sizeOfOrderByType,
+ out int sizeOfConstraintUsageType
+ )
+ {
+ sizeOfInfoType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_info));
+
+ sizeOfConstraintType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint));
+
+ sizeOfOrderByType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_orderby));
+
+ sizeOfConstraintUsageType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint_usage));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to allocate and initialize a native
+ ///
+ /// structure.
+ ///
+ ///
+ /// The number of instances to
+ /// pre-allocate space for.
+ ///
+ ///
+ /// The number of instances to
+ /// pre-allocate space for.
+ ///
+ ///
+ /// The newly allocated native
+ /// structure
+ /// -OR- if it could not be fully allocated.
+ ///
+ private static IntPtr AllocateAndInitializeNative(
+ int nConstraint,
+ int nOrderBy
+ )
+ {
+ IntPtr pIndex = IntPtr.Zero;
+ IntPtr pInfo = IntPtr.Zero;
+ IntPtr pConstraint = IntPtr.Zero;
+ IntPtr pOrderBy = IntPtr.Zero;
+ IntPtr pConstraintUsage = IntPtr.Zero;
+
+ try
+ {
+ int sizeOfInfoType;
+ int sizeOfOrderByType;
+ int sizeOfConstraintType;
+ int sizeOfConstraintUsageType;
+
+ SizeOfNative(out sizeOfInfoType, out sizeOfConstraintType,
+ out sizeOfOrderByType, out sizeOfConstraintUsageType);
+
+ if ((sizeOfInfoType > 0) &&
+ (sizeOfConstraintType > 0) &&
+ (sizeOfOrderByType > 0) &&
+ (sizeOfConstraintUsageType > 0))
+ {
+ pInfo = SQLiteMemory.Allocate(sizeOfInfoType);
+
+ pConstraint = SQLiteMemory.Allocate(
+ sizeOfConstraintType * nConstraint);
+
+ pOrderBy = SQLiteMemory.Allocate(
+ sizeOfOrderByType * nOrderBy);
+
+ pConstraintUsage = SQLiteMemory.Allocate(
+ sizeOfConstraintUsageType * nConstraint);
+
+ if ((pInfo != IntPtr.Zero) &&
+ (pConstraint != IntPtr.Zero) &&
+ (pOrderBy != IntPtr.Zero) &&
+ (pConstraintUsage != IntPtr.Zero))
+ {
+ int offset = 0;
+
+ SQLiteMarshal.WriteInt32(
+ pInfo, offset, nConstraint);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(
+ pInfo, offset, pConstraint);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ SQLiteMarshal.WriteInt32(
+ pInfo, offset, nOrderBy);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(
+ pInfo, offset, pOrderBy);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(
+ pInfo, offset, pConstraintUsage);
+
+ pIndex = pInfo; /* NOTE: Success. */
+ }
+ }
+ }
+ finally
+ {
+ if (pIndex == IntPtr.Zero) /* NOTE: Failure? */
+ {
+ if (pConstraintUsage != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraintUsage);
+ pConstraintUsage = IntPtr.Zero;
+ }
+
+ if (pOrderBy != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pOrderBy);
+ pOrderBy = IntPtr.Zero;
+ }
+
+ if (pConstraint != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraint);
+ pConstraint = IntPtr.Zero;
+ }
+
+ if (pInfo != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pInfo);
+ pInfo = IntPtr.Zero;
+ }
+ }
+ }
+
+ return pIndex;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Frees all the memory associated with a native
+ ///
+ /// structure.
+ ///
+ ///
+ /// The native pointer to the native sqlite3_index_info structure to
+ /// free.
+ ///
+ private static void FreeNative(
+ IntPtr pIndex
+ )
+ {
+ if (pIndex == IntPtr.Zero)
+ return;
+
+ int offset = 0;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ IntPtr pConstraint = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ IntPtr pOrderBy = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ IntPtr pConstraintUsage = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ if (pConstraintUsage != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraintUsage);
+ pConstraintUsage = IntPtr.Zero;
+ }
+
+ if (pOrderBy != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pOrderBy);
+ pOrderBy = IntPtr.Zero;
+ }
+
+ if (pConstraint != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraint);
+ pConstraint = IntPtr.Zero;
+ }
+
+ if (pIndex != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pIndex);
+ pIndex = IntPtr.Zero;
+ }
+ }
+ #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.
///
+ ///
+ /// Non-zero to include fields from the outputs portion of the native
+ /// structure; otherwise, the "output" fields will not be read.
+ ///
///
/// Upon success, this parameter will be modified to contain the newly
/// created object instance.
///
internal static void FromIntPtr(
IntPtr pIndex,
+ bool includeOutput,
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);
+ 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);
+
+ IntPtr pConstraintUsage = IntPtr.Zero;
+
+ if (includeOutput)
+ {
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ pConstraintUsage = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+ }
index = new SQLiteIndex(nConstraint, nOrderBy);
+ SQLiteIndexInputs inputs = index.Inputs;
- Type indexConstraintType = typeof(
+ if (inputs == null)
+ return;
+
+ SQLiteIndexConstraint[] constraints = inputs.Constraints;
+
+ if (constraints == null)
+ return;
+
+ SQLiteIndexOrderBy[] orderBys = inputs.OrderBys;
+
+ if (orderBys == null)
+ return;
+
+ Type constraintType = typeof(
UnsafeNativeMethods.sqlite3_index_constraint);
- int sizeOfConstraintType = Marshal.SizeOf(indexConstraintType);
+ int sizeOfConstraintType = Marshal.SizeOf(
+ constraintType);
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);
+ Marshal.PtrToStructure(pOffset, constraintType);
- index.Inputs.Constraints[iConstraint] =
- new SQLiteIndexConstraint(constraint);
+ constraints[iConstraint] = new SQLiteIndexConstraint(
+ constraint);
}
- Type indexOrderByType = typeof(
+ Type orderByType = typeof(
UnsafeNativeMethods.sqlite3_index_orderby);
- int sizeOfOrderByType = Marshal.SizeOf(indexOrderByType);
+ int sizeOfOrderByType = Marshal.SizeOf(orderByType);
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);
+ Marshal.PtrToStructure(pOffset, orderByType);
- index.Inputs.OrderBys[iOrderBy] =
- new SQLiteIndexOrderBy(orderBy);
+ orderBys[iOrderBy] = new SQLiteIndexOrderBy(orderBy);
+ }
+
+ if (includeOutput)
+ {
+ SQLiteIndexOutputs outputs = index.Outputs;
+
+ if (outputs == null)
+ return;
+
+ SQLiteIndexConstraintUsage[] constraintUsages =
+ outputs.ConstraintUsages;
+
+ if (constraintUsages == null)
+ return;
+
+ Type constraintUsageType = typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint_usage);
+
+ int sizeOfConstraintUsageType = Marshal.SizeOf(
+ constraintUsageType);
+
+ for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
+ {
+ IntPtr pOffset = SQLiteMarshal.IntPtrForOffset(
+ pConstraintUsage, iConstraint * sizeOfConstraintUsageType);
+
+ UnsafeNativeMethods.sqlite3_index_constraint_usage constraintUsage =
+ (UnsafeNativeMethods.sqlite3_index_constraint_usage)
+ Marshal.PtrToStructure(pOffset, constraintUsageType);
+
+ constraintUsages[iConstraint] = new SQLiteIndexConstraintUsage(
+ constraintUsage);
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ outputs.IndexNumber = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ outputs.IndexString = SQLiteString.StringFromUtf8IntPtr(
+ SQLiteMarshal.ReadIntPtr(pIndex, offset));
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ outputs.NeedToFreeIndexString = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(int));
+
+ outputs.OrderByConsumed = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(double));
+
+ outputs.EstimatedCost = SQLiteMarshal.ReadDouble(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(double), sizeof(long));
+
+ if (outputs.CanUseEstimatedRows())
+ {
+ outputs.EstimatedRows = SQLiteMarshal.ReadInt64(
+ pIndex, offset);
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(long), sizeof(int));
+
+ if (outputs.CanUseIndexFlags())
+ {
+ outputs.IndexFlags = (SQLiteIndexFlags)
+ SQLiteMarshal.ReadInt32(pIndex, offset);
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(long));
+
+ if (outputs.CanUseColumnsUsed())
+ {
+ outputs.ColumnsUsed = SQLiteMarshal.ReadInt64(
+ pIndex, offset);
+ }
}
}
///////////////////////////////////////////////////////////////////////
@@ -1337,129 +1711,216 @@
///
///
/// The native pointer to the pre-allocated native sqlite3_index_info
/// structure.
///
+ ///
+ /// Non-zero to include fields from the inputs portion of the native
+ /// structure; otherwise, the "input" fields will not be written.
+ ///
internal static void ToIntPtr(
SQLiteIndex index,
- IntPtr pIndex
+ IntPtr pIndex,
+ bool includeInput
)
{
- if ((index == null) || (index.Inputs == null) ||
- (index.Inputs.Constraints == null) ||
- (index.Outputs == null) ||
- (index.Outputs.ConstraintUsages == null))
- {
+ if (index == null)
+ return;
+
+ SQLiteIndexOutputs outputs = index.Outputs;
+
+ if (outputs == null)
+ return;
+
+ SQLiteIndexConstraintUsage[] constraintUsages =
+ outputs.ConstraintUsages;
+
+ if (constraintUsages == null)
return;
+
+ SQLiteIndexInputs inputs = null;
+ SQLiteIndexConstraint[] constraints = null;
+ SQLiteIndexOrderBy[] orderBys = null;
+
+ if (includeInput)
+ {
+ inputs = index.Inputs;
+
+ if (inputs == null)
+ return;
+
+ constraints = inputs.Constraints;
+
+ if (constraints == null)
+ return;
+
+ orderBys = inputs.OrderBys;
+
+ if (orderBys == 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);
+ if (includeInput && (nConstraint != constraints.Length))
+ return;
+
+ if (nConstraint != constraintUsages.Length)
+ return;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ if (includeInput)
+ {
+ IntPtr pConstraint = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ int sizeOfConstraintType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint));
+
+ for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
+ {
+ UnsafeNativeMethods.sqlite3_index_constraint constraint =
+ new UnsafeNativeMethods.sqlite3_index_constraint(
+ constraints[iConstraint]);
+
+ Marshal.StructureToPtr(
+ constraint, SQLiteMarshal.IntPtrForOffset(
+ pConstraint, iConstraint * sizeOfConstraintType),
+ false);
+ }
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ int nOrderBy = includeInput ?
+ SQLiteMarshal.ReadInt32(pIndex, offset) : 0;
+
+ if (includeInput && (nOrderBy != orderBys.Length))
+ return;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ if (includeInput)
+ {
+ IntPtr pOrderBy = SQLiteMarshal.ReadIntPtr(pIndex, offset);
+
+ int sizeOfOrderByType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_orderby));
+
+ for (int iOrderBy = 0; iOrderBy < nOrderBy; iOrderBy++)
+ {
+ UnsafeNativeMethods.sqlite3_index_orderby orderBy =
+ new UnsafeNativeMethods.sqlite3_index_orderby(
+ orderBys[iOrderBy]);
+
+ Marshal.StructureToPtr(
+ orderBy, SQLiteMarshal.IntPtrForOffset(
+ pOrderBy, iOrderBy * sizeOfOrderByType),
+ false);
+ }
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ 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]);
+ constraintUsages[iConstraint]);
Marshal.StructureToPtr(
constraintUsage, SQLiteMarshal.IntPtrForOffset(
pConstraintUsage, iConstraint * sizeOfConstraintUsageType),
false);
}
- offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
- sizeof(int));
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
SQLiteMarshal.WriteInt32(pIndex, offset,
- index.Outputs.IndexNumber);
+ outputs.IndexNumber);
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
- IntPtr.Size);
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
SQLiteMarshal.WriteIntPtr(pIndex, offset,
- SQLiteString.Utf8IntPtrFromString(index.Outputs.IndexString));
+ SQLiteString.Utf8IntPtrFromString(outputs.IndexString));
- offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
- sizeof(int));
+ 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.
+ // need to set make sure the NeedToFreeIndexString field
+ // is non-zero; however, we are not picky about the exact
+ // value.
//
- SQLiteMarshal.WriteInt32(pIndex, offset, 1);
+ int needToFreeIndexString = outputs.NeedToFreeIndexString != 0 ?
+ outputs.NeedToFreeIndexString : 1;
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
- sizeof(int));
+ SQLiteMarshal.WriteInt32(pIndex, offset,
+ needToFreeIndexString);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(int));
SQLiteMarshal.WriteInt32(pIndex, offset,
- index.Outputs.OrderByConsumed);
+ outputs.OrderByConsumed);
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
- sizeof(double));
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(double));
- if (index.Outputs.EstimatedCost.HasValue)
+ if (outputs.EstimatedCost.HasValue)
{
SQLiteMarshal.WriteDouble(pIndex, offset,
- index.Outputs.EstimatedCost.GetValueOrDefault());
+ outputs.EstimatedCost.GetValueOrDefault());
}
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(double),
- sizeof(long));
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(double), sizeof(long));
- if (index.Outputs.CanUseEstimatedRows() &&
- index.Outputs.EstimatedRows.HasValue)
+ if (outputs.CanUseEstimatedRows() &&
+ outputs.EstimatedRows.HasValue)
{
SQLiteMarshal.WriteInt64(pIndex, offset,
- index.Outputs.EstimatedRows.GetValueOrDefault());
+ outputs.EstimatedRows.GetValueOrDefault());
}
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(long),
- sizeof(int));
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(long), sizeof(int));
- if (index.Outputs.CanUseIdxFlags() &&
- index.Outputs.IdxFlags.HasValue)
+ if (outputs.CanUseIndexFlags() &&
+ outputs.IndexFlags.HasValue)
{
SQLiteMarshal.WriteInt32(pIndex, offset,
- (int)index.Outputs.IdxFlags.GetValueOrDefault());
+ (int)outputs.IndexFlags.GetValueOrDefault());
}
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
- sizeof(long));
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(long));
- if (index.Outputs.CanUseColUsed() &&
- index.Outputs.ColUsed.HasValue)
+ if (outputs.CanUseColumnsUsed() &&
+ outputs.ColumnsUsed.HasValue)
{
SQLiteMarshal.WriteInt64(pIndex, offset,
- index.Outputs.ColUsed.GetValueOrDefault());
+ outputs.ColumnsUsed.GetValueOrDefault());
}
}
#endregion
///////////////////////////////////////////////////////////////////////
@@ -3279,10 +3740,39 @@
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
+ /// value to be read is located.
+ ///
+ ///
+ /// The value at the specified memory location.
+ ///
+ public static long ReadInt64(
+ IntPtr pointer,
+ int offset
+ )
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return Marshal.ReadInt64(pointer, offset);
+#else
+ return Marshal.ReadInt64(IntPtrForOffset(pointer, offset));
+#endif
+ }
///////////////////////////////////////////////////////////////////////
///
/// Reads a value from the specified memory
@@ -5141,15 +5631,15 @@
if (pVtab == IntPtr.Zero)
return false;
int offset = 0;
- offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
- sizeof(int));
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
- IntPtr.Size);
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
IntPtr pOldError = SQLiteMarshal.ReadIntPtr(pVtab, offset);
if (pOldError != IntPtr.Zero)
{
@@ -5419,17 +5909,17 @@
int offset = 0;
SQLiteMarshal.WriteIntPtr(pVtab, offset, IntPtr.Zero);
- offset = SQLiteMarshal.NextOffsetOf(offset, IntPtr.Size,
- sizeof(int));
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
SQLiteMarshal.WriteInt32(pVtab, offset, 0);
- offset = SQLiteMarshal.NextOffsetOf(offset, sizeof(int),
- IntPtr.Size);
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
SQLiteMarshal.WriteIntPtr(pVtab, offset, IntPtr.Zero);
}
///////////////////////////////////////////////////////////////////////
@@ -6081,26 +6571,26 @@
/// to contain the specified flags.
///
///
/// The object instance to modify.
///
- ///
+ ///
/// The index flags value to use. Using a null value means that the
/// default value provided by the SQLite core library should be used.
///
///
/// Non-zero upon success.
///
- protected virtual bool SetIdxFlags(
+ protected virtual bool SetIndexFlags(
SQLiteIndex index,
- SQLiteIndexFlags? idxFlags
+ SQLiteIndexFlags? indexFlags
)
{
if ((index == null) || (index.Outputs == null))
return false;
- index.Outputs.IdxFlags = idxFlags;
+ index.Outputs.IndexFlags = indexFlags;
return true;
}
///////////////////////////////////////////////////////////////////////
@@ -6112,15 +6602,15 @@
/// The object instance to modify.
///
///
/// Non-zero upon success.
///
- protected virtual bool SetIdxFlags(
+ protected virtual bool SetIndexFlags(
SQLiteIndex index
)
{
- return SetIdxFlags(index, null);
+ return SetIndexFlags(index, null);
}
#endregion
#endregion
///////////////////////////////////////////////////////////////////////
@@ -6257,15 +6747,15 @@
if (table != null)
{
SQLiteIndex index = null;
- SQLiteIndex.FromIntPtr(pIndex, ref index);
+ SQLiteIndex.FromIntPtr(pIndex, true, ref index);
if (BestIndex(table, index) == SQLiteErrorCode.Ok)
{
- SQLiteIndex.ToIntPtr(index, pIndex);
+ SQLiteIndex.ToIntPtr(index, pIndex, true);
return SQLiteErrorCode.Ok;
}
}
}
catch (Exception e) /* NOTE: Must catch ALL. */
Index: System.Data.SQLite/UnsafeNativeMethods.cs
==================================================================
--- System.Data.SQLite/UnsafeNativeMethods.cs
+++ System.Data.SQLite/UnsafeNativeMethods.cs
@@ -2853,19 +2853,22 @@
internal struct sqlite3_index_info
{
/* Inputs */
public int nConstraint; /* Number of entries in aConstraint */
public IntPtr aConstraint;
- public int nOrderBy;
+ public int nOrderBy; /* Number of entries in aOrderBy */
public IntPtr aOrderBy;
/* Outputs */
public IntPtr aConstraintUsage;
public int idxNum; /* Number used to identify the index */
public string idxStr; /* String, possibly obtained from sqlite3_malloc */
public int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
public int orderByConsumed; /* True if output is already ordered */
public double estimatedCost; /* Estimated cost of using this index */
+ public long estimatedRows; /* Estimated number of rows returned */
+ public SQLiteIndexFlags idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ public long colUsed; /* Input: Mask of columns used by statement */
}
#endif
#endregion
}
Index: Tests/vtab.eagle
==================================================================
--- Tests/vtab.eagle
+++ Tests/vtab.eagle
@@ -1773,10 +1773,214 @@
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite defineConstant.System.Data.SQLite.INTEROP_VIRTUAL_TABLE\
compileCSharp} -match regexp -result [string map [list \n \r\n] {^Ok\
System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{-?\d+ one -?\d+ two -?\d+\
three -?\d+ 4 -?\d+ 5\.0\}$}]}
+
+###############################################################################
+
+runTest {test vtab-1.12 {SQLiteIndex managed-to-native-to-managed} -setup {
+ set nConstraint 3; set nOrderBy 3
+} -body {
+ set index(1) [object create -alias -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndex $nConstraint $nOrderBy]
+
+ for {set iConstraint 0} {$iConstraint < $nConstraint} {incr iConstraint} {
+ set constraint(1) [object create -alias -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndexConstraint 0 0 0 0]
+
+ $index(1) Inputs.Constraints.SetValue $constraint(1) $iConstraint
+
+ $constraint(1) iColumn [expr {int(0x01234567 + $iConstraint)}]
+ $constraint(1) op [expr {0xFF - $iConstraint}]
+ $constraint(1) usable [expr {0xCC + $iConstraint}]
+ $constraint(1) iTermOffset [expr {int(0x89ABCDEF + $iConstraint)}]
+ }
+
+ for {set iOrderBy 0} {$iOrderBy < $nOrderBy} {incr iOrderBy} {
+ set orderBy(1) [object create -alias -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndexOrderBy 0 0]
+
+ $index(1) Inputs.OrderBys.SetValue $orderBy(1) $iOrderBy
+
+ $orderBy(1) iColumn [expr {int(0x23016745 + $iOrderBy)}]
+ $orderBy(1) desc [expr {0xFF - $iOrderBy}]
+ }
+
+ for {set iConstraint 0} {$iConstraint < $nConstraint} {incr iConstraint} {
+ set constraintUsage(1) [object create -alias -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndexConstraintUsage 0 0]
+
+ $index(1) Outputs.ConstraintUsages.SetValue $constraintUsage(1) \
+ $iConstraint
+
+ $constraintUsage(1) argvIndex [expr {int(0xAB89EFCD + $iConstraint)}]
+ $constraintUsage(1) omit [expr {0xCC + $iConstraint}]
+ }
+
+ $index(1) Outputs.IndexNumber [expr {int(0xAAAAAAAA)}]
+ $index(1) Outputs.IndexString "\x01test index string.\xFF"
+ $index(1) Outputs.NeedToFreeIndexString [expr {int(0x55555555)}]
+ $index(1) Outputs.OrderByConsumed [expr {int(0x33333333)}]
+ $index(1) Outputs.EstimatedCost 1.0
+
+ if {[$index(1) Outputs.CanUseEstimatedRows]} then {
+ $index(1) Outputs.EstimatedRows [expr {int(0xCCCCCCCC)}]
+ }
+
+ if {[$index(1) Outputs.CanUseIndexFlags]} then {
+ $index(1) Outputs.IndexFlags [expr {int(0xEEEEEEEE)}]
+ }
+
+ if {[$index(1) Outputs.CanUseColumnsUsed]} then {
+ $index(1) Outputs.ColumnsUsed [expr {wide(0xBADC0FFEE875621A)}]
+ }
+
+ set pIndex(1) [object invoke -create -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndex AllocateAndInitializeNative \
+ $nConstraint $nOrderBy]
+
+ object invoke -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndex ToIntPtr $index(1) $pIndex(1) true
+
+ set index(2) [object create -alias -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndex $nConstraint $nOrderBy]
+
+ object invoke -alias -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndex FromIntPtr $pIndex(1) true index(2)
+
+ for {set iConstraint 0} {$iConstraint < $nConstraint} {incr iConstraint} {
+ set constraint(1) [$index(1) \
+ -alias Inputs.Constraints.GetValue $iConstraint]
+
+ set constraint(2) [$index(2) \
+ -alias Inputs.Constraints.GetValue $iConstraint]
+
+ if {[$constraint(1) iColumn] != [$constraint(2) iColumn]} then {
+ error [appendArgs \
+ "iColumn at index " $iConstraint " does not match"]
+ }
+
+ if {[$constraint(1) op] != [$constraint(2) op]} then {
+ error [appendArgs \
+ "op at index " $iConstraint " does not match"]
+ }
+
+ if {[$constraint(1) usable] != [$constraint(2) usable]} then {
+ error [appendArgs \
+ "usable at index " $iConstraint " does not match"]
+ }
+
+ if {[$constraint(1) iTermOffset] != [$constraint(2) iTermOffset]} then {
+ error [appendArgs \
+ "iTermOffset at index " $iConstraint " does not match"]
+ }
+ }
+
+ for {set iOrderBy 0} {$iOrderBy < $nOrderBy} {incr iOrderBy} {
+ set orderBy(1) [$index(1) \
+ -alias Inputs.OrderBys.GetValue $iOrderBy]
+
+ set orderBy(2) [$index(2) \
+ -alias Inputs.OrderBys.GetValue $iOrderBy]
+
+ if {[$orderBy(1) iColumn] != [$orderBy(2) iColumn]} then {
+ error [appendArgs \
+ "iColumn at index " $iOrderBy " does not match"]
+ }
+
+ if {[$orderBy(1) desc] != [$orderBy(2) desc]} then {
+ error [appendArgs \
+ "desc at index " $iOrderBy " does not match"]
+ }
+ }
+
+ for {set iConstraint 0} {$iConstraint < $nConstraint} {incr iConstraint} {
+ set constraintUsage(1) [$index(1) \
+ -alias Outputs.ConstraintUsages.GetValue $iConstraint]
+
+ set constraintUsage(2) [$index(2) \
+ -alias Outputs.ConstraintUsages.GetValue $iConstraint]
+
+ if {[$constraintUsage(1) argvIndex] != \
+ [$constraintUsage(2) argvIndex]} then {
+ error [appendArgs \
+ "argvIndex at index " $iConstraint " does not match"]
+ }
+
+ if {[$constraintUsage(1) omit] != [$constraintUsage(2) omit]} then {
+ error [appendArgs \
+ "omit at index " $iConstraint " does not match"]
+ }
+ }
+
+ if {[$index(1) Outputs.IndexNumber] != \
+ [$index(2) Outputs.IndexNumber]} then {
+ error "IndexNumber does not match"
+ }
+
+ if {[$index(1) Outputs.IndexString] ne \
+ [$index(2) Outputs.IndexString]} then {
+ error "IndexString does not match"
+ }
+
+ if {[$index(1) Outputs.NeedToFreeIndexString] != \
+ [$index(2) Outputs.NeedToFreeIndexString]} then {
+ error "NeedToFreeIndexString does not match"
+ }
+
+ if {[$index(1) Outputs.OrderByConsumed] != \
+ [$index(2) Outputs.OrderByConsumed]} then {
+ error "OrderByConsumed does not match"
+ }
+
+ if {[$index(1) Outputs.EstimatedCost] != \
+ [$index(2) Outputs.EstimatedCost]} then {
+ error "EstimatedCost does not match"
+ }
+
+ if {[$index(1) Outputs.CanUseEstimatedRows] && \
+ [$index(2) Outputs.CanUseEstimatedRows]} then {
+ if {[$index(1) Outputs.EstimatedRows] != \
+ [$index(2) Outputs.EstimatedRows]} then {
+ error "EstimatedRows does not match"
+ }
+
+ tputs $test_channel "---- checked EstimatedRows property\n"
+ }
+
+ if {[$index(1) Outputs.CanUseIndexFlags] && \
+ [$index(2) Outputs.CanUseIndexFlags]} then {
+ if {[$index(1) Outputs.IndexFlags] != \
+ [$index(2) Outputs.IndexFlags]} then {
+ error "IndexFlags does not match"
+ }
+
+ tputs $test_channel "---- checked IndexFlags property\n"
+ }
+
+ if {[$index(1) Outputs.CanUseColumnsUsed] && \
+ [$index(2) Outputs.CanUseColumnsUsed]} then {
+ if {[$index(1) Outputs.ColumnsUsed] != \
+ [$index(2) Outputs.ColumnsUsed]} then {
+ error "ColumnsUsed does not match"
+ }
+
+ tputs $test_channel "---- checked ColumnsUsed property\n"
+ }
+} -cleanup {
+ catch {
+ object invoke -flags +NonPublic \
+ System.Data.SQLite.SQLiteIndex FreeNative $pIndex(1)
+ }
+
+ unset -nocomplain constraintUsage
+ unset -nocomplain orderBy nOrderBy iOrderBy
+ unset -nocomplain constraint nConstraint iConstraint
+ unset -nocomplain pIndex index
+} -constraints {eagle command.object System.Data.SQLite\
+defineConstant.System.Data.SQLite.INTEROP_VIRTUAL_TABLE} -result {}}
###############################################################################
runSQLiteTestEpilogue
runTestEpilogue
Index: readme.htm
==================================================================
--- readme.htm
+++ readme.htm
@@ -211,10 +211,12 @@
1.0.99.0 - December XX, 2015 (release scheduled)
- Updated to SQLite 3.9.2.
+ - Add preliminary support for the .NET Framework 4.6.1.
+ - Fix virtual table handling of sqlite3_index_info members not available with older versions of the SQLite core library. ** Potentially Incompatible Change **
- Update and improve documentation comments for the native virtual table methods.
- Permit an existing registered function to be replaced. Fix for [2556655d1b].
- Make GetValue work for boolean columns with textual "True" and "False" values. Fix for [7714b60d61].
- Add Reset method to the SQLiteCommand class.
- Add FileName property to the SQLiteConnection class.
Index: www/news.wiki
==================================================================
--- www/news.wiki
+++ www/news.wiki
@@ -5,10 +5,12 @@
1.0.99.0 - December XX, 2015 (release scheduled)
- Updated to [https://www.sqlite.org/releaselog/3_9_2.html|SQLite 3.9.2].
+ - Add preliminary support for the .NET Framework 4.6.1.
+ - Fix virtual table handling of sqlite3_index_info members not available with older versions of the SQLite core library. ** Potentially Incompatible Change **
- Update and improve documentation comments for the native virtual table methods.
- Permit an existing registered function to be replaced. Fix for [2556655d1b].
- Make GetValue work for boolean columns with textual "True" and "False" values. Fix for [7714b60d61].
- Add Reset method to the SQLiteCommand class.
- Add FileName property to the SQLiteConnection class.