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