// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "createtablesqlstatementbuilder.h" #include "sqliteglobal.h" #include "sqlitecolumn.h" #include "sqliteindex.h" #include "sqliteexception.h" namespace Sqlite { class Database; template class BasicTable { public: using Column = ::Sqlite::BasicColumn; using ColumnConstReferences = ::Sqlite::BasicColumnConstReferences; using Columns = ::Sqlite::StableReferenceBasicColumns; using Indices = StableReferenceSqliteIndices; BasicTable([[maybe_unused]] std::size_t reserve = 10) {} void setName(Utils::SmallStringView name) { m_tableName = name; } Utils::SmallStringView name() const { return m_tableName; } void setUseWithoutRowId(bool useWithoutWorId) { m_withoutRowId = useWithoutWorId; } bool useWithoutRowId() const { return m_withoutRowId; } void setUseIfNotExists(bool useIfNotExists) { m_useIfNotExists = useIfNotExists; } void setUseTemporaryTable(bool useTemporaryTable) { m_useTemporaryTable = useTemporaryTable; } Column &addColumn(Utils::SmallStringView name, ColumnType type = {}, Constraints &&constraints = {}) { m_sqliteColumns.emplace_back(m_tableName, name, type, std::move(constraints)); return m_sqliteColumns.back(); } Column &addForeignKeyColumn(Utils::SmallStringView name, const BasicTable &referencedTable, ForeignKeyAction foreignKeyupdateAction = {}, ForeignKeyAction foreignKeyDeleteAction = {}, Enforment foreignKeyEnforcement = {}, Constraints &&constraints = {}, ColumnType type = ColumnType::Integer) { constraints.emplace_back(ForeignKey{referencedTable.name(), "", foreignKeyupdateAction, foreignKeyDeleteAction, foreignKeyEnforcement}); m_sqliteColumns.emplace_back(m_tableName, name, type, std::move(constraints)); return m_sqliteColumns.back(); } Column &addForeignKeyColumn(Utils::SmallStringView name, const BasicColumn &referencedColumn, ForeignKeyAction foreignKeyupdateAction = {}, ForeignKeyAction foreignKeyDeleteAction = {}, Enforment foreignKeyEnforcement = {}, Constraints &&constraints = {}) { if (!constainsUniqueIndex(referencedColumn.constraints)) throw ForeignKeyColumnIsNotUnique(); constraints.emplace_back(ForeignKey{referencedColumn.tableName, referencedColumn.name, foreignKeyupdateAction, foreignKeyDeleteAction, foreignKeyEnforcement}); m_sqliteColumns.emplace_back(m_tableName, name, referencedColumn.type, std::move(constraints)); return m_sqliteColumns.back(); } void addPrimaryKeyContraint(const BasicColumnConstReferences &columns) { Utils::SmallStringVector columnNames; for (const auto &column : columns) columnNames.emplace_back(column.name); m_tableConstraints.emplace_back(TablePrimaryKey{std::move(columnNames)}); } Index &addIndex(const BasicColumnConstReferences &columns, Utils::SmallStringView condition = {}) { return m_sqliteIndices.emplace_back(m_tableName, sqliteColumnNames(columns), IndexType::Normal, condition); } Index &addUniqueIndex(const BasicColumnConstReferences &columns, Utils::SmallStringView condition = {}) { return m_sqliteIndices.emplace_back(m_tableName, sqliteColumnNames(columns), IndexType::Unique, condition); } const Columns &columns() const { return m_sqliteColumns; } bool isReady() const { return m_isReady; } template void initialize(Database &database, const source_location &sourceLocation = source_location::current()) { CreateTableSqlStatementBuilder builder; builder.setTableName(m_tableName); builder.setUseWithoutRowId(m_withoutRowId); builder.setUseIfNotExists(m_useIfNotExists); builder.setUseTemporaryTable(m_useTemporaryTable); builder.setColumns(m_sqliteColumns); builder.setConstraints(m_tableConstraints); database.execute(builder.sqlStatement(), sourceLocation); initializeIndices(database, sourceLocation); m_isReady = true; } template void initializeIndices(Database &database, const source_location &sourceLocation) { for (const Index &index : m_sqliteIndices) database.execute(index.sqlStatement(), sourceLocation); } friend bool operator==(const BasicTable &first, const BasicTable &second) { return first.m_tableName == second.m_tableName && first.m_withoutRowId == second.m_withoutRowId && first.m_useIfNotExists == second.m_useIfNotExists && first.m_isReady == second.m_isReady && first.m_sqliteColumns == second.m_sqliteColumns; } static bool constainsUniqueIndex(const Constraints &constraints) { return std::find_if(constraints.begin(), constraints.end(), [](const Constraint &constraint) { return std::holds_alternative(constraint) || std::holds_alternative(constraint); }) != constraints.end(); } private: Utils::SmallStringVector sqliteColumnNames(const ColumnConstReferences &columns) { Utils::SmallStringVector columnNames; for (const Column &column : columns) columnNames.push_back(column.name); return columnNames; } private: Utils::SmallString m_tableName; Columns m_sqliteColumns; Indices m_sqliteIndices; TableConstraints m_tableConstraints; bool m_withoutRowId = false; bool m_useIfNotExists = false; bool m_useTemporaryTable = false; bool m_isReady = false; }; using Table = BasicTable; using StrictTable = BasicTable; } // namespace Sqlite