private bool SetUnversionedField(CustomSqlItemData targetItem, SqlItemFieldValue currentField, int version, string language)
        {
            // check for corruption in SQL server tables (field values in wrong table) - an unversioned field should have a version less than 1 (SQL sends -1 back for unversioned) and a language
            if (version >= 1)
            {
                Log.Error($"[Dilithium] Data corruption in {targetItem.DatabaseName}://{{{targetItem.Id}}}! Field {currentField.FieldId} (unversioned) had a value in the versioned fields table. The field value will be ignored.", this);
                return(false);
            }

            if (string.IsNullOrEmpty(language))
            {
                Log.Error($"[Dilithium] Data corruption in {targetItem.DatabaseName}://{{{targetItem.Id}}}! Field {currentField.FieldId} (unversioned) had a value in the shared fields table. The field value will be ignored.", this);
                return(false);
            }

            foreach (var languageFields in targetItem.RawUnversionedFields)
            {
                if (languageFields.Language.Name.Equals(language, StringComparison.Ordinal))
                {
                    languageFields.RawFields.Add(currentField);
                    return(true);
                }
            }

            var newLanguage = new SqlItemLanguage();

            newLanguage.Language = new CultureInfo(language);
            newLanguage.RawFields.Add(currentField);

            targetItem.RawUnversionedFields.Add(newLanguage);

            return(true);
        }
        private bool SetSharedField(CustomSqlItemData targetItem, SqlItemFieldValue currentField, int version, string language)
        {
            // check for corruption in SQL server tables (field values in wrong table) - shared field should have neither language nor version one or greater (SQL sends version -1 for shared)
            if (version >= 1)
            {
                Log.Error($"[Dilithium] Data corruption in {targetItem.DatabaseName}://{{{targetItem.Id}}}! Field {{{currentField.FieldId}}} (shared) had a value in the versioned fields table. The field value will be ignored.", this);
                return(false);
            }

            if (!string.IsNullOrEmpty(language))
            {
                Log.Error($"[Dilithium] Data corruption in {targetItem.DatabaseName}://{{{targetItem.Id}}}! Field {currentField.FieldId} (shared) had a value in the unversioned fields table. The field value will be ignored.", this);
                return(false);
            }

            targetItem.RawSharedFields.Add(currentField);

            return(true);
        }
        private bool IngestFieldData(SqlDataReader reader)
        {
            var errors = false;

            // the reader will be on result set 0 when it arrives (item data)
            // so we need to advance it to set 1 (descendants field data)
            reader.NextResult();

            var itemsById = _itemsById;

            while (reader.Read())
            {
                var itemId   = reader.GetGuid(0);
                var language = reader.GetString(1);
                var version  = reader.GetInt32(4);

                var currentField = new SqlItemFieldValue(itemId, Database.Name, language, version)
                {
                    FieldId = reader.GetGuid(2), Value = reader.GetString(3)
                };


                // get current item to add fields to
                if (!itemsById.TryGetValue(itemId, out var targetItem))
                {
                    throw new InvalidOperationException($"Item {itemId} was not read by the item loader but had field {currentField.FieldId} in the field loader!");
                }

                var fieldMetadata = GetTemplateField(currentField.FieldId, targetItem.TemplateId);

                if (fieldMetadata == null)
                {
                    // if we got here it probably means that there's a field value in the DB that is from the _wrong_ template ID
                    // Sitecore seems to ignore this when it occurs, so so will we - we'll skip loading the field
                    continue;
                }

                currentField.NameHint  = fieldMetadata.Name;
                currentField.FieldType = fieldMetadata.Type;

                // for blob fields we need to set the blob ID so it can be read
                if (fieldMetadata.IsBlob)
                {
                    var blobCandidateValue = currentField.Value;
                    if (blobCandidateValue.Length > 38)
                    {
                        blobCandidateValue = blobCandidateValue.Substring(0, 38);
                    }

                    if (ID.TryParse(blobCandidateValue, out var blobId))
                    {
                        currentField.BlobId = blobId.Guid;
                    }
                }

                // add field to target item data
                if (fieldMetadata.IsShared)
                {
                    // shared field = no version, no language
                    if (!SetSharedField(targetItem, currentField, version, language))
                    {
                        errors = true;
                    }
                }
                else if (fieldMetadata.IsUnversioned)
                {
                    // unversioned field = no version, with language (version -1 is used as a nonversioned flag)
                    if (!SetUnversionedField(targetItem, currentField, version, language))
                    {
                        errors = true;
                    }
                }
                else
                {
                    // versioned field
                    if (!SetVersionedField(targetItem, language, version, currentField))
                    {
                        errors = true;
                    }
                }
            }

            return(errors);
        }
        private bool SetVersionedField(CustomSqlItemData targetItem, string language, int version, SqlItemFieldValue currentField)
        {
            // check for corruption in SQL server tables (field values in wrong table) - a versioned field should have both a language and a version that's one or greater
            if (version < 1)
            {
                if (string.IsNullOrEmpty(language))
                {
                    Log.Error($"[Dilithium] Data corruption in {targetItem.DatabaseName}://{{{targetItem.Id}}}! Field {currentField.FieldId} (versioned) had a value in the shared fields table. The field value will be ignored.", this);
                }
                else
                {
                    Log.Error($"[Dilithium] Data corruption in {targetItem.DatabaseName}://{{{targetItem.Id}}}! Field {currentField.FieldId} (versioned) had a value in the unversioned fields table. The field value will be ignored.", this);
                }
                return(false);
            }

            foreach (var versionFields in targetItem.RawVersions)
            {
                if (versionFields.Language.Name.Equals(language, StringComparison.Ordinal) && versionFields.VersionNumber == version)
                {
                    versionFields.RawFields.Add(currentField);
                    return(true);
                }
            }

            var newVersion = new SqlItemVersion
            {
                Language      = new CultureInfo(language),
                VersionNumber = version
            };

            newVersion.RawFields.Add(currentField);

            targetItem.RawVersions.Add(newVersion);

            return(true);
        }