# Path: animanager/db/query/collections.py
# class Anime:
#     aid: int = 0
#     title: str = ''
#     type: str = ''
#     episodecount: int = 0
#     startdate: int = 0
#     enddate: int = 0
#     watched_episodes: int = 0
#     complete: bool = False
#     regexp: str = ''
#     episodes: 'List[Episode]' = ()
# 
# class Episode:
#     aid: int = 0
#     type: str = ''
#     number: int = 0
#     title: str = ''
#     length: int = 0
#     user_watched: bool = False
# 
# Path: animanager/db/query/status.py
# STATUS_FIELDS = {
#     'complete': 'complete',
#     'watched_episodes': 'watched_episodes',
# }
# 
# def cache_status(db, aid, force=False):
#     """Calculate and cache status for given anime.
# 
#     Don't do anything if status already exists and force is False.
# 
#     """
#     with db:
#         cur = db.cursor()
#         if not force:
#             # We don't do anything if we already have this aid in our
#             # cache.
#             cur.execute('SELECT 1 FROM cache_anime WHERE aid=?', (aid,))
#             if cur.fetchone() is not None:
#                 return
# 
#         # Retrieve information for determining complete.
#         cur.execute(
#             'SELECT episodecount, enddate FROM anime WHERE aid=?', (aid,))
#         row = cur.fetchone()
#         if row is None:
#             raise ValueError('aid provided does not exist')
#         episodecount, enddate = row
# 
#         # Select all regular episodes in ascending order.
#         cur.execute("""
#             SELECT number, user_watched FROM episode
#             WHERE aid=? AND type=?
#             ORDER BY number ASC
#             """, (aid, get_eptype(db, 'regular').id))
# 
#         # We find the last consecutive episode that is user_watched.
#         number = 0
#         for number, watched in cur:
#             # Once we find the first unwatched episode, we set the last
#             # consecutive watched episode to the previous episode (or 0).
#             if watched == 0:
#                 number -= 1
#                 break
#         # We store this in the cache.
#         set_status(db, aid, enddate and episodecount <= number, number)


import logging
from typing import Any, Iterable, Iterator, Mapping, Sequence, Union
from .collections import Anime, Episode
from .status import STATUS_FIELDS, cache_status
        episode_fields: FieldsParam = (),
) -> Iterator[Anime]:
    """Perform an arbitrary SQL SELECT WHERE on the anime table.

    By nature of "arbitrary query", this is vulnerable to injection, use only
    trusted values for `where_query`.

    This will "lazily" fetch the requested fields as needed.  For example,
    episodes (which require a separate query per anime) will only be fetched if
    `episode_fields` is provided.  Anime status will be cached only if status
    fields are requested.

    :param str where_query: SELECT WHERE query
    :param where_params: parameters for WHERE query
    :param fields: anime fields to get.  If :const:`ALL`, get all fields.
        Default is :const:`ALL`.
    :param episode_fields: episode fields to get.
        If :const:`ALL`, get all fields.  If empty, don't get episodes.
        `fields` must contain 'aid' to get episodes.
    :param bool force_status: whether to force status calculation.
    :returns: iterator of Anime

    """

    logger.debug(
        'select(%r, %r, %r, %r, %r)',
        db, where_query, where_params, fields, episode_fields)
    fields = _clean_fields(ANIME_FIELDS, fields)
    if not fields:
        raise ValueError('Fields cannot be empty')

# Path: animanager/db/query/collections.py
# class Anime:
#     aid: int = 0
#     title: str = ''
#     type: str = ''
#     episodecount: int = 0
#     startdate: int = 0
#     enddate: int = 0
#     watched_episodes: int = 0
#     complete: bool = False
#     regexp: str = ''
#     episodes: 'List[Episode]' = ()
# 
# class Episode:
#     aid: int = 0
#     type: str = ''
#     number: int = 0
#     title: str = ''
#     length: int = 0
#     user_watched: bool = False
# 
# Path: animanager/db/query/status.py
# STATUS_FIELDS = {
#     'complete': 'complete',
#     'watched_episodes': 'watched_episodes',
# }
# 
# def cache_status(db, aid, force=False):
#     """Calculate and cache status for given anime.
# 
#     Don't do anything if status already exists and force is False.
# 
#     """
#     with db:
#         cur = db.cursor()
#         if not force:
#             # We don't do anything if we already have this aid in our
#             # cache.
#             cur.execute('SELECT 1 FROM cache_anime WHERE aid=?', (aid,))
#             if cur.fetchone() is not None:
#                 return
# 
#         # Retrieve information for determining complete.
#         cur.execute(
#             'SELECT episodecount, enddate FROM anime WHERE aid=?', (aid,))
#         row = cur.fetchone()
#         if row is None:
#             raise ValueError('aid provided does not exist')
#         episodecount, enddate = row
# 
#         # Select all regular episodes in ascending order.
#         cur.execute("""
#             SELECT number, user_watched FROM episode
#             WHERE aid=? AND type=?
#             ORDER BY number ASC
#             """, (aid, get_eptype(db, 'regular').id))
# 
#         # We find the last consecutive episode that is user_watched.
#         number = 0
#         for number, watched in cur:
#             # Once we find the first unwatched episode, we set the last
#             # consecutive watched episode to the previous episode (or 0).
#             if watched == 0:
#                 number -= 1
#                 break
#         # We store this in the cache.
#         set_status(db, aid, enddate and episodecount <= number, number)


# Path: animanager/db/query/select.py
import logging
from typing import Any, Iterable, Iterator, Mapping, Sequence, Union
from .collections import Anime, Episode
from .status import STATUS_FIELDS, cache_status

        episode_fields: FieldsParam = (),
) -> Iterator[Anime]:
    """Perform an arbitrary SQL SELECT WHERE on the anime table.

    By nature of "arbitrary query", this is vulnerable to injection, use only
    trusted values for `where_query`.

    This will "lazily" fetch the requested fields as needed.  For example,
    episodes (which require a separate query per anime) will only be fetched if
    `episode_fields` is provided.  Anime status will be cached only if status
    fields are requested.

    :param str where_query: SELECT WHERE query
    :param where_params: parameters for WHERE query
    :param fields: anime fields to get.  If :const:`ALL`, get all fields.
        Default is :const:`ALL`.
    :param episode_fields: episode fields to get.
        If :const:`ALL`, get all fields.  If empty, don't get episodes.
        `fields` must contain 'aid' to get episodes.
    :param bool force_status: whether to force status calculation.
    :returns: iterator of Anime

    """

    logger.debug(
        'select(%r, %r, %r, %r, %r)',
        db, where_query, where_params, fields, episode_fields)
    fields = _clean_fields(ANIME_FIELDS, fields)
    if not fields:
        raise ValueError('Fields cannot be empty')

    if set(fields) & STATUS_FIELDS.keys():