/* ===================================================================================== SQL DATA HANDLING */

        private static async Task <TimestampData> GetTimestampDataForOneNodeIntegrityCheckAsync(string path, int[] excludedNodeTypeIds)
        {
            var checkNodeSql = "SELECT N.NodeId, V.VersionId, CONVERT(bigint, n.timestamp) NodeTimestamp, CONVERT(bigint, v.timestamp) VersionTimestamp, N.LastMajorVersionId, N.LastMinorVersionId from Versions V join Nodes N on V.NodeId = N.NodeId WHERE N.Path = '{0}' COLLATE Latin1_General_CI_AS";

            if (excludedNodeTypeIds != null && excludedNodeTypeIds.Length > 0)
            {
                checkNodeSql += $" AND N.NodeTypeId NOT IN ({string.Join(", ", excludedNodeTypeIds)})";
            }

            var sql = string.Format(checkNodeSql, path);

            using var ctx = new MsSqlDataContext(ConnectionStrings.ConnectionString,
                                                 DataOptions.GetLegacyConfiguration(), CancellationToken.None);

            return(await ctx.ExecuteReaderAsync(sql, async (reader, cancel) =>
            {
                cancel.ThrowIfCancellationRequested();
                TimestampData dbNode = null;
                if (await reader.ReadAsync(cancel).ConfigureAwait(false))
                {
                    dbNode = new TimestampData
                    {
                        NodeId = reader.GetSafeInt32(reader.GetOrdinal("NodeId")),
                        VersionId = reader.GetSafeInt32(reader.GetOrdinal("VersionId")),
                        NodeTimestamp = reader.GetSafeInt64(reader.GetOrdinal("NodeTimestamp")),
                        VersionTimestamp = reader.GetSafeInt64(reader.GetOrdinal("VersionTimestamp")),
                        LastMajorVersionId = reader.GetSafeInt32(reader.GetOrdinal("LastMajorVersionId")),
                        LastMinorVersionId = reader.GetSafeInt32(reader.GetOrdinal("LastMinorVersionId")),
                    };
                }
                return dbNode;
            }).ConfigureAwait(false));
        }
        private int CheckDbAndIndex(TimestampData dbData, IndexReader ixReader, List <Difference> result)
        {
            var nodeIdFromDb       = dbData.NodeId;
            var versionId          = dbData.VersionId;
            var dbNodeTimestamp    = dbData.NodeTimestamp;
            var dbVersionTimestamp = dbData.VersionTimestamp;
            var lastMajorVersionId = dbData.LastMajorVersionId;
            var lastMinorVersionId = dbData.LastMinorVersionId;
            var termDocs           = ixReader.TermDocs(new Term(IndexFieldName.VersionId, NumericUtils.IntToPrefixCoded(versionId)));
            var docId = -1;

            if (termDocs.Next())
            {
                docId = termDocs.Doc();
                var doc = ixReader.Document(docId);
                var indexNodeTimestamp    = ParseLong(doc.Get(IndexFieldName.NodeTimestamp));
                var indexVersionTimestamp = ParseLong(doc.Get(IndexFieldName.VersionTimestamp));
                var nodeId  = ParseInt(doc.Get(IndexFieldName.NodeId));
                var version = doc.Get(IndexFieldName.Version);
                var p       = doc.Get(IndexFieldName.Path);
                if (termDocs.Next())
                {
                    result.Add(new Difference(IndexDifferenceKind.MoreDocument)
                    {
                        DocId              = docId,
                        NodeId             = nodeId,
                        VersionId          = versionId,
                        Version            = version,
                        Path               = p,
                        DbNodeTimestamp    = dbNodeTimestamp,
                        DbVersionTimestamp = dbVersionTimestamp,
                        IxNodeTimestamp    = indexNodeTimestamp,
                        IxVersionTimestamp = indexVersionTimestamp,
                    });
                }
                if (dbVersionTimestamp != indexVersionTimestamp)
                {
                    result.Add(new Difference(IndexDifferenceKind.DifferentVersionTimestamp)
                    {
                        DocId              = docId,
                        VersionId          = versionId,
                        DbNodeTimestamp    = dbNodeTimestamp,
                        DbVersionTimestamp = dbVersionTimestamp,
                        IxNodeTimestamp    = indexNodeTimestamp,
                        IxVersionTimestamp = indexVersionTimestamp,
                        NodeId             = nodeId,
                        Version            = version,
                        Path = p
                    });
                }

                // Check version flags by comparing them to the db: we assume that the last
                // major and minor version ids in the Nodes table is the correct one.
                var isLastPublic        = doc.Get(IndexFieldName.IsLastPublic);
                var isLastDraft         = doc.Get(IndexFieldName.IsLastDraft);
                var isLastPublicInDb    = versionId == lastMajorVersionId;
                var isLastDraftInDb     = versionId == lastMinorVersionId;
                var isLastPublicInIndex = isLastPublic == IndexValue.Yes;
                var isLastDraftInIndex  = isLastDraft == IndexValue.Yes;

                if (isLastPublicInDb != isLastPublicInIndex)
                {
                    result.Add(new Difference(IndexDifferenceKind.DifferentLastPublicFlag)
                    {
                        DocId              = docId,
                        VersionId          = versionId,
                        DbNodeTimestamp    = dbNodeTimestamp,
                        DbVersionTimestamp = dbVersionTimestamp,
                        IxNodeTimestamp    = indexNodeTimestamp,
                        IxVersionTimestamp = indexVersionTimestamp,
                        NodeId             = nodeId,
                        Version            = version,
                        Path         = p,
                        IsLastPublic = isLastPublicInIndex,
                        IsLastDraft  = isLastDraftInIndex
                    });
                }
                if (isLastDraftInDb != isLastDraftInIndex)
                {
                    result.Add(new Difference(IndexDifferenceKind.DifferentLastDraftFlag)
                    {
                        DocId              = docId,
                        VersionId          = versionId,
                        DbNodeTimestamp    = dbNodeTimestamp,
                        DbVersionTimestamp = dbVersionTimestamp,
                        IxNodeTimestamp    = indexNodeTimestamp,
                        IxVersionTimestamp = indexVersionTimestamp,
                        NodeId             = nodeId,
                        Version            = version,
                        Path         = p,
                        IsLastPublic = isLastPublicInIndex,
                        IsLastDraft  = isLastDraftInIndex
                    });
                }

                if (dbNodeTimestamp != indexNodeTimestamp)
                {
                    var ok = false;
                    if (isLastDraft != IndexValue.Yes)
                    {
                        var      latestDocs = ixReader.TermDocs(new Term(IndexFieldName.NodeId, NumericUtils.IntToPrefixCoded(nodeId)));
                        Document latestDoc  = null;
                        while (latestDocs.Next())
                        {
                            var latestDocId = latestDocs.Doc();
                            var d           = ixReader.Document(latestDocId);
                            if (d.Get(IndexFieldName.IsLastDraft) != IndexValue.Yes)
                            {
                                continue;
                            }
                            latestDoc = d;
                            break;
                        }
                        var latestPath = latestDoc?.Get(IndexFieldName.Path);
                        if (latestPath == p)
                        {
                            ok = true;
                        }
                    }
                    if (!ok)
                    {
                        result.Add(new Difference(IndexDifferenceKind.DifferentNodeTimestamp)
                        {
                            DocId              = docId,
                            VersionId          = versionId,
                            DbNodeTimestamp    = dbNodeTimestamp,
                            DbVersionTimestamp = dbVersionTimestamp,
                            IxNodeTimestamp    = indexNodeTimestamp,
                            IxVersionTimestamp = indexVersionTimestamp,
                            NodeId             = nodeId,
                            Version            = version,
                            Path = p
                        });
                    }
                }
            }
            else
            {
                result.Add(new Difference(IndexDifferenceKind.NotInIndex)
                {
                    DocId              = docId,
                    VersionId          = versionId,
                    DbNodeTimestamp    = dbNodeTimestamp,
                    DbVersionTimestamp = dbVersionTimestamp,
                    NodeId             = nodeIdFromDb
                });
            }
            return(docId);
        }