protected static function get_nested_expiry_info_from_sql(string $sql, array $params) : array {
        global $DB;

        $fulllist = $DB->get_recordset_sql($sql, $params);
        $datalist = [];
        $expiredcontents = [];
        $pathstoskip = [];

        $userpurpose = data_registry::get_effective_contextlevel_value(CONTEXT_USER, 'purpose');
        foreach ($fulllist as $record) {
            \context_helper::preload_from_record($record);
            $context = \context::instance_by_id($record->id, false);

            if (!self::is_eligible_for_deletion($pathstoskip, $context)) {
                // We should skip this context, and therefore all of it's children.
                $datalist = array_filter($datalist, function($data, $path) use ($context) {
                    // Remove any child of this context.
                    // Technically this should never be fulfilled because the query is ordered in path DESC, but is kept
                    // in to be certain.
                    return (false === strpos($path, "{$context->path}/"));
                }, ARRAY_FILTER_USE_BOTH);

                if ($record->expiredctxid) {
                    // There was previously an expired context record.
                    // Delete it to be on the safe side.
                    $expiredcontext = new expired_context(null, expired_context::extract_record($record, 'expiredctx'));
                    $expiredcontext->delete();
                }
                continue;
            }

            if ($context instanceof \context_user) {
                $purpose = $userpurpose;
            } else {
                $purposevalue = $record->purposeid !== null ? $record->purposeid : context_instance::NOTSET;
                $purpose = api::get_effective_context_purpose($context, $purposevalue);
            }

            if ($context instanceof \context_user && !empty($record->userdeleted)) {
                $expiryinfo = static::get_expiry_info($purpose, $record->userdeleted);
            } else {
                $expiryinfo = static::get_expiry_info($purpose, $record->expirydate);
            }

            foreach ($datalist as $path => $data) {
                // Merge with already-processed children.
                if (strpos($path, $context->path) !== 0) {
                    continue;
                }

                $expiryinfo->merge_with_child($data->info);
            }

            $datalist[$context->path] = (object) [
                'context' => $context,
                'record' => $record,
                'purpose' => $purpose,
                'info' => $expiryinfo,
            ];
        }
        $fulllist->close();

        return $datalist;
    }