///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </summary>
        /// <param name="table">
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </param>
        /// <param name="index">
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </param>
        /// <returns>
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </returns>
        public override SQLiteErrorCode BestIndex(
            SQLiteVirtualTable table,
            SQLiteIndex index
            )
        {
            CheckDisposed();

            return(GetMethodResultCode("BestIndex"));
        }
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </summary>
        /// <param name="table">
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </param>
        /// <param name="index">
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </param>
        /// <returns>
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </returns>
        public override SQLiteErrorCode BestIndex(
            SQLiteVirtualTable table,
            SQLiteIndex index
            )
        {
            CheckDisposed();

            if (!table.BestIndex(index))
            {
                SetTableError(table, String.Format(CultureInfo.CurrentCulture,
                                                   "failed to select best index for virtual table \"{0}\"",
                                                   table.TableName));

                return(SQLiteErrorCode.Error);
            }

            return(SQLiteErrorCode.Ok);
        }
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </summary>
        /// <param name="table">
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </param>
        /// <param name="index">
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </param>
        /// <returns>
        /// See the <see cref="ISQLiteManagedModule.BestIndex" /> method.
        /// </returns>
        public override SQLiteErrorCode BestIndex(
            SQLiteVirtualTable table,
            SQLiteIndex index
            )
        {
            CheckDisposed();

            return GetMethodResultCode("BestIndex");
        }
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// This method is called in response to the
        /// <see cref="ISQLiteNativeModule.xBestIndex" /> method.
        /// </summary>
        /// <param name="table">
        /// The <see cref="SQLiteVirtualTable" /> object instance associated
        /// with this virtual table.
        /// </param>
        /// <param name="index">
        /// The <see cref="SQLiteIndex" /> object instance containing all the
        /// data for the inputs and outputs relating to index selection.
        /// </param>
        /// <returns>
        /// A standard SQLite return code.
        /// </returns>
        public abstract SQLiteErrorCode BestIndex(
            SQLiteVirtualTable table,
            SQLiteIndex index
            );
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Modifies the specified <see cref="SQLiteIndex" /> object instance
        /// to contain the default estimated rows.
        /// </summary>
        /// <param name="index">
        /// The <see cref="SQLiteIndex" /> object instance to modify.
        /// </param>
        /// <returns>
        /// Non-zero upon success.
        /// </returns>
        protected virtual bool SetEstimatedRows(
            SQLiteIndex index
            )
        {
            return SetEstimatedRows(index, null);
        }
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Modifies the specified <see cref="SQLiteIndex" /> object instance
        /// to contain the specified estimated rows.
        /// </summary>
        /// <param name="index">
        /// The <see cref="SQLiteIndex" /> object instance to modify.
        /// </param>
        /// <param name="estimatedRows">
        /// The estimated rows value to use.  Using a null value means that the
        /// default value provided by the SQLite core library should be used.
        /// </param>
        /// <returns>
        /// Non-zero upon success.
        /// </returns>
        protected virtual bool SetEstimatedRows(
            SQLiteIndex index,
            long? estimatedRows
            )
        {
            if ((index == null) || (index.Outputs == null))
                return false;

            index.Outputs.EstimatedRows = estimatedRows;
            return true;
        }
        ///////////////////////////////////////////////////////////////////////

        #region Index Handling Helper Methods
        /// <summary>
        /// Modifies the specified <see cref="SQLiteIndex" /> object instance
        /// to contain the specified estimated cost.
        /// </summary>
        /// <param name="index">
        /// The <see cref="SQLiteIndex" /> object instance to modify.
        /// </param>
        /// <param name="estimatedCost">
        /// The estimated cost value to use.  Using a null value means that the
        /// default value provided by the SQLite core library should be used.
        /// </param>
        /// <returns>
        /// Non-zero upon success.
        /// </returns>
        protected virtual bool SetEstimatedCost(
            SQLiteIndex index,
            double? estimatedCost
            )
        {
            if ((index == null) || (index.Outputs == null))
                return false;

            index.Outputs.EstimatedCost = estimatedCost;
            return true;
        }
        ///////////////////////////////////////////////////////////////////////

        #region Public Methods
        /// <summary>
        /// This method should normally be used by the
        /// <see cref="ISQLiteManagedModule.BestIndex" /> method in order to
        /// perform index selection based on the constraints provided by the
        /// SQLite core library.
        /// </summary>
        /// <param name="index">
        /// The <see cref="SQLiteIndex" /> object instance containing all the
        /// data for the inputs and outputs relating to index selection.
        /// </param>
        /// <returns>
        /// Non-zero upon success.
        /// </returns>
        public virtual bool BestIndex(
            SQLiteIndex index
            )
        {
            CheckDisposed();

            this.index = index;

            return true;
        }
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Populates the outputs of a pre-allocated native sqlite3_index_info
        /// structure using an existing <see cref="SQLiteIndex" /> object
        /// instance.
        /// </summary>
        /// <param name="index">
        /// The existing <see cref="SQLiteIndex" /> object instance containing
        /// the output data to use.
        /// </param>
        /// <param name="pIndex">
        /// The native pointer to the pre-allocated native sqlite3_index_info
        /// structure.
        /// </param>
        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));

            if (index.Outputs.EstimatedCost.HasValue)
            {
                SQLiteMarshal.WriteDouble(pIndex, offset,
                    index.Outputs.EstimatedCost.GetValueOrDefault());
            }

            if (index.Outputs.CanUseEstimatedRows() &&
                index.Outputs.EstimatedRows.HasValue)
            {
                SQLiteMarshal.WriteInt64(pIndex, offset,
                    index.Outputs.EstimatedRows.GetValueOrDefault());
            }
        }
        ///////////////////////////////////////////////////////////////////////

        #region Internal Marshal Helper Methods
        /// <summary>
        /// Converts a native pointer to a native sqlite3_index_info structure
        /// into a new <see cref="SQLiteIndex" /> object instance.
        /// </summary>
        /// <param name="pIndex">
        /// The native pointer to the native sqlite3_index_info structure to
        /// convert.
        /// </param>
        /// <param name="index">
        /// Upon success, this parameter will be modified to contain the newly
        /// created <see cref="SQLiteIndex" /> object instance.
        /// </param>
        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);
            }
        }
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Modifies the specified <see cref="SQLiteIndex" /> object instance
        /// to contain the default estimated cost.
        /// </summary>
        /// <param name="index">
        /// The <see cref="SQLiteIndex" /> object instance to modify.
        /// </param>
        /// <returns>
        /// Non-zero upon success.
        /// </returns>
        protected virtual bool SetEstimatedCost(
            SQLiteIndex index
            )
        {
            return SetEstimatedCost(index, SQLiteIndex.DefaultEstimatedCost);
        }