1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright notice,
11 // this list of conditions and the following disclaimer.
12 //
13 // * Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 //
17 // * Neither the name of the author nor the names of other contributors may
18 // be used to endorse or promote products derived from this software
19 // without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34
35 #include "pch.hpp"
36
37 #include <musikcore/db/Connection.h>
38 #include <musikcore/db/SqliteExtensions.h>
39
40 #pragma warning(push, 0)
41 #include <sqlite/sqlite3.h>
42 #pragma warning(pop)
43
44 static std::mutex globalMutex;
45
46 using namespace musik::core::db;
47
Connection()48 Connection::Connection() noexcept
49 : connection(nullptr)
50 , transactionCounter(0) {
51 this->UpdateReferenceCount(true);
52 }
53
~Connection()54 Connection::~Connection() {
55 this->Close();
56 this->UpdateReferenceCount(false);
57 }
58
Open(const std::string & database,unsigned int options,unsigned int cache)59 int Connection::Open(const std::string &database, unsigned int options, unsigned int cache) {
60 int error;
61
62 #ifdef WIN32
63 std::wstring wdatabase = u8to16(database);
64 error = sqlite3_open16(wdatabase.c_str(), &this->connection);
65 #else
66 error = sqlite3_open(database.c_str(), &this->connection);
67 #endif
68
69 if (error == SQLITE_OK) {
70 this->Initialize(cache);
71 }
72
73 return error;
74 }
75
Close()76 int Connection::Close() noexcept {
77 if (sqlite3_close(this->connection) == SQLITE_OK) {
78 this->connection = 0;
79 return Okay;
80 }
81
82 return Error;
83 }
84
Execute(const char * sql)85 int Connection::Execute(const char* sql) {
86 sqlite3_stmt *stmt = nullptr;
87
88 /* prepare seems to give errors when interrupted */
89 {
90 std::unique_lock<std::mutex> lock(this->mutex);
91
92 if (sqlite3_prepare_v2(this->connection, sql, -1, &stmt, nullptr) != SQLITE_OK) {
93 sqlite3_finalize(stmt);
94 return Error;
95 }
96 }
97
98 const int error = this->StepStatement(stmt);
99 if (error != SQLITE_OK && error != SQLITE_DONE) {
100 sqlite3_finalize(stmt);
101 return Error;
102 }
103
104 sqlite3_reset(stmt);
105 sqlite3_finalize(stmt);
106
107 return Okay;
108 }
109
Checkpoint()110 void Connection::Checkpoint() noexcept {
111 sqlite3_wal_checkpoint(this->connection, nullptr);
112 }
113
LastInsertedId()114 int64_t Connection::LastInsertedId() noexcept {
115 return sqlite3_last_insert_rowid(this->connection);
116 }
117
LastModifiedRowCount()118 int Connection::LastModifiedRowCount() noexcept {
119 return narrow_cast<int>(sqlite3_changes(this->connection));
120 }
121
Initialize(unsigned int cache)122 void Connection::Initialize(unsigned int cache) {
123 SqliteExtensions::Register(this->connection);
124
125 sqlite3_enable_shared_cache(1);
126 sqlite3_busy_timeout(this->connection, 10000);
127
128 sqlite3_exec(this->connection, "PRAGMA optimize", nullptr, nullptr, nullptr); // Optimize the database when applicable
129 sqlite3_exec(this->connection, "PRAGMA synchronous=NORMAL", nullptr, nullptr, nullptr); // NORMAL useful for auto-checkpointing with WAL
130 sqlite3_exec(this->connection, "PRAGMA page_size=4096", nullptr, nullptr, nullptr); // According to windows standard page size
131 sqlite3_exec(this->connection, "PRAGMA auto_vacuum=0", nullptr, nullptr, nullptr); // No autovaccum.
132 sqlite3_exec(this->connection, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr); // Allow reading while writing (write-ahead-logging)
133
134 if (cache != 0) {
135 // Divide by 4 to since the page_size is 4096
136 // Total cache is the same as page_size*cache_size
137 cache = cache / 4;
138 std::string cacheSize("PRAGMA cache_size=" + std::to_string(cache));
139 sqlite3_exec(this->connection,cacheSize.c_str(), nullptr, nullptr, nullptr); // size * 1.5kb = 6Mb cache
140 }
141
142 //sqlite3_exec(this->connection, "PRAGMA case_sensitive_like=0", nullptr, nullptr, nullptr); // More speed if case insensitive
143 sqlite3_exec(this->connection, "PRAGMA count_changes=0", nullptr, nullptr, nullptr); // If set it counts changes on SQL UPDATE. More speed when not.
144 sqlite3_exec(this->connection, "PRAGMA legacy_file_format=OFF", nullptr, nullptr, nullptr); // No reason to be backwards compatible :)
145 sqlite3_exec(this->connection, "PRAGMA temp_store=MEMORY", nullptr, nullptr, nullptr); // MEMORY, not file. More speed.
146 }
147
Interrupt()148 void Connection::Interrupt() {
149 std::unique_lock<std::mutex> lock(this->mutex);
150 sqlite3_interrupt(this->connection);
151 }
152
UpdateReferenceCount(bool init)153 void Connection::UpdateReferenceCount(bool init) {
154 std::unique_lock<std::mutex> lock(this->mutex);
155
156 static int count = 0;
157
158 if (init) {
159 if (count == 0) {
160 sqlite3_initialize();
161 }
162
163 ++count;
164 }
165 else {
166 --count;
167 if (count <= 0) {
168 sqlite3_shutdown();
169 count = 0;
170 }
171 }
172 }
173
StepStatement(sqlite3_stmt * stmt)174 int Connection::StepStatement(sqlite3_stmt *stmt) noexcept {
175 return sqlite3_step(stmt);
176 }
177