1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #pragma once
27 
28 #include "createtablesqlstatementbuilder.h"
29 #include "sqliteglobal.h"
30 #include "sqlitecolumn.h"
31 #include "sqliteindex.h"
32 #include "sqliteexception.h"
33 
34 namespace Sqlite {
35 
36 class Database;
37 
38 class Table
39 {
40 public:
41     Table(std::size_t reserve = 10)
42     {
43         m_sqliteColumns.reserve(reserve);
44         m_sqliteIndices.reserve(reserve);
45     }
46 
setName(Utils::SmallStringView name)47     void setName(Utils::SmallStringView name) { m_tableName = name; }
48 
name()49     Utils::SmallStringView name() const
50     {
51         return m_tableName;
52     }
53 
setUseWithoutRowId(bool useWithoutWorId)54     void setUseWithoutRowId(bool useWithoutWorId)
55     {
56         m_withoutRowId = useWithoutWorId;
57     }
58 
useWithoutRowId()59     bool useWithoutRowId() const
60     {
61         return m_withoutRowId;
62     }
63 
setUseIfNotExists(bool useIfNotExists)64     void setUseIfNotExists(bool useIfNotExists)
65     {
66         m_useIfNotExists = useIfNotExists;
67     }
68 
setUseTemporaryTable(bool useTemporaryTable)69     void setUseTemporaryTable(bool useTemporaryTable)
70     {
71         m_useTemporaryTable = useTemporaryTable;
72     }
73 
74     Column &addColumn(Utils::SmallStringView name,
75                       ColumnType type = ColumnType::None,
76                       Constraints &&constraints = {})
77     {
78         m_sqliteColumns.emplace_back(m_tableName, name, type, std::move(constraints));
79 
80         return m_sqliteColumns.back();
81     }
82 
83     Column &addForeignKeyColumn(Utils::SmallStringView name,
84                                 const Table &referencedTable,
85                                 ForeignKeyAction foreignKeyupdateAction = {},
86                                 ForeignKeyAction foreignKeyDeleteAction = {},
87                                 Enforment foreignKeyEnforcement = {},
88                                 Constraints &&constraints = {},
89                                 ColumnType type = ColumnType::Integer)
90     {
91         constraints.emplace_back(ForeignKey{referencedTable.name(),
92                                             "",
93                                             foreignKeyupdateAction,
94                                             foreignKeyDeleteAction,
95                                             foreignKeyEnforcement});
96 
97         m_sqliteColumns.emplace_back(m_tableName, name, type, std::move(constraints));
98 
99         return m_sqliteColumns.back();
100     }
101 
102     Column &addForeignKeyColumn(Utils::SmallStringView name,
103                                 const Column &referencedColumn,
104                                 ForeignKeyAction foreignKeyupdateAction = {},
105                                 ForeignKeyAction foreignKeyDeleteAction = {},
106                                 Enforment foreignKeyEnforcement = {},
107                                 Constraints &&constraints = {})
108     {
109         if (!constainsUniqueIndex(referencedColumn.constraints))
110             throw ForeignKeyColumnIsNotUnique("Foreign column key must be unique!");
111 
112         constraints.emplace_back(ForeignKey{referencedColumn.tableName,
113                                             referencedColumn.name,
114                                             foreignKeyupdateAction,
115                                             foreignKeyDeleteAction,
116                                             foreignKeyEnforcement});
117 
118         m_sqliteColumns.emplace_back(m_tableName,
119                                      name,
120                                      referencedColumn.type,
121                                      std::move(constraints));
122 
123         return m_sqliteColumns.back();
124     }
125 
addPrimaryKeyContraint(const SqliteColumnConstReferences & columns)126     void addPrimaryKeyContraint(const SqliteColumnConstReferences &columns)
127     {
128         Utils::SmallStringVector columnNames;
129         columnNames.reserve(columns.size());
130 
131         for (const auto &column : columns)
132             columnNames.emplace_back(column.get().name);
133 
134         m_tableConstraints.emplace_back(TablePrimaryKey{std::move(columnNames)});
135     }
136 
137     Index &addIndex(const SqliteColumnConstReferences &columns, Utils::SmallStringView condition = {})
138     {
139         return m_sqliteIndices.emplace_back(m_tableName,
140                                             sqliteColumnNames(columns),
141                                             IndexType::Normal,
142                                             condition);
143     }
144 
145     Index &addUniqueIndex(const SqliteColumnConstReferences &columns,
146                           Utils::SmallStringView condition = {})
147     {
148         return m_sqliteIndices.emplace_back(m_tableName,
149                                             sqliteColumnNames(columns),
150                                             IndexType::Unique,
151                                             condition);
152     }
153 
columns()154     const SqliteColumns &columns() const
155     {
156         return m_sqliteColumns;
157     }
158 
isReady()159     bool isReady() const
160     {
161         return m_isReady;
162     }
163 
164     template <typename Database>
initialize(Database & database)165     void initialize(Database &database)
166     {
167         CreateTableSqlStatementBuilder builder;
168 
169         builder.setTableName(m_tableName.clone());
170         builder.setUseWithoutRowId(m_withoutRowId);
171         builder.setUseIfNotExists(m_useIfNotExists);
172         builder.setUseTemporaryTable(m_useTemporaryTable);
173         builder.setColumns(m_sqliteColumns);
174         builder.setConstraints(m_tableConstraints);
175 
176         database.execute(builder.sqlStatement());
177 
178         initializeIndices(database);
179 
180         m_isReady = true;
181     }
182     template <typename Database>
initializeIndices(Database & database)183     void initializeIndices(Database &database)
184     {
185         for (const Index &index : m_sqliteIndices)
186             database.execute(index.sqlStatement());
187     }
188 
189     friend bool operator==(const Table &first, const Table &second)
190     {
191         return first.m_tableName == second.m_tableName
192             && first.m_withoutRowId == second.m_withoutRowId
193             && first.m_useIfNotExists == second.m_useIfNotExists
194             && first.m_isReady == second.m_isReady
195             && first.m_sqliteColumns == second.m_sqliteColumns;
196     }
197 
constainsUniqueIndex(const Constraints & constraints)198     static bool constainsUniqueIndex(const Constraints &constraints)
199     {
200         return std::find_if(constraints.begin(),
201                             constraints.end(),
202                             [](const Constraint &constraint) {
203                                 return Utils::holds_alternative<Unique>(constraint)
204                                        || Utils::holds_alternative<PrimaryKey>(constraint);
205                             })
206                != constraints.end();
207     }
208 
209 private:
sqliteColumnNames(const SqliteColumnConstReferences & columns)210     Utils::SmallStringVector sqliteColumnNames(const SqliteColumnConstReferences &columns)
211     {
212         Utils::SmallStringVector columnNames;
213 
214         for (const Column &column : columns)
215             columnNames.push_back(column.name);
216 
217         return columnNames;
218     }
219 
220 private:
221     Utils::SmallString m_tableName;
222     SqliteColumns m_sqliteColumns;
223     SqliteIndices m_sqliteIndices;
224     TableConstraints m_tableConstraints;
225     bool m_withoutRowId = false;
226     bool m_useIfNotExists = false;
227     bool m_useTemporaryTable = false;
228     bool m_isReady = false;
229 };
230 
231 } // namespace Sqlite
232