#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""

TODO

0) Function "mark_feed_as_read": see function "maintain_archive"

1) Function to open connection (receive db_file).
   Function to close connection.
   All other functions to receive cursor.

2) Merge function add_metadata into function import_feeds.

3) SQL prepared statements.

4) Support categories;

"""

import asyncio
#from asyncio import Lock
from slixfeed.utility.logger import UtilityLogger
from sqlite3 import connect, Error, IntegrityError, Row
import sys
import time

# from eliot import start_action, to_file
# # with start_action(action_type="list_feeds()", db=db_file):
# # with start_action(action_type="last_entries()", num=num):
# # with start_action(action_type="get_feeds()"):
# # with start_action(action_type="remove_entry()", source=source):
# # with start_action(action_type="search_entries()", query=query):
# # with start_action(action_type="check_entry()", link=link):

CURSORS = {}

# aiosqlite
DBLOCK = asyncio.Lock()

logger = UtilityLogger(__name__)

class SQLiteGeneral:

    def create_connection(db_file):
        """
        Create a database connection to the SQLite database
        specified by db_file.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        conn : object
            Connection object or None.
        """
        time_begin = time.time()
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        message_log = "{}"
        logger.debug(message_log.format(function_name))
        conn = None
        try:
            conn = connect(db_file)
            conn.execute("PRAGMA foreign_keys = ON")
            # return conn
        except Error as e:
            logger.error(f"{function_name}	{db_file}	{str(e)}")
        time_end = time.time()
        difference = time_end - time_begin
        if difference > 1:
            logger.warning(f"{function_name}	{db_file}	Duration: {difference}")
        return conn

    def create_a_database_for_an_account(db_file):
        """
        Create an SQLite database for an account.

        Parameters
        ----------
        db_file : str
            Path to database file.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            entries_properties = (
                """
                CREATE TABLE IF NOT EXISTS entries_properties (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL,
                    identifier TEXT NOT NULL UNIQUE,
                    link TEXT,
                    title TEXT,
                    title_type TEXT,
                    summary_text TEXT,
                    summary_lang TEXT,
                    summary_type TEXT,
                    summary_base TEXT,
                    category TEXT,
                    href TEXT,
                    comments TEXT,
                    rating TEXT,
                    published TEXT,
                    updated TEXT,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            entries_properties_authors = (
                """
                CREATE TABLE IF NOT EXISTS entries_properties_authors (
                    id INTEGER NOT NULL,
                    entry_id INTEGER NOT NULL,
                    name TEXT,
                    url TEXT,
                    email TEXT,
                    FOREIGN KEY ("entry_id") REFERENCES "entries_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            entries_properties_contents = (
                """
                CREATE TABLE IF NOT EXISTS entries_properties_contents (
                    id INTEGER NOT NULL,
                    entry_id INTEGER NOT NULL,
                    text TEXT,
                    type TEXT,
                    base TEXT,
                    lang TEXT,
                    FOREIGN KEY ("entry_id") REFERENCES "entries_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            entries_properties_contributors = (
                """
                CREATE TABLE IF NOT EXISTS entries_properties_contributors (
                    id INTEGER NOT NULL,
                    entry_id INTEGER NOT NULL,
                    name TEXT,
                    url TEXT,
                    email TEXT,
                    FOREIGN KEY ("entry_id") REFERENCES "entries_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            entries_properties_links = (
                """
                CREATE TABLE IF NOT EXISTS entries_properties_links (
                    id INTEGER NOT NULL,
                    entry_id INTEGER NOT NULL,
                    url TEXT,
                    type TEXT,
                    rel TEXT,
                    size INTEGER,
                    FOREIGN KEY ("entry_id") REFERENCES "entries_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            entries_properties_tags = (
                """
                CREATE TABLE IF NOT EXISTS entries_properties_tags (
                    id INTEGER NOT NULL,
                    entry_id INTEGER NOT NULL,
                    term TEXT,
                    scheme TEXT,
                    label TEXT,
                    FOREIGN KEY ("entry_id") REFERENCES "entries_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            entries_state = (
                """
                CREATE TABLE IF NOT EXISTS entries_state (
                    id INTEGER NOT NULL,
                    entry_id INTEGER NOT NULL,
                    rejected INTEGER NOT NULL DEFAULT 0,
                    read INTEGER NOT NULL DEFAULT 0,
                    archived INTEGER NOT NULL DEFAULT 0,
                    FOREIGN KEY ("entry_id") REFERENCES "entries_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            # TODO Rethink!
            # Albeit, probably, more expensive, we might want to have feed_id
            # as foreign key, as it is with feeds_properties and feeds_state
            feeds_preferences = (
                """
                CREATE TABLE IF NOT EXISTS feeds_preferences (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL UNIQUE,
                    enabled INTEGER NOT NULL DEFAULT 1,
                    mutable INTEGER NOT NULL DEFAULT 0,
                    filter INTEGER NOT NULL DEFAULT 1,
                    priority INTEGER NOT NULL DEFAULT 0,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            feeds_properties = (
                """
                CREATE TABLE IF NOT EXISTS feeds_properties (
                    id INTEGER NOT NULL,
                    url TEXT NOT NULL UNIQUE,
                    identifier TEXT NOT NULL,
                    title TEXT,
                    title_type TEXT,
                    subtitle TEXT,
                    subtitle_type TEXT,
                    version TEXT,
                    encoding TEXT,
                    language TEXT,
                    rating TEXT,
                    entries INTEGER,
                    icon TEXT,
                    image TEXT,
                    logo TEXT,
                    ttl TEXT,
                    updated TEXT,
                    PRIMARY KEY ("id")
                  );
                """
                )
            feeds_properties_links = (
                """
                CREATE TABLE IF NOT EXISTS feeds_properties_links (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL,
                    url TEXT,
                    type TEXT,
                    rel TEXT,
                    size INTEGER,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            feeds_properties_tags = (
                """
                CREATE TABLE IF NOT EXISTS feeds_properties_tags (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL,
                    term TEXT,
                    scheme TEXT,
                    label TEXT,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            feeds_rules = (
                """
                CREATE TABLE IF NOT EXISTS feeds_rules (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL UNIQUE,
                    type TEXT NOT NULL,
                    keywords TEXT,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            feeds_state = (
                """
                CREATE TABLE IF NOT EXISTS feeds_state (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL UNIQUE,
                    renewed TEXT,
                    scanned TEXT,
                    status_code INTEGER,
                    valid INTEGER,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            feeds_statistics = (
                """
                CREATE TABLE IF NOT EXISTS statistics (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL UNIQUE,
                    offline INTEGER,
                    entries INTEGER,
                    entries INTEGER,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            # TODO
            # Consider parameter unique:
            # entry_id TEXT NOT NULL UNIQUE,
            # Will eliminate function:
            # check_entry_exist
            filters = (
                """
                CREATE TABLE IF NOT EXISTS filters (
                    id INTEGER NOT NULL,
                    key TEXT NOT NULL,
                    value TEXT,
                    PRIMARY KEY ("id")
                  );
                """
                )
            settings = (
                """
                CREATE TABLE IF NOT EXISTS settings (
                    id INTEGER NOT NULL,
                    key TEXT NOT NULL UNIQUE,
                    value INTEGER,
                    PRIMARY KEY ("id")
                  );
                """
                )
            status = (
                """
                CREATE TABLE IF NOT EXISTS status (
                    id INTEGER NOT NULL,
                    key TEXT NOT NULL,
                    value INTEGER,
                    PRIMARY KEY ("id")
                  );
                """
                )
            tagged_feeds = (
                """
                CREATE TABLE IF NOT EXISTS tagged_feeds (
                    id INTEGER NOT NULL,
                    feed_id INTEGER NOT NULL,
                    tag_id INTEGER NOT NULL,
                    FOREIGN KEY ("feed_id") REFERENCES "feeds_properties" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    FOREIGN KEY ("tag_id") REFERENCES "tags" ("id")
                      ON UPDATE CASCADE
                      ON DELETE CASCADE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            tags = (
                """
                CREATE TABLE IF NOT EXISTS tags (
                    id INTEGER NOT NULL,
                    tag TEXT NOT NULL UNIQUE,
                    PRIMARY KEY ("id")
                  );
                """
                )
            cur = conn.cursor()
            # cur = get_cursor(db_file)
            cur.execute(entries_properties)
            cur.execute(entries_properties_authors)
            cur.execute(entries_properties_contents)
            cur.execute(entries_properties_contributors)
            cur.execute(entries_properties_links)
            cur.execute(entries_properties_tags)
            cur.execute(entries_state)
            cur.execute(feeds_properties)
            cur.execute(feeds_properties_links)
            cur.execute(feeds_properties_tags)
            cur.execute(feeds_preferences)
            cur.execute(feeds_rules)
            cur.execute(feeds_state)
            cur.execute(filters)
            # cur.execute(statistics)
            cur.execute(settings)
            cur.execute(status)
            cur.execute(tagged_feeds)
            cur.execute(tags)

    def get_cursor(db_file):
        """
        Allocate a cursor to connection per database.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        CURSORS[db_file] : object
            Cursor.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        if db_file in CURSORS:
            return CURSORS[db_file]
        else:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                CURSORS[db_file] = cur
        return CURSORS[db_file]

    async def import_feeds(db_file, subscriptions):
        """
        Insert subscriptions into table subscriptions.

        Parameters
        ----------
        db_file : str
            Path to database file.
        subscriptions : list
            Set of subscriptions (Title and URL).
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                for subscription in subscriptions:
                    logger.debug(f"{function_name}	{db_file}	URL: {subscription}")
                    identifier = subscription["identifier"]
                    title = subscription["title"]
                    uri = subscription["uri"]
                    sql = (
                        """
                        INSERT
                        INTO feeds_properties(
                            identifier, title, url)
                        VALUES(
                            ?, ?, ?)
                        """
                        )
                    par = (identifier, title, uri)
                    try:
                        cur.execute(sql, par)
                        #await asyncio.sleep(0)
                    except IntegrityError as e:
                        logger.warning(f"{function_name}	{db_file}	Skipping: {str(url)}")
                        logger.error(e)

    async def add_metadata(db_file):
        """
        Insert a new feed into the feeds table.

        Parameters
        ----------
        db_file : str
            Path to database file.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    SELECT id
                    FROM feeds_properties
                    ORDER BY id ASC
                    """
                    )
                ixs = cur.execute(sql).fetchall()
                for ix in ixs:
                    feed_id = ix[0]
                    # Set feed status
                    sql = (
                        """
                        INSERT
                        INTO feeds_state(
                            feed_id)
                        VALUES(
                            ?)
                        """
                        )
                    par = (feed_id,)
                    try:
                        cur.execute(sql, par)
                       #await asyncio.sleep(0)
                    except IntegrityError as e:
                        logger.warning(f"{function_name}	{db_file}	Skipping feed_id {feed_id} for table feeds_state")
                        logger.error(f"{function_name}	{db_file}	{str(e)}")
                    # Set feed preferences.
                    sql = (
                        """
                        INSERT
                        INTO feeds_preferences(
                            feed_id)
                        VALUES(
                            ?)
                        """
                        )
                    par = (feed_id,)
                    try:
                        cur.execute(sql, par)
                       #await asyncio.sleep(0)
                    except IntegrityError as e:
                        logger.warning(f"{function_name}	{db_file}	Skipping feed_id {feed_id} for table feeds_preferences")
                        logger.error(f"{function_name}	{db_file}	{str(e)}")

    async def insert_feed(db_file, url, title, identifier, entries="",
                          version="", encoding="", language="", status_code="",
                          updated="", links=""):
        """
        Insert a new feed into the feeds table.

        Parameters
        ----------
        db_file : str
            Path to database file.
        url : str
            URL.
        title : str
            Feed title.
        identifier : str
            Feed identifier.
        entries : int, optional
            Number of entries. The default is None.
        version : str, optional
            Type of feed. The default is None.
        encoding : str, optional
            Encoding of feed. The default is None.
        language : str, optional
            Language code of feed. The default is None.
        status : str, optional
            HTTP status code. The default is None.
        updated : ???, optional
            Date feed was last updated. The default is None.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	URL: {url}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    INSERT
                    INTO feeds_properties(
                         url, title, identifier, entries, version, encoding, language, updated)
                    VALUES(
                         ?, ?, ?, ?, ?, ?, ?, ?)
                    """
                    )
                par = (url, title, identifier, entries, version, encoding, language, updated)
                cur.execute(sql, par)
                sql = (
                    """
                    SELECT id
                    FROM feeds_properties
                    WHERE url = :url
                    """
                    )
                par = (url,)
                feed_id = cur.execute(sql, par).fetchone()[0]
                sql = (
                    """
                    INSERT
                    INTO feeds_state(
                        feed_id, status_code, valid)
                    VALUES(
                        ?, ?, ?)
                    """
                    )
                par = (feed_id, status_code, 1)
                cur.execute(sql, par)
                sql = (
                    """
                    INSERT
                    INTO feeds_preferences(
                        feed_id)
                    VALUES(
                        ?)
                    """
                    )
                par = (feed_id,)
                cur.execute(sql, par)
                for link in links:
                    sql = (
                        """
                        INSERT
                        INTO feeds_properties_links(
                            feed_id, url, type, rel)
                        VALUES(
                            ?, ?, ?, ?)
                        """
                        )
                    par = (feed_id, link["href"], link["type"], link["rel"])
                    cur.execute(sql, par)

    async def remove_feed_by_url(db_file, url):
        """
        Delete a feed by feed URL.

        Parameters
        ----------
        db_file : str
            Path to database file.
        url : str
            URL of feed.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	URL: {url}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            async with DBLOCK:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM feeds_properties
                    WHERE url = ?
                    """
                    )
                par = (url,)
                cur.execute(sql, par)

    async def delete_entry_id_by_indices(db_file, entries_ids):
        """
        Delete entries of feed by entry ID.

        Parameters
        ----------
        db_file : str
            Path to database file.
        entries_ids : list
            Indices of entries.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            async with DBLOCK:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM entries_properties
                    WHERE id = ?
                    """
                    )
                for ix in entries_ids:
                    #await asyncio.sleep(0)
                    cur.execute(sql, ix)

    # NOTE
    # This function causes to irresponsiveness upon deletion of subscriptions with
    # a larger set of items.
    # Consider to iterate with instruction `await asyncio.sleep(0)` and remove
    # entries one by one, instead of relying on SQLite to do that task.
    async def remove_feed_by_index(db_file, ix):
        """
        Delete a feed by feed ID.

        Parameters
        ----------
        db_file : str
            Path to database file.
        ix : str
            Index of feed.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index: {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            async with DBLOCK:
                cur = conn.cursor()
                # # NOTE Should we move DBLOCK to this line? 2022-12-23
                # sql = (
                #     "DELETE "
                #     "FROM entries_properties "
                #     "WHERE feed_id = ?"
                #     )
                # par = (url,)
                # cur.execute(sql, par)
                sql = (
                    """
                    DELETE
                    FROM feeds_properties
                    WHERE id = ?
                    """
                    )
                par = (ix,)
                cur.execute(sql, par)

    def get_feeds_by_tag_id(db_file, tag_id):
        """
        Get feeds of given tag.

        Parameters
        ----------
        db_file : str
            Path to database file.
        tag_id : str
            Tag ID.

        Returns
        -------
        result : tuple
            List of tags.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Tag ID: {tag_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT feeds_properties.*
                FROM feeds_properties
                INNER JOIN tagged_feeds ON feeds_properties.id = tagged_feeds.feed_id
                INNER JOIN tags ON tags.id = tagged_feeds.tag_id
                WHERE tags.id = ?
                ORDER BY feeds_properties.title;
                """
                )
            par = (tag_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_tags_by_feed_id(db_file, feed_id):
        """
        Get tags of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed ID.

        Returns
        -------
        result : tuple
            List of tags.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT tags.tag
                FROM tags
                INNER JOIN tagged_feeds ON tags.id = tagged_feeds.tag_id
                INNER JOIN feeds_properties ON feeds_properties.id = tagged_feeds.feed_id
                WHERE feeds_properties.id = ?
                ORDER BY tags.tag;
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    async def set_feed_id_and_tag_id(db_file, feed_id, tag_id):
        """
        Set Feed ID and Tag ID.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed ID
        tag_id : str
            Tag ID
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}; Tag ID: {tag_id}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    INSERT
                    INTO tagged_feeds(
                        feed_id, tag_id)
                    VALUES(
                        :feed_id, :tag_id)
                    """
                    )
                par = {
                    "feed_id": feed_id,
                    "tag_id": tag_id
                    }
                cur.execute(sql, par)

    def get_feed_properties(db_file, feed_id):
        """
        Get properties of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed ID.

        Returns
        -------
        properties : list
            List of properties.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT identifier, title, subtitle
                FROM feeds_properties
                WHERE id = :feed_id
                """
                )
            par = (feed_id,)
            properties = cur.execute(sql, par).fetchone()
            return properties

    def get_feed_identifier(db_file, feed_id):
        """
        Get identifier of given feed ID.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed ID.

        Returns
        -------
        identifier : str
            Identifier name.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT identifier
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def check_identifier_exist(db_file, identifier):
        """
        Check whether given identifier exist.

        Parameters
        ----------
        db_file : str
            Path to database file.
        identifier : str
            Identifier name.

        Returns
        -------
        id : str
            ID.
        feed_id : str
            Feed ID.
        identifier : str
            Identifier name.
        """
        function_name = sys._getframe().f_code.co_name
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Identifier: {identifier}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT identifier
                FROM feeds_properties
                WHERE identifier = ?
                """
                )
            par = (identifier,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_feed_version(db_file, feed_id):
        """
        Get version of given feed ID.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed ID.

        Returns
        -------
        version : str
            Version.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT version
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_tag_id(db_file, tag_name):
        """
        Get ID of given tag. Check whether tag exist.

        Parameters
        ----------
        db_file : str
            Path to database file.
        tag_name : str
            Tag name.

        Returns
        -------
        ix : str
            Tag ID.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Tag Name: {tag_name}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id
                FROM tags
                WHERE tag = ?
                """
                )
            par = (tag_name,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_tag_name(db_file, ix):
        """
        Get name of given tag. Check whether tag exist.

        Parameters
        ----------
        db_file : str
            Path to database file.
        ix : str
            Tag ID.

        Returns
        -------
        tag_name : str
            Tag name.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index: {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT tag
                FROM tags
                WHERE id = ?
                """
                )
            par = (ix,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def is_tag_id_associated(db_file, tag_id):
        """
        Check whether tag_id is associated with any feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        tag_id : str
            Tag ID.

        Returns
        -------
        tag_id : str
            Tag ID.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Tag ID: {tag_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT tag_id
                FROM tagged_feeds
                WHERE tag_id = :tag_id
                """
                )
            par = {
                "tag_id": tag_id
                }
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    async def delete_tag_by_index(db_file, ix):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM tags
                    WHERE id = :id
                    """
                    )
                par = {"id": ix}
                cur.execute(sql, par)

    def is_tag_id_of_feed_id(db_file, tag_id, feed_id):
        """
        Check whether given tag is related with given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed ID.
        tag_id : str
            Tag ID.

        Returns
        -------
        tag_id : str
            Tag ID.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}; Tag ID {tag_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT tag_id
                FROM tagged_feeds
                WHERE tag_id = :tag_id
                  AND feed_id = :feed_id
                """
                )
            par = {
                "tag_id": tag_id,
                "feed_id": feed_id
                }
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    async def delete_feed_id_tag_id(db_file, feed_id, tag_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}; Tag ID {tag_id}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM tagged_feeds
                    WHERE tag_id = :tag_id
                      AND feed_id = :feed_id
                    """
                    )
                par = {
                    "tag_id": tag_id,
                    "feed_id": feed_id
                    }
                cur.execute(sql, par)

    async def set_new_tag(db_file, tag):
        """
        Set new Tag

        Parameters
        ----------
        db_file : str
            Path to database file.
        tag : str
            Tag
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Tag {tag}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    INSERT
                    INTO tags(
                        tag)
                    VALUES(
                        :tag)
                    """
                    )
                par = {
                    "tag": tag
                    }
                cur.execute(sql, par)

    def get_feed_id_and_name(db_file, url):
        """
        Get Id and Name of feed.
        Check whether a feed exists.
        Query for feeds by given url.

        Parameters
        ----------
        db_file : str
            Path to database file.
        url : str
            URL.

        Returns
        -------
        result : tuple
            List of ID and Name of feed.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	URL {url}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id, title
                FROM feeds_properties
                WHERE url = ?
                """
                )
            par = (url,)
            result = cur.execute(sql, par).fetchone()
            return result

    def get_number_of_items(db_file, table):
        """
        Return number of entries or feeds.

        Parameters
        ----------
        db_file : str
            Path to database file.
        table : str
            "entries_properties" or "feeds_properties".

        Returns
        -------
        count : ?
            Number of rows.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Table {table}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT count(id)
                FROM {}
                """
                ).format(table)
            count = cur.execute(sql).fetchone()[0]
            return count

    def get_number_of_feeds_active(db_file):
        """
        Return number of active feeds.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        count : str
            Number of rows.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT count(id)
                FROM feeds_preferences
                WHERE enabled = 1
                """
                )
            count = cur.execute(sql).fetchone()[0]
            return count

    def get_number_of_entries_unread(db_file):
        """
        Return number of unread items.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        count : ?
            Number of rows.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT count(id)
                FROM entries_state
                WHERE read = 0
                """
                )
            count = cur.execute(sql).fetchone()[0]
            return count

    def get_entries(db_file, num):
        """
        Extract information from entries.

        Parameters
        ----------
        db_file : str
            Path to database file.
        num : str, optional
            Number. The default is None.

        Returns
        -------
        result : tuple
            News items.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Number {num}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id, title, link, summary_text, feed_id, published
                FROM entries_properties
                ORDER BY published DESC
                LIMIT :num
                """
                )
            par = (num,)
            result = cur.execute(sql, par).fetchall()
            return result

    # NOTE Only [0] [1] [4] are in use.
    # See results = SQLiteUtility.get_entries_rejected(db_file, num)
    # Module xmpp/client.py
    def get_entries_rejected(db_file, num):
        """
        Extract information from rejected entries.

        Parameters
        ----------
        db_file : str
            Path to database file.
        num : str, optional
            Number. The default is None.

        Returns
        -------
        result : tuple
            News items.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Number {num}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT entries_properties.id, title, link, summary_text, feed_id, published
                FROM entries_properties
                INNER JOIN entries_state ON entries_properties.id = entries_state.entry_id
                WHERE entries_state.rejected = 1
                ORDER BY published DESC
                LIMIT :num
                """
                )
            par = (num,)
            result = cur.execute(sql, par).fetchall()
            return result

    # TODO All enclosures (fetchall)
    def get_enclosure_by_entry_id(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Entry ID {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT url
                FROM entries_properties_links
                WHERE entry_id = :entry_id
                  AND rel = "enclosure"
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def retrieve_entries_properties_unread(db_file: str, num: str) -> tuple:
        """
        Retrieve information of unread entries.

        Parameters
        ----------
        db_file : str
            Path to database file.
        num : str, optional
            Number. The default is None.

        Returns
        -------
        result : tuple
            News items.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Number {num}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT entries_properties.id, 
                       entries_properties.identifier, 
                       entries_properties.title, 
                       entries_properties_links.url,
                       entries_properties.summary_text, 
                       entries_properties.feed_id
                FROM entries_properties
                INNER JOIN entries_state ON
                           entries_properties.id = entries_state.entry_id
                INNER JOIN entries_properties_links ON
                           entries_properties.id = entries_properties_links.entry_id
                WHERE entries_state.read = 0 AND
                      entries_properties_links.rel = "alternate" AND
                      entries_properties_links.entry_id = entries_properties.id
                ORDER BY entries_properties.published DESC
                LIMIT :num;
                """
                )
            par = (num,)
            result = cur.execute(sql, par).fetchall()
            return result

    # TODO Deprecate in favour of function "retrieve_entries_properties_unread".
    def get_unread_entries(db_file, num):
        """
        Extract information from unread entries.

        Parameters
        ----------
        db_file : str
            Path to database file.
        num : str, optional
            Number. The default is None.

        Returns
        -------
        result : tuple
            News items.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Number {num}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT entries_properties.id, title, link, summary_text, feed_id, published
                FROM entries_properties
                INNER JOIN entries_state
                  ON entries_properties.id = entries_state.entry_id
                WHERE entries_state.read = 0
                ORDER BY published DESC
                LIMIT :num
                """
                )
            par = (num,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_feed_id_by_entry_index(db_file, ix):
        """
        Get feed id by entry index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        ix : str
            Index.

        Returns
        -------
        feed_id : str
            Feed index.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT feed_id
                FROM entries_properties
                WHERE id = :ix
                """
                )
            par = (ix,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_feed_id(db_file, url):
        """
        Get index of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        url : str
            URL.

        Returns
        -------
        feed_id : str
            Feed index.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	URL {url}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id
                FROM feeds_properties
                WHERE url = ?
                """
                )
            par = (url,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def is_entry_read(db_file, ix):
        """
        Check whether a given entry is marked as read.

        Parameters
        ----------
        db_file : str
            Path to database file.
        ix : str
            Index of entry.

        Returns
        -------
        result : tuple
            Entry ID.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT read
                FROM entries_state
                WHERE entry_id = ?
                """
                )
            par = (ix,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_last_update_time_of_feed(db_file, feed_id):
        """
        Get status information of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT renewed, scanned
                FROM feeds_state
                WHERE feed_id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result

    def get_unread_entries_of_feed(db_file, feed_id):
        """
        Get entries of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM entries_properties
                INNER JOIN entries_state
                  ON entries_properties.id = entries_state.entry_id
                WHERE entries_state.read = 0
                  AND feed_id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_number_of_unread_entries_by_feed(db_file, feed_id):
        """
        Count entries of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT count(entries_properties.id)
                FROM entries_properties
                INNER JOIN entries_state
                  ON entries_properties.id = entries_state.entry_id
                WHERE entries_state.read = 0
                  AND feed_id = ?
                """
                )
            par = (feed_id,)
            count = cur.execute(sql, par).fetchone()
            return count

    async def delete_entry_by_id(db_file, ix):
        """
        Delete entry by Id.

        Parameters
        ----------
        db_file : str
            Path to database file.
        ix : str
            Index.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM entries_properties
                    WHERE id = :ix
                    """
                    )
                par = (ix,)
                cur.execute(sql, par)

    async def archive_entry(db_file, ix):
        """
        Insert entry to archive and delete entry.

        Parameters
        ----------
        db_file : str
            Path to database file.
        ix : str
            Index.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE entries_state
                    SET archived = 1
                    WHERE entry_id = :ix
                    """
                    )
                par = (ix,)
                cur.execute(sql, par)

    def get_feed_title(db_file, feed_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT title
                FROM feeds_properties
                WHERE id = :feed_id
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_feed_subtitle(db_file, feed_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT subtitle
                FROM feeds_properties
                WHERE id = :feed_id
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    async def set_feed_title(db_file, feed_id, title):
        """
        Set new name for feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Index of feed.
        name : str
            New name.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}; Title {title}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE feeds_properties
                    SET title = :title
                    WHERE id = :feed_id
                    """
                    )
                par = {
                    "title": title,
                    "feed_id": feed_id
                    }
                cur.execute(sql, par)

    def get_entry_properties(db_file, ix):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM entries_properties
                WHERE id = :ix
                """
                )
            par = (ix,)
            title = cur.execute(sql, par).fetchone()
            return title

    def get_entry_title(db_file, ix):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT title
                FROM entries_properties
                WHERE id = :ix
                """
                )
            par = (ix,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_entry_url(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT url
                FROM entries_properties_links
                WHERE rel = 'alternate' AND
                      entry_id = ?
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_entry_content(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT text
                FROM entries_properties_contents
                WHERE entry_id = ?
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_entry_summary(db_file, ix):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT summary_text
                FROM entries_properties
                WHERE id = :ix
                """
                )
            par = (ix,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_feed_url(db_file, feed_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT url
                FROM feeds_properties
                WHERE id = :feed_id
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    async def mark_all_as_read(db_file):
        """
        Set read status of all entries as read.

        Parameters
        ----------
        db_file : str
            Path to database file.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE entries_state
                    SET read = 1
                    """
                    )
                cur.execute(sql)
                
                sql = (
                    """
                    SELECT entries_properties.id
                    FROM entries_properties
                    INNER JOIN entries_state ON entries_properties.id = entries_state.entry_id
                    WHERE entries_state.archived = 1
                    """
                    )
                ixs = cur.execute(sql).fetchall()
                sql = (
                    """
                    DELETE
                    FROM entries_properties
                    WHERE id = ?
                    """
                    )
                for ix in ixs: cur.execute(sql, ix)

    async def mark_feed_as_read(db_file, feed_id):
        """
        Set read status of entries of given feed as read.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed ID.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                # TODO Utilize function get_entry_id_indices_by_feed_id instead.
                sql = (
                    """
                    SELECT id
                    FROM entries_properties
                    WHERE feed_id = ?
                    """
                    )
                par = (feed_id,)
                ixs = cur.execute(sql, par).fetchall()
                sql = (
                    """
                    UPDATE entries_state
                    SET read = 1
                    WHERE entry_id = ?
                    """
                    )
                for ix in ixs: cur.execute(sql, ix)
                # for ix in ixs:
                #     par = ix # Variable ix is already of type tuple
                #     cur.execute(sql, par)

    async def mark_as_read(db_file, ix):
        """
        Set read status of entry as read or delete entry.

        Parameters
        ----------
        db_file : str
            Path to database file.
        ix : str
            Index of entry.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                # Check whether a given entry is archived.
                sql = (
                    """
                    SELECT id
                    FROM entries_state
                    WHERE archived = 1
                      AND entry_id = ?
                    """
                    )
                par = (ix,)
                result = cur.execute(sql, par).fetchone()
                # is_entry_archived
                if result:
                    sql = (
                        """
                        DELETE
                        FROM entries_properties
                        WHERE id = ?
                        """
                        )
                else:
                    sql = (
                        """
                        UPDATE entries_state
                        SET read = 1
                        WHERE entry_id = ?
                        """
                        )
                par = (ix,)
                cur.execute(sql, par)

    async def set_enabled_status(db_file, feed_id, status):
        """
        Set status of feed to enabled or not enabled (i.e. disabled).

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Index of feed.
        status : int
            0 or 1.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}; Status {status}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE feeds_preferences
                    SET enabled = :status
                    WHERE feed_id = :feed_id
                    """
                    )
                par = {
                    "status": status,
                    "feed_id": feed_id
                    }
                cur.execute(sql, par)

    """
    TODO

    Investigate what causes date to be int 0

    NOTE

    When time functions of slixfeed.timedate
    were async, there were errors of coroutines

    """
    async def add_entry(db_file, title, link, entry_id, feed_id, date,
                        read_status):
        """
        Add a new entry row into the entries table.

        Parameters
        ----------
        db_file : str
            Path to database file.
        title : str
            Title.
        link : str
            Link.
        entry_id : str
            Entry index.
        feed_id : str
            Feed Id.
        date : str
            Date.
        read_status : str
            0 or 1.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Link {link}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    INSERT
                    INTO entries(
                        title, link, entry_id, feed_id, published, read)
                    VALUES(
                        :title, :link, :entry_id, :feed_id, :published, :read)
                    """
                    )
                par = {
                    "title"     : title,
                    "link"      : link,
                    "entry_id"  : entry_id,
                    "feed_id"   : feed_id,
                    "published" : date,
                    "read"      : read_status}
                cur.execute(sql, par)
                # try:
                #     cur.execute(sql, entry)
                # except:
                #     # None
                #     print("Unknown error for SQLiteUtility.add_entry")
                #     print(entry)
                #     #
                #     # print(current_time(), "COROUTINE OBJECT NOW")
                #     # for i in entry:
                #     #     print(type(i))
                #     #     print(i)
                #     # print(type(entry))
                #     # print(entry)
                #     # print(current_time(), "COROUTINE OBJECT NOW")
                #     # breakpoint()

    async def add_entries_and_update_feed_state(db_file, feed_id, entries):
        """
        Add new entries and update feed state.

        Parameters
        ----------
        db_file : str
            Path to database file.
        entries : tuple
            Set of entries as dict.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                for entry in entries:
                    identifier = entry["id"]
                    if SQLiteGeneral.check_identifier_exist(db_file, identifier):
                        logger.warning(f"{function_name}	{db_file}	Identifier {identifier} already exists.")
                        continue
                    sql = (
                        """
                        INSERT
                        INTO entries_properties(
                            feed_id,
                            identifier,
                            title,
                            title_type,
                            summary_text,
                            summary_lang,
                            summary_type,
                            summary_base,
                            published,
                            updated)
                        VALUES(
                            :feed_id,
                            :identifier,
                            :title,
                            :title_type,
                            :summary_text,
                            :summary_lang,
                            :summary_type,
                            :summary_base,
                            :published,
                            :updated)
                        """
                        )
                    logger.debug(f"{function_name}	{db_file}	New entry {identifier}")
                    par = {
                        "feed_id"      : feed_id,
                        "identifier"   : identifier,
                        "title"        : entry["title"]["text"],
                        "title_type"   : entry["title"]["type"],
                        "summary_text" : entry["summary"]["text"],
                        "summary_lang" : entry["summary"]["lang"],
                        "summary_type" : entry["summary"]["type"],
                        "summary_base" : entry["summary"]["base"],
                        "published"    : entry["published"],
                        "updated"      : entry["updated"]}
                    cur.execute(sql, par)
                    entry_id = cur.lastrowid
                    sql = (
                        """
                        INSERT
                        INTO entries_state(
                            entry_id)
                        VALUES(
                            :entry_id)
                        """
                        )
                    par = {
                        "entry_id": entry_id,
                        }
                    cur.execute(sql, par)
                   #await asyncio.sleep(0)
                    for entry_author in entry["authors"]:
                        sql = (
                            """
                            INSERT
                            INTO entries_properties_authors(
                                entry_id, name, url, email)
                            VALUES(
                                :entry_id, :name, :url, :email)
                            """
                            )
                        par = {
                            "entry_id" : entry_id,
                            "name"     : entry_author["name"],
                            "url"      : entry_author["url"] if "url" in entry_author else None,
                            "email"    : entry_author["email"] if "email" in entry_author else None}
                        cur.execute(sql, par)
                       #await asyncio.sleep(0)
                    for entry_contributor in entry["contributors"]:
                        sql = (
                            """
                            INSERT
                            INTO entries_properties_contributors(
                                entry_id, name, url, email)
                            VALUES(
                                :entry_id, :name, :url, :email)
                            """
                            )
                        par = {
                            "entry_id" : entry_id,
                            "name"     : entry_contributor["name"],
                            "url"      : entry_contributor["url"] if "url" in entry_contributor else None,
                            "email"    : entry_contributor["email"] if "email" in entry_contributor else None}
                        cur.execute(sql, par)
                       #await asyncio.sleep(0)
                    sql = (
                        """
                        INSERT
                        INTO entries_properties_contents(
                            entry_id, text, type, base, lang)
                        VALUES(
                            :entry_id, :text, :type, :base, :lang)
                        """
                        )
                    par = {
                        "entry_id" : entry_id,
                        "text"     : entry["content"]["text"],
                        "type"     : entry["content"]["type"],
                        "base"     : entry["content"]["base"],
                        "lang"     : entry["content"]["lang"]}
                    cur.execute(sql, par)
                    for entry_link in entry["categories"]:
                        sql = (
                            """
                            INSERT
                            INTO entries_properties_tags(
                                entry_id, term, scheme, label)
                            VALUES(
                                :entry_id, :term, :scheme, :label)
                            """
                            )
                        par = {
                            "entry_id" : entry_id,
                            "term"     : entry_link["term"],
                            "scheme"   : entry_link["scheme"],
                            "label"    : entry_link["label"]}
                        cur.execute(sql, par)
                       #await asyncio.sleep(0)
                    for entry_link in entry["links"]:
                        sql = (
                            """
                            INSERT
                            INTO entries_properties_links(
                                entry_id, url, type, rel, size)
                            VALUES(
                                :entry_id, :url, :type, :rel, :size)
                            """
                            )
                        par = {
                            "entry_id" : entry_id,
                            "url"      : entry_link["href"],
                            "rel"      : entry_link["rel"],
                            "type"     : entry_link["type"],
                            "size"     : entry_link["length"]}
                        cur.execute(sql, par)
                       #await asyncio.sleep(0)
                sql = (
                    """
                    UPDATE feeds_state
                    SET renewed = :renewed
                    WHERE feed_id = :feed_id
                    """
                    )
                par = {
                    "renewed" : time.time(),
                    "feed_id" : feed_id
                    }
                cur.execute(sql, par)

    async def set_date(db_file, feed_id):
        """
        Set renewed date of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE feeds_state
                    SET renewed = :renewed
                    WHERE feed_id = :feed_id
                    """
                    )
                par = {
                    "renewed" : time.time(),
                    "feed_id" : feed_id
                    }
                # cur = conn.cursor()
                cur.execute(sql, par)

    async def update_feed_identifier(db_file, feed_id, identifier):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}; Identifier {identifier}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE feeds_properties
                    SET identifier = :identifier
                    WHERE id = :feed_id
                    """
                    )
                par = {
                    "identifier" : identifier,
                    "feed_id"    : feed_id
                    }
                cur.execute(sql, par)

    async def update_feed_status(db_file, feed_id, status_code):
        """
        Set status_code of feed_id in table status.

        Parameters
        ----------
        db_file : str
            Path to database file.
        url : str
            Feed URL.
        status : str
            Status ID or message.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        if status_code != 200:
            logger.debug(f"{function_name}	{db_file}	Status code {status_code}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE feeds_state
                    SET status_code = :status_code, scanned = :scanned
                    WHERE feed_id = :feed_id
                    """
                    )
                par = {
                    "status_code" : status_code,
                    "scanned"     : time.time(),
                    "feed_id"     : feed_id
                    }
                cur.execute(sql, par)

    async def update_feed_validity(db_file, feed_id, valid):
        """
        Set validity status of feed_id in table status.

        Parameters
        ----------
        db_file : str
            Path to database file.
        url : str
            Feed URL.
        valid : boolean
            0 or 1.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}; Valid {valid}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE feeds_state
                    SET valid = :valid
                    WHERE feed_id = :feed_id
                    """
                    )
                par = {
                    "valid"   : valid,
                    "feed_id" : feed_id
                    }
                cur.execute(sql, par)

    async def update_feed_properties(db_file, feed_id, atom):
        """
        Update properties of url in table feeds.

        Parameters
        ----------
        db_file : str
            Path to database file.
        url : str
            Feed URL.
        atom : dict
            Data of an Atom format document.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE feeds_properties
                    SET version = :version, encoding = :encoding,
                        language = :language, rating = :rating,
                        entries = :entries, icon = :icon, image = :image,
                        logo = :logo, ttl = :ttl, updated = :updated
                    WHERE id = :feed_id
                    """
                    )
                par = {
                    "version"  : atom["properties"]["version"],
                    "encoding" : atom["properties"]["encoding"],
                    "language" : atom["feed"]["language"],
                    "rating"   : "",
                    "entries"  : atom["properties"]["entries"],
                    "icon"     : atom["feed"]["icon"],
                    "image"    : "",
                    "logo"     : atom["feed"]["logo"],
                    "ttl"      : "",
                    "updated"  : atom["feed"]["updated"],
                    "feed_id"  : feed_id
                    }
                cur.execute(sql, par)

    async def maintain_archive(db_file, limit):
        """
        Maintain list of archived entries equal to specified number of items.

        Parameters
        ----------
        db_file : str
            Path to database file.
        limit : str
            Number of maximum entries to store.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Limit {limit}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    SELECT count(id)
                    FROM entries_state
                    WHERE archived = 1
                    """
                    )
                count = cur.execute(sql).fetchone()[0]
                # FIXME Upon first time joining to a groupchat
                # and then adding a URL, variable "limit"
                # becomes a string in one of the iterations.
                # if isinstance(limit,str):
                #     print("STOP")
                #     breakpoint()
                difference = count - int(limit)
                if difference > 0:
                    sql = (
                        """
                        DELETE
                        FROM entries_properties
                        WHERE id
                        IN (
                            SELECT entry_id
                            FROM entries_state
                            INNER JOIN entries_properties ON entries_state.entry_id = entries_properties.id
                            WHERE archived = 1
                            ORDER BY published ASC
                            LIMIT :difference
                            )
                        """
                        )
                    par = {
                        "difference" : difference
                        }
                    cur.execute(sql, par)

    def get_authors_by_entry_id(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Entry ID {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM entries_properties_authors
                WHERE entry_id = :entry_id
                ORDER BY name DESC
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_contributors_by_entry_id(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Entry ID {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM entries_properties_contributors
                WHERE entry_id = :entry_id
                ORDER BY name DESC
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_links_by_entry_id(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Entry ID {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM entries_properties_links
                WHERE entry_id = :entry_id
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_tags_by_entry_id(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Entry ID {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM entries_properties_tags
                WHERE entry_id = :entry_id
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    # NOTE fetch only relevant/specific items
    def get_contents_by_entry_id(db_file, entry_id):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Entry ID {entry_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM entries_properties_contents
                WHERE entry_id = :entry_id
                """
                )
            par = (entry_id,)
            result = cur.execute(sql, par).fetchall()
            return result

    async def process_invalid_entries(db_file, ixs):
        """
        Batch process of invalid items.

        Parameters
        ----------
        db_file : TYPE
            DESCRIPTION.
        ixs : TYPE
            DESCRIPTION.

        Returns
        -------
        None.

        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                for ix in ixs:
                    logger.debug(f"{function_name}	{db_file}	Index {ix}")
                    if ixs[ix] == 1:
                        sql = (
                            """
                            DELETE
                            FROM entries_properties
                            WHERE id = :ix
                            """
                            )
                    else:
                        sql = (
                            """
                            UPDATE entries_state
                            SET archived = 1
                            WHERE entry_id = :ix
                            """
                            )
                    par = (ix,)
                    cur.execute(sql, par)

    # TODO Move entries that don"t exist into table archive.
    # NOTE Entries that are marked as archived and are read are deleted.
    # NOTE Unlike entries from table entries, entries from
    #      table archive are not marked as read.
    def get_entries_of_feed(db_file, feed_id):
        """
        Get entries of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id, title, link, identifier, published
                FROM entries_properties
                WHERE feed_id = ?
                ORDER BY published DESC
                """
                )
            par = (feed_id,)
            items = cur.execute(sql, par).fetchall()
            return items

    def get_entries_recent(db_file: str, num: str) -> tuple:
        """
        Extract information from recent entries.

        Parameters
        ----------
        db_file : str
            Path to database file.
        num : str, optional
            Number. The default is None.

        Returns
        -------
        result : tuple
            News items.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Number {num}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id, title, link, summary_text, feed_id, published, updated
                FROM entries_properties
                ORDER BY published DESC
                LIMIT :num
                """
                )
            par = (num,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_entries_of_subscription(db_file: str, feed_id: str) -> tuple:
        """
        Get entries of a given subscription.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id, title, link, summary_text, identifier, published, updated
                FROM entries_properties
                WHERE feed_id = ?
                ORDER BY published DESC
                """
                )
            par = (feed_id,)
            items = cur.execute(sql, par).fetchall()
            return items

    # NOTE Instruction "ORDER BY" might not be needed.
    def get_entry_id_indices_by_feed_id(db_file, feed_id):
        """
        Get entries of given feed.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id
                FROM entries_properties
                WHERE feed_id = ?
                ORDER BY published DESC
                """
                )
            par = (feed_id,)
            indices = cur.execute(sql, par).fetchall()
            return indices

    # TODO What is this function for? 2024-01-02
    # def get_feeds(db_file):
    #     """
    #     Query table feeds for Title, URL, Categories, Tags.

    #     Parameters
    #     ----------
    #     db_file : str
    #         Path to database file.

    #     Returns
    #     -------
    #     result : tuple
    #         Title, URL, Categories, Tags of feeds.
    #     """
    #     with SQLiteGeneral.create_connection(db_file) as conn:
    #         cur = conn.cursor()
    #         sql = (
    #             "SELECT name, address, type, categories, tags "
    #             "FROM feeds"
    #             )
    #         result = cur.execute(sql).fetchall()
    #         return result

    # TODO select by "feed_id" (of table "status") from
    # "feed" urls that are enabled in table "status"
    def get_feeds_url(db_file):
        """
        Query table feeds for URLs.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        result : tuple
            URLs of active feeds.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT url
                FROM feeds_properties
                """
                )
            result = cur.execute(sql).fetchall()
            return result

    def get_feeds_by_enabled_state(db_file, enabled_state):
        """
        Query table feeds by enabled state.

        Parameters
        ----------
        db_file : str
            Path to database file.
        enabled_state : boolean
            False or True.

        Returns
        -------
        result : tuple
            List of URLs.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Enabled State {enabled_state}")
        enabled_state = 1 if enabled_state else 0
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT feeds_properties.*
                FROM feeds_properties
                INNER JOIN feeds_preferences ON feeds_properties.id = feeds_preferences.feed_id
                WHERE feeds_preferences.enabled = ?
                """
                )
            par = (enabled_state,)
            result = cur.execute(sql, par).fetchall()
            return result

    def get_feeds_and_enabled_state(db_file):
        """
        Select table feeds and join column enabled.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        result : tuple
            List of URLs.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT feeds_properties.*, feeds_preferences.enabled
                FROM feeds_properties
                INNER JOIN feeds_preferences ON feeds_properties.id = feeds_preferences.feed_id
                ORDER BY feeds_properties.title ASC
                """
                )
            result = cur.execute(sql).fetchall()
            return result

    def get_active_feeds_url(db_file):
        """
        Query table feeds for active URLs.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        result : tuple
            URLs of active feeds.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT feeds_properties.url
                FROM feeds_properties
                INNER JOIN feeds_preferences ON feeds_properties.id = feeds_preferences.feed_id
                WHERE feeds_preferences.enabled = 1
                """
                )
            result = cur.execute(sql).fetchall()
            return result

    def get_active_feeds_url_sorted_by_last_scanned(db_file):
        """
        Query table feeds for active URLs and sort them by last scanned time.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        result : tuple
            URLs of active feeds.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT feeds_properties.url
                FROM feeds_properties
                INNER JOIN feeds_preferences ON feeds_properties.id = feeds_preferences.feed_id
                INNER JOIN feeds_state ON feeds_properties.id = feeds_state.feed_id
                WHERE feeds_preferences.enabled = 1
                ORDER BY feeds_state.scanned
                """
                )
            result = cur.execute(sql).fetchall()
            return result

    def get_tags(db_file):
        """
        Query table tags and list items.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        result : tuple
            List of tags.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT tag, id
                FROM tags
                ORDER BY tag
                """
                )
            result = cur.execute(sql).fetchall()
            return result

    def get_subscriptions_indices(db_file):
        """
        Query table feeds and list items.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        result : tuple
            URLs of feeds.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id
                FROM feeds_properties
                """
                )
            result = cur.execute(sql).fetchall()
            return result

    def get_subscription_links(db_file, feed_id):
        """
        Get links of a given subscription index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Subscription index.

        Returns
        -------
        links : list
            Links.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT *
                FROM feeds_properties_links
                WHERE feed_id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchall()
        return result

    def get_subscription_locale_code(db_file, feed_id):
        """
        Get locale code of a given subscription index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Subscription index.

        Returns
        -------
        locale_code : str
            Locale code.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT language
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_subscription_title(db_file, feed_id):
        """
        Get title of a given subscription index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Subscription index.

        Returns
        -------
        title : str
            Title.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT title
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_subscription_subtitle(db_file, feed_id):
        """
        Get subtitle of a given subscription index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Subscription index.

        Returns
        -------
        Subtitle : str
            Subtitle.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT subtitle
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_subscription_icon(db_file, feed_id):
        """
        Get icon of a given subscription index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Subscription index.

        Returns
        -------
        icon : str
            URI.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT icon
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_subscription_logo(db_file, feed_id):
        """
        Get logo of a given subscription index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Subscription index.

        Returns
        -------
        logo : str
            URI.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT logo
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_subscription_uri(db_file, feed_id):
        """
        Get uri of a given subscription index.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Subscription index.

        Returns
        -------
        uri : str
            URI.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID: {feed_id}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT url
                FROM feeds_properties
                WHERE id = ?
                """
                )
            par = (feed_id,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_feeds(db_file):
        """
        Query table feeds and list items.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        result : tuple
            URLs of feeds.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        # TODO
        # 1) Select id from table feeds
        #    Select name, url (feeds) updated, enabled, feed_id (status)
        # 2) Sort feeds by id. Sort status by feed_id
        # result += cur.execute(sql).fetchall()
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id, title, url
                FROM feeds_properties
                ORDER BY title
                """
                )
            result = cur.execute(sql).fetchall()
            return result

    def get_last_entries(db_file, num):
        """
        Query entries.

        Parameters
        ----------
        db_file : str
            Path to database file.
        num : str
            Number.

        Returns
        -------
        titles_list : tuple
            List of recent N entries as message.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Number {num}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            # sql = (
            #     "SELECT title, link "
            #     "FROM entries "
            #     "ORDER BY ROWID DESC "
            #     "LIMIT :num"
            #     )
            sql = (
                """
                SELECT title, link, published
                FROM entries_properties
                INNER JOIN entries_state ON entries_properties.id = entries_state.entry_id
                WHERE entries_state.read = 0
                ORDER BY published DESC
                LIMIT :num
                """
                )
            par = (num,)
            result = cur.execute(sql, par).fetchall()
            return result

    def search_feeds(db_file, query):
        """
        Query feeds.

        Parameters
        ----------
        db_file : str
            Path to database file.
        query : str
            Search query.

        Returns
        -------
        result : tuple
            Feeds of specified keywords as message.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Query {query}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id, title, url
                FROM feeds_properties
                WHERE title LIKE ?
                OR url LIKE ?
                LIMIT 50
                """
                )
            par = [f"%{query}%", f"%{query}%"]
            result = cur.execute(sql, par).fetchall()
            return result

    def search_entries(db_file, query):
        """
        Query entries.

        Parameters
        ----------
        db_file : str
            Path to database file.
        query : str
            Search query.

        Returns
        -------
        titles_list : tuple
            Entries of specified keywords as message.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Query {query}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            sql = (
                """
                SELECT title, link
                FROM entries_properties
                WHERE title LIKE ?
                LIMIT 50
                """
                )
            par = [f"%{query}%"]
            conn.row_factory = Row
            cur = conn.cursor()
            result = cur.execute(sql, par).fetchall()
            return result

    """
    FIXME

    Error due to missing date, but it appears that date is present:
    ERROR DATE: source = https://blog.heckel.io/feed/
    ERROR DATE: date = 2008-05-13T13:51:50+00:00
    ERROR DATE: result = https://blog.heckel.io/feed/

    19:32:05 ERROR DATE: source = https://mwl.io/feed
    19:32:05 ERROR DATE: date = 2023-11-30T10:56:39+00:00
    19:32:05 ERROR DATE: result = https://mwl.io/feed
    19:32:05 ERROR DATE: source = https://mwl.io/feed
    19:32:05 ERROR DATE: date = 2023-11-22T16:59:08+00:00
    19:32:05 ERROR DATE: result = https://mwl.io/feed
    19:32:06 ERROR DATE: source = https://mwl.io/feed
    19:32:06 ERROR DATE: date = 2023-11-16T10:33:57+00:00
    19:32:06 ERROR DATE: result = https://mwl.io/feed
    19:32:06 ERROR DATE: source = https://mwl.io/feed
    19:32:06 ERROR DATE: date = 2023-11-09T07:37:57+00:00
    19:32:06 ERROR DATE: result = https://mwl.io/feed

    """
    def check_entry_exist(db_file, feed_id, identifier=None, title=None, link=None,
                          published=None):
        """
        Check whether an entry exists.
        If entry has an ID, check by ID.
        If entry has timestamp (published), check by title, link and date.
        Otherwise, check by title and link.

        Parameters
        ----------
        db_file : str
            Path to database file.
        feed_id : str
            Feed Id.
        identifier : str, optional
            Entry ID. The default is None.
        title : str, optional
            Entry title. The default is None.
        link : str, optional
            Entry URL. The default is None.
        published : str, optional
            Entry Timestamp. The default is None.

        Returns
        -------
        bool
            True or None.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Feed ID {feed_id}")
        exist = False
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            if identifier:
                sql = (
                    """
                    SELECT id
                    FROM entries_properties
                    WHERE identifier = :identifier
                      AND feed_id = :feed_id
                    """
                    )
                par = {
                    "identifier": identifier,
                    "feed_id": feed_id
                    }
                result = cur.execute(sql, par).fetchone()
                if result: exist = True
            elif published:
                sql = (
                    """
                    SELECT id
                    FROM entries_properties
                    WHERE title = :title
                      AND link = :link
                      AND published = :date
                    """
                    )
                par = {
                    "title": title,
                    "link": link,
                    "date": published
                    }
                try:
                    result = cur.execute(sql, par).fetchone()
                    if result: exist = True
                except:
                    logger.error("source =" + feed_id)
                    logger.error("published =" + published)
            else:
                sql = (
                    """
                    SELECT id
                    FROM entries_properties
                    WHERE title = :title
                      AND link = :link
                    """
                    )
                par = {
                    "title": title,
                    "link": link
                    }
                result = cur.execute(sql, par).fetchone()
                if result: exist = True
            # try:
            #     if result:
            #         return True
            #     else:
            #         return None
            # except:
            #     print(current_time(), "ERROR DATE: result =", url)
            return exist

    def get_entry_id_by_identifier(db_file, identifier):
        """
        Get entry ID by its identifier.

        Parameters
        ----------
        db_file : str
            Path to database file.
        identifier : str
            Entry identifier.

        Returns
        -------
        result : tuple
            Entry ID or None.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Identifier {identifier}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT id
                FROM entries_properties
                WHERE identifier = ?
                """
                )
            par = (identifier,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_entry_identifier(db_file, ix):
        """
        Get identifier by its entry ID.

        Parameters
        ----------
        db_file : str
            Path to database file.
        id : str
            Entry ID.

        Returns
        -------
        result : tuple
            Entry ID or None.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Index {ix}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT identifier
                FROM entries_properties
                WHERE id = :ix
                """
                )
            par = {
                "ix": ix
                }
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def set_initial_setting_value(db_file, key, value):
        """
        Set initial setting value.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key_value : list
             key : str
                   enabled, interval, masters, quantum, random.
             value : int
                   Numeric value.
        """

        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}; Value {value}")

        # NOTE This is not a good practice!
        # When INI file was used, all values were strings.
        # When TOML is now used, integers are integers, which means that
        # statement "if not val" is equivalent to "if not 0" which is not so to
        # statement "if not "0""

        # if not val:
        #     raise Exception("Missing value for key "{}" ({}).".format(key, db_file))
            # logger.error("Missing value for key "{}" ({}).".format(key, db_file))
            # return

        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                INSERT
                INTO settings(
                    key, value)
                VALUES(
                    :key, :value)
                """
                )
            par = {
                "key": key,
                "value": value
                }
            cur.execute(sql, par)

    async def set_setting_value(db_file, key, value):
        """
        Set setting value.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key : str
              enabled, interval, masters, quantum, random.
        value : int
              Numeric value.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}; Value {val}")

        # NOTE This is not a good practice!
        # When INI file was used, all values were strings.
        # When TOML is now used, integers are integers, which means that
        # statement "if not val" is equivalent to "if not 0" which is not so to
        # statement "if not "0""

        # if not val:
        #     raise Exception("Missing value for key "{}" ({}).".format(key, db_file))
            # logger.error("Missing value for key "{}" ({}).".format(key, db_file))
            # return

        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    INSERT
                    INTO settings(
                        key, value)
                    VALUES(
                        :key, :val)
                    """
                    )
                par = {
                    "key": key,
                    "val": val
                    }
                cur.execute(sql, par)

    async def update_setting_value(db_file: str, key: str, value: int):
        """
        Update setting value.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key : str
              A key: amount, enabled, interval, masters, random.
        value : int
              A numeric value.
        """
        # if isinstance(key_value, list):
        #     key = key_value[0]
        #     val = key_value[1]
        # elif key_value == "enable":
        #     key = "enabled"
        #     val = 1
        # else:
        #     key = "enabled"
        #     val = 0

        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}; Value {value}")

        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE settings
                    SET value = :value
                    WHERE key = :key
                    """
                    )
                par = {
                    "key": key,
                    "value": value
                    }
                cur.execute(sql, par)
                # except:
                #     logging.debug(
                #         "No specific value set for key {}.".format(key)
                #         )

    async def delete_filter(db_file, key):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM filters
                    WHERE key = ?
                    """
                    )
                par = (key,)
                cur.execute(sql, par)

    async def delete_setting(db_file, key):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM settings
                    WHERE key = ?
                    """
                    )
                par = (key,)
                cur.execute(sql, par)

    async def delete_settings(db_file):
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    DELETE
                    FROM settings
                    """
                    )
                cur.execute(sql)

    def get_setting_value(db_file, key):
        """
        Get settings value.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key : str
            Key: archive, enabled, filter-allow, filter-deny,
                 interval, length, old, quantum, random.

        Returns
        -------
        val : str
            Numeric value.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT value
                FROM settings
                WHERE key = ?
                """
                )
            par = (key,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def is_setting_key(db_file, key):
        """
        Check whether setting key exist.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key : str
            Key: allow, deny.

        Returns
        -------
        key : str
            Key.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT key
                FROM settings
                WHERE key = ?
                """
                )
            par = (key,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    async def set_filter_value(db_file, key_value):
        """
        Set settings value.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key_value : list
             key : str
                   allow, deny, replace.
             value : int
                   Numeric value.
        """
        key = key_value[0]
        val = key_value[1]

        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}; Value {val}")

        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    INSERT
                    INTO filters(
                        key, value)
                    VALUES(
                        :key, :val)
                    """
                    )
                par = {
                    "key" : key,
                    "val" : val}
                cur.execute(sql, par)

    async def update_filter_value(db_file, key_value):
        """
        Update settings value.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key_value : list
             key : str
                   allow, deny, replace.
             value : int
                   Numeric value.
        """
        # if isinstance(key_value, list):
        #     key = key_value[0]
        #     val = key_value[1]
        # elif key_value == "enable":
        #     key = "enabled"
        #     val = 1
        # else:
        #     key = "enabled"
        #     val = 0
        key = key_value[0]
        val = key_value[1]

        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}; Value {val}")

        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE filters
                    SET value = :value
                    WHERE key = :key
                    """
                    )
                par = {
                    "key"   : key,
                    "value" : val}
                cur.execute(sql, par)

    def is_filter_key(db_file, key):
        """
        Check whether filter key exist.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key : str
            Key: allow, deny.

        Returns
        -------
        key : str
            Key.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT key
                FROM filters
                WHERE key = ?
                """
                )
            par = (key,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    def get_filter_value(db_file, key):
        """
        Get filter value.

        Parameters
        ----------
        db_file : str
            Path to database file.
        key : str
            Key: allow, deny.

        Returns
        -------
        value : str
            List of strings.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        logger.debug(f"{function_name}	{db_file}	Key {key}")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT value
                FROM filters
                WHERE key = ?
                """
                )
            par = (key,)
            result = cur.execute(sql, par).fetchone()
            return result[0] if result else None

    async def set_last_update_time(db_file):
        """
        Set value of last_update.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        None.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    INSERT
                    INTO status(
                        key, value)
                    VALUES(
                        :key, :value)
                    """
                    )
                par = {
                    "key"   : "last_update",
                    "value" : time.time()}
                cur.execute(sql, par)

    def get_last_update_time(db_file):
        """
        Get value of last_update.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        val : str
            Time.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        with SQLiteGeneral.create_connection(db_file) as conn:
            cur = conn.cursor()
            sql = (
                """
                SELECT value
                FROM status
                WHERE key = "last_update"
                """
                )
            result = cur.execute(sql).fetchone()
            return result[0] if result else None

    async def update_last_update_time(db_file):
        """
        Update value of last_update.

        Parameters
        ----------
        db_file : str
            Path to database file.

        Returns
        -------
        None.
        """
        function_name = sys._getframe().f_code.co_name
        logger.debug(f"{function_name}	{db_file}	Start")
        async with DBLOCK:
            with SQLiteGeneral.create_connection(db_file) as conn:
                cur = conn.cursor()
                sql = (
                    """
                    UPDATE status
                    SET value = :value
                    WHERE key = "last_update"
                    """
                    )
                par = {"value" : time.time()}
                cur.execute(sql, par)
