chromium/chromeos/ash/components/file_manager/indexing/term_table.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/file_manager/indexing/term_table.h"

#include "sql/statement.h"

namespace ash::file_manager {

namespace {

#define TERM_TABLE "term_table"
#define TERM_ID "term_id"
#define FIELD_NAME "field_name"
#define TOKEN_ID "token_id"

// The statement used to create the term table.
static constexpr char kCreateTermTableQuery[] =
    // clang-format off
    "CREATE TABLE IF NOT EXISTS " TERM_TABLE "("
      TERM_ID " INTEGER PRIMARY KEY AUTOINCREMENT,"
      FIELD_NAME " TEXT NOT NULL,"
      TOKEN_ID " INTEGER NOT NULL REFERENCES term_table(token_id),"
      "UNIQUE(" FIELD_NAME ", " TOKEN_ID "))";
// clang-format on

// The statement used to insert a new term into the table.
static constexpr char kInsertTermQuery[] =
    // clang-format off
    "INSERT OR REPLACE INTO " TERM_TABLE "(" FIELD_NAME ", "
    TOKEN_ID ") VALUES (?, ?) RETURNING " TERM_ID;
// clang-format on

// The statement used to delete an term ID from the database by term_id.
static constexpr char kDeleteTermQuery[] =
    // clang-format off
    "DELETE FROM " TERM_TABLE " WHERE " TERM_ID "=? "
    "RETURNING " TERM_ID;
// clang-format on

// The statement used fetch the term ID by field name and token ID.
static constexpr char kGetTermIdQuery[] =
    // clang-format off
    "SELECT " TERM_ID " FROM " TERM_TABLE " "
    "WHERE " FIELD_NAME "=? AND " TOKEN_ID "=?";
// clang-format on

}  // namespace

TermTable::TermTable(sql::Database* db) : db_(db) {}
TermTable::~TermTable() = default;

bool TermTable::Init() {
  if (!db_->is_open()) {
    LOG(WARNING) << "Faield to initialize term_table "
                 << "due to closed database";
    return false;
  }
  sql::Statement create_table(
      db_->GetCachedStatement(SQL_FROM_HERE, kCreateTermTableQuery));
  DCHECK(create_table.is_valid()) << "Invalid create the table statement: \""
                                  << create_table.GetSQLStatement() << "\"";
  if (!create_table.Run()) {
    LOG(ERROR) << "Failed to create term_table";
    return false;
  }
  return true;
}

int64_t TermTable::GetTermId(const std::string& field_name,
                             int64_t token_id) const {
  sql::Statement get_term_id(
      db_->GetCachedStatement(SQL_FROM_HERE, kGetTermIdQuery));
  DCHECK(get_term_id.is_valid()) << "Invalid get term ID statement: \""
                                 << get_term_id.GetSQLStatement() << "\"";
  get_term_id.BindString(0, field_name);
  get_term_id.BindInt64(1, token_id);
  if (get_term_id.Step()) {
    return get_term_id.ColumnInt64(0);
  }
  return -1;
}

int64_t TermTable::GetOrCreateTermId(const std::string& field_name,
                                     int64_t token_id) {
  int64_t term_id = GetTermId(field_name, token_id);
  if (term_id != -1) {
    return term_id;
  }
  sql::Statement insert_term(
      db_->GetCachedStatement(SQL_FROM_HERE, kInsertTermQuery));
  DCHECK(insert_term.is_valid()) << "Invalid insert term statement: \""
                                 << insert_term.GetSQLStatement() << "\"";
  insert_term.BindString(0, field_name);
  insert_term.BindInt64(1, token_id);
  if (insert_term.Step()) {
    return insert_term.ColumnInt64(0);
  }
  LOG(ERROR) << "Failed to insert term " << field_name << ":" << token_id;
  return -1;
}

int64_t TermTable::DeleteTermById(int64_t term_id) {
  sql::Statement delete_term(
      db_->GetCachedStatement(SQL_FROM_HERE, kDeleteTermQuery));
  DCHECK(delete_term.is_valid()) << "Invalid delete term statement: \""
                                 << delete_term.GetSQLStatement() << "\"";
  delete_term.BindInt64(0, term_id);
  if (!delete_term.Step()) {
    if (!delete_term.Succeeded()) {
      LOG(ERROR) << "Failed to delete term " << term_id;
    }
    return -1;
  }
  return delete_term.ColumnInt64(0);
}

}  // namespace ash::file_manager