Repository: xbmc/xbmc
File: xbmc/video/VideoDatabase.cpp (C++)

Code before:

    sql = PrepareSQL("UPDATE art SET media_id=%i WHERE media_id=%i AND media_type='videoversion'",
                     idFile, oldIdFile);
    m_pDS->exec(sql);

    sql = PrepareSQL("SELECT 1 FROM streamdetails WHERE idFile = %i", idFile);
    m_pDS->query(sql);
    if (m_pDS->eof())
    {
      sql = PrepareSQL("UPDATE streamdetails SET idFile=%i WHERE idFile=%i", idFile, oldIdFile);
      m_pDS->exec(sql);
    }
    DeleteFile(oldIdFile);

    return true;
  }
  catch (...)
  {
    CLog::Log(LOGERROR, "{} ({}) failed", __FUNCTION__, idFile);
  }
  return false;
}

void CVideoDatabase::DeleteFile(int idFile)
{
  // First check not multiple references to file (eg. episodes)
  std::string sql{PrepareSQL("SELECT idFile FROM movie WHERE idFile = %i UNION SELECT idFile FROM "
                             "episode WHERE idFile = %i UNION SELECT idFile FROM "
                             "videoversion WHERE idFile = %i",
                             idFile, idFile, idFile)};
  m_pDS->query(sql);
  if (m_pDS->eof())
  {
    // Get idPath
    sql = PrepareSQL("SELECT path.idPath, path.strPath FROM path JOIN files ON path.idPath = "
                     "files.idPath WHERE idFile = %i",
                     idFile);
    m_pDS->query(sql);
    int idPath{-1};
    std::string strPath;
    if (!m_pDS->eof())
    {
      idPath = m_pDS->fv("idPath").get_asInt();
      strPath = m_pDS->fv("strPath").get_asString();
    }

    // Associated bookmarks and streamdetails deleted by delete trigger
    sql = PrepareSQL("DELETE FROM files WHERE idFile = %i", idFile);
    m_pDS->exec(sql);

    // Delete path if orphan (and not base directory - needs to remain to prevent re-adding on library update)

Review comment (bug):
suggestion: // First check for references to the file
A single reference is enough to abort, right?

A bool return status for errors would be good and would let the caller react.
A remaining reference is not an error and could just be logged at LOGDEBUG level.

Code after:
                     "FROM streamdetails WHERE idFile=%i)",
                     idFile, oldIdFile, idFile);
    m_pDS->exec(sql);

    return DeleteFile(oldIdFile);
  }
  catch (...)
  {
    CLog::LogF(LOGERROR, " idFile {}, fileAndPath {}, idMovie {}, oldIdFile {} - failed", idFile,
               fileAndPath, idMovie, oldIdFile);
  }
  return false;
}

bool CVideoDatabase::DeleteFile(int idFile)
{
  try
  {
    // First check no other references to file (eg. other episodes)
    std::string sql{PrepareSQL("SELECT idFile FROM movie WHERE idFile = %i "
                               "UNION SELECT idFile FROM episode WHERE idFile = %i "
                               "UNION SELECT idFile FROM videoversion WHERE idFile = %i",
                               idFile, idFile, idFile)};
    m_pDS->query(sql);
    if (m_pDS->eof())
    {
      // Get idPath
      sql = PrepareSQL("SELECT path.idPath, path.strPath FROM path "
                       "JOIN files ON path.idPath = files.idPath "
                       "WHERE idFile = %i",
                       idFile);
      m_pDS->query(sql);
      int idPath{-1};
      std::string strPath;
      if (!m_pDS->eof())
      {
        idPath = m_pDS->fv("idPath").get_asInt();
        strPath = m_pDS->fv("strPath").get_asString();
      }

      // Associated bookmarks and streamdetails deleted by delete trigger
      sql = PrepareSQL("DELETE FROM files WHERE idFile = %i", idFile);
      m_pDS->exec(sql);

      // Delete path if orphan (and not base directory - needs to remain to prevent re-adding on library update)
      if (idPath >= 0 && URIUtils::IsBlurayPath(strPath))
      {
        sql = PrepareSQL("DELETE FROM path WHERE idPath = %i "
                         "AND NOT EXISTS (SELECT 1 FROM files WHERE files.idPath = %i)",
                         idPath, idPath);
        m_pDS->exec(sql);