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 #include "sqlitesessions.h"
27 #include "sqlitereadstatement.h"
28 #include "sqlitesessionchangeset.h"
29 #include "sqlitetable.h"
30
31 #include <sqlite.h>
32
33 #include <memory>
34
35 namespace Sqlite {
36
37 namespace {
38
checkResultCode(int resultCode)39 void checkResultCode(int resultCode)
40 {
41 switch (resultCode) {
42 case SQLITE_NOMEM:
43 throw std::bad_alloc();
44 case SQLITE_SCHEMA:
45 throw CannotApplyChangeSet("Cannot apply change set!");
46 case SQLITE_MISUSE:
47 throw ChangeSetIsMisused("Change set is misused!");
48 }
49
50 if (resultCode != SQLITE_OK)
51 throw UnknowError("Unknow exception");
52 }
53
xConflict(void *,int conflict,sqlite3_changeset_iter *)54 int xConflict(void *, int conflict, sqlite3_changeset_iter *)
55 {
56 switch (conflict) {
57 case SQLITE_CHANGESET_DATA:
58 return SQLITE_CHANGESET_REPLACE;
59 case SQLITE_CHANGESET_NOTFOUND:
60 return SQLITE_CHANGESET_OMIT;
61 case SQLITE_CHANGESET_CONFLICT:
62 return SQLITE_CHANGESET_REPLACE;
63 case SQLITE_CHANGESET_CONSTRAINT:
64 return SQLITE_CHANGESET_OMIT;
65 case SQLITE_CHANGESET_FOREIGN_KEY:
66 return SQLITE_CHANGESET_OMIT;
67 }
68
69 return SQLITE_CHANGESET_ABORT;
70 }
71 } // namespace
72
attachTables(const Utils::SmallStringVector & tableNames)73 void Sessions::attachTables(const Utils::SmallStringVector &tableNames)
74 {
75 for (Utils::SmallStringView tableName : tableNames) {
76 int resultCode = sqlite3session_attach(session.get(), tableName.data());
77 checkResultCode(resultCode);
78 }
79 }
80
81 Sessions::~Sessions() = default;
82
setAttachedTables(Utils::SmallStringVector tables)83 void Sessions::setAttachedTables(Utils::SmallStringVector tables)
84 {
85 tableNames = std::move(tables);
86 }
87
create()88 void Sessions::create()
89 {
90 sqlite3_session *newSession = nullptr;
91 int resultCode = sqlite3session_create(database.backend().sqliteDatabaseHandle(),
92 databaseName.data(),
93 &newSession);
94 session.reset(newSession);
95
96 checkResultCode(resultCode);
97
98 attachTables(tableNames);
99 }
100
commit()101 void Sessions::commit()
102 {
103 if (session && !sqlite3session_isempty(session.get())) {
104 SessionChangeSet changeSet{*this};
105
106 insertSession.write(changeSet.asBlobView());
107 }
108
109 session.reset();
110 }
111
rollback()112 void Sessions::rollback()
113 {
114 session.reset();
115 }
116
createSessionTable(Database & database)117 void Internal::SessionsBase::createSessionTable(Database &database)
118 {
119 Sqlite::Table table;
120 table.setUseIfNotExists(true);
121 table.setName(sessionsTableName);
122 table.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{AutoIncrement::Yes}});
123 table.addColumn("changeset", Sqlite::ColumnType::Blob);
124
125 table.initialize(database);
126 }
127
revert()128 void Sessions::revert()
129 {
130 ReadStatement<1> selectChangeSets{Utils::PathString::join({"SELECT changeset FROM ",
131 sessionsTableName,
132 " ORDER BY id DESC"}),
133 database};
134
135 auto changeSets = selectChangeSets.values<SessionChangeSet>(1024);
136
137 for (auto &changeSet : changeSets) {
138 int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(),
139 changeSet.size(),
140 changeSet.data(),
141 nullptr,
142 xConflict,
143 nullptr,
144 nullptr,
145 nullptr,
146 SQLITE_CHANGESETAPPLY_INVERT
147 | SQLITE_CHANGESETAPPLY_NOSAVEPOINT);
148 checkResultCode(resultCode);
149 }
150 }
151
apply()152 void Sessions::apply()
153 {
154 ReadStatement<1> selectChangeSets{Utils::PathString::join({"SELECT changeset FROM ",
155 sessionsTableName,
156 " ORDER BY id"}),
157 database};
158
159 auto changeSets = selectChangeSets.values<SessionChangeSet>(1024);
160
161 for (auto &changeSet : changeSets) {
162 int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(),
163 changeSet.size(),
164 changeSet.data(),
165 nullptr,
166 xConflict,
167 nullptr,
168 nullptr,
169 nullptr,
170 SQLITE_CHANGESETAPPLY_NOSAVEPOINT);
171 checkResultCode(resultCode);
172 }
173 }
174
applyAndUpdateSessions()175 void Sessions::applyAndUpdateSessions()
176 {
177 create();
178 apply();
179 deleteAll();
180 commit();
181 }
182
deleteAll()183 void Sessions::deleteAll()
184 {
185 WriteStatement{Utils::SmallString::join({"DELETE FROM ", sessionsTableName}), database}.execute();
186 }
187
changeSets() const188 SessionChangeSets Sessions::changeSets() const
189 {
190 ReadStatement<1> selectChangeSets{Utils::PathString::join({"SELECT changeset FROM ",
191 sessionsTableName,
192 " ORDER BY id DESC"}),
193 database};
194
195 return selectChangeSets.values<SessionChangeSet>(1024);
196 }
197
198 } // namespace Sqlite
199