/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in, software
 * distributed under the License is distributed on an "AS IS",
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SRC_TRACE_PROCESSOR_SQLITE_BINDINGS_SQLITE_MODULE_H_
#define SRC_TRACE_PROCESSOR_SQLITE_BINDINGS_SQLITE_MODULE_H_

#include <sqlite3.h>

#include "perfetto/base/logging.h"

namespace perfetto::trace_processor::sqlite {

// Prototype for a virtual table (vtab) module which can be registered with
// SQLite.
//
// See https://www.sqlite.org/vtab.html for how to implement this class.
template <typename Impl>
struct Module {
  // Specifies the type of module: implementations can override this field by
  // declaring and defining it.
  //
  // Specifying this to kCreateOnly requires that the |Create| and |Destroy|
  // functions are defined.
  //
  // See the SQLite documentation on what these types mean.
  static constexpr enum { kEponymousOnly, kCreateOnly } kType = kCreateOnly;

  // Specifies whether this table is supports making changes to it:
  // implementations can override this field by declaring and defining it.
  //
  // Setting this to true requires the |Update| function to be defined.
  static constexpr bool kSupportsWrites = true;

  // Specifies whether this table supports overloading functions:
  // implementations can override this field by declaring and defining it.
  //
  // Setting this to true requires that the |FindFunction| function is defined.
  static constexpr bool kDoesOverloadFunctions = true;

  // Specifies whether this module supports transactions. Implementations can
  // override this field by declaring and defining it.
  //
  // Setting this to true requires that all transaction functions are declared
  // and defined.
  static constexpr bool kDoesSupportTransactions = false;

  // Specifies the type of context for the module. Implementations should define
  // this type to match the context type which is expected to be passed into
  // |sqlite3_create_module|.
  using Context = void;

  // Specifies the type for the vtab created by this module.
  //
  // Implementations should define this type to match the vtab type they use in
  // |Create| and |Connect|.
  using Vtab = sqlite3_vtab;

  // Specifies the type for the cursor created by this module.
  //
  // Implementations should define this type to match the cursor type they use
  // in |Open| and |Close|.
  using Cursor = sqlite3_vtab_cursor;

  // Creates a new instance of a virtual table and its backing storage.
  //
  // Implementations MUST define this function themselves if
  // |kType| == |kCreateOnly|; this function is declared but *not* defined so
  // linker errors will be thrown if not defined.
  static int Create(sqlite3*,
                    void*,
                    int,
                    const char* const*,
                    sqlite3_vtab**,
                    char**);

  // Destroys the virtual table and its backing storage.
  //
  // Implementations MUST define this function themselves if
  // |kType| == |kCreateOnly|; this function is declared but *not* defined so
  // linker errors will be thrown if not defined.
  static int Destroy(sqlite3_vtab*);

  // Creates a new instance of the virtual table, connecting to existing
  // backing storage.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Connect(sqlite3*,
                     void*,
                     int,
                     const char* const*,
                     sqlite3_vtab**,
                     char**);

  // Destroys the virtual table but *not* its backing storage.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Disconnect(sqlite3_vtab*);

  // Specifies filtering and cost information for the query planner.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int BestIndex(sqlite3_vtab*, sqlite3_index_info*);

  // Opens a cursor into the given vtab.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Open(sqlite3_vtab*, sqlite3_vtab_cursor**);

  // Closes the cursor.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Close(sqlite3_vtab_cursor*);

  // Resets this cursor to filter rows matching the provided set of filter
  // constraints and order by clauses.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Filter(sqlite3_vtab_cursor*,
                    int,
                    const char*,
                    int,
                    sqlite3_value**);

  // Forwards the cursor to point to the next row.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Next(sqlite3_vtab_cursor*);

  // Returns 1 if the cursor has reached its end or 0 otherwise.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Eof(sqlite3_vtab_cursor*);

  // Returns the value column at the given index for the current row the cursor
  // points to.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Column(sqlite3_vtab_cursor*, sqlite3_context*, int);

  // Returns the rowid for the current row.
  //
  // Implementations MUST define this function themselves; this function is
  // declared but *not* defined so linker errors will be thrown if not defined.
  static int Rowid(sqlite3_vtab_cursor*, sqlite_int64*);

  // Inserts/deletes/updates one row.
  //
  // Implementations MUST define this function themselves if
  // |kSupportsWrites| == |true|; this function is declared but *not* defined so
  // linker errors will be thrown if not defined.
  static int Update(sqlite3_vtab*, int, sqlite3_value**, sqlite_int64*);

  // Overloads a function with the given name when executed with a vtab column
  // as the first argument.
  //
  // Implementations MUST define this function themselves if
  // |kDoesOverloadFunctions| == |true|; this function is declared but *not*
  // defined so linker errors will be thrown if not defined.
  using FindFunctionFn = void(sqlite3_context*, int, sqlite3_value**);
  static int FindFunction(sqlite3_vtab*,
                          int,
                          const char*,
                          FindFunctionFn**,
                          void**);

  // Callback functions required to implement support for SQLite transactions.
  //
  // Implementations *must* define these funcitons themselves if
  // `kDoesSupportTransactions` is true; these function is declared but *not*
  // defined so linker errors will be thrown if not defined.
  static int Begin(sqlite3_vtab*);
  static int Sync(sqlite3_vtab*);
  static int Commit(sqlite3_vtab*);
  static int Rollback(sqlite3_vtab*);
  static int Savepoint(sqlite3_vtab*, int);
  static int Release(sqlite3_vtab*, int);
  static int RollbackTo(sqlite3_vtab*, int);

  // Helper function to cast the module context pointer to the correct type.
  static auto GetContext(void* ctx) {
    return static_cast<typename Impl::Context*>(ctx);
  }

  // Helper function to cast the vtab pointer to the correct type.
  static auto GetVtab(sqlite3_vtab* vtab) {
    return static_cast<typename Impl::Vtab*>(vtab);
  }

  // Helper function to cast the cursor pointer to the correct type.
  static auto GetCursor(sqlite3_vtab_cursor* cursor) {
    return static_cast<typename Impl::Cursor*>(cursor);
  }

  // Returns sqlite3_module object corresponding to the module. Used to pass
  // information about this module to SQLite.
  static constexpr sqlite3_module CreateModule() {
    sqlite3_module module{};
    module.iVersion = 4;
    module.xBestIndex = &Impl::BestIndex;
    module.xOpen = &Impl::Open;
    module.xClose = &Impl::Close;
    module.xFilter = &Impl::Filter;
    module.xNext = &Impl::Next;
    module.xEof = &Impl::Eof;
    module.xColumn = &Impl::Column;
    module.xRowid = &Impl::Rowid;
    if constexpr (Impl::kType == kCreateOnly) {
      module.xCreate = &Impl::Create;
      module.xDestroy = &Impl::Destroy;
      module.xConnect = &Impl::Connect;
      module.xDisconnect = &Impl::Disconnect;
    } else {
      module.xCreate = nullptr;
      module.xDestroy = [](sqlite3_vtab*) -> int {
        PERFETTO_FATAL("Should not be reachable");
      };
      module.xConnect = &Impl::Connect;
      module.xDisconnect = &Impl::Disconnect;
    }
    if constexpr (Impl::kSupportsWrites) {
      module.xUpdate = &Impl::Update;
    }
    if constexpr (Impl::kDoesOverloadFunctions) {
      module.xFindFunction = &Impl::FindFunction;
    }
    if constexpr (Impl::kDoesSupportTransactions) {
      module.xBegin = &Impl::Begin;
      module.xSync = &Impl::Sync;
      module.xCommit = &Impl::Commit;
      module.xRollback = &Impl::Rollback;
      module.xSavepoint = &Impl::Savepoint;
      module.xRelease = &Impl::Release;
      module.xRollbackTo = &Impl::RollbackTo;
    }
    return module;
  }
};

}  // namespace perfetto::trace_processor::sqlite

#endif  // SRC_TRACE_PROCESSOR_SQLITE_BINDINGS_SQLITE_MODULE_H_
