|
| 1 | +///////////////////////////////////////////////////////////////////////////// |
| 2 | +// // |
| 3 | +// NppSnippets - Code Snippets plugin for Notepad++ // |
| 4 | +// Copyright (C) 2010-2011 Frank Fesevur // |
| 5 | +// // |
| 6 | +// This program is free software; you can redistribute it and/or modify // |
| 7 | +// it under the terms of the GNU General Public License as published by // |
| 8 | +// the Free Software Foundation; either version 2 of the License, or // |
| 9 | +// (at your option) any later version. // |
| 10 | +// // |
| 11 | +// This program is distributed in the hope that it will be useful, // |
| 12 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of // |
| 13 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // |
| 14 | +// GNU General Public License for more details. // |
| 15 | +// // |
| 16 | +// You should have received a copy of the GNU General Public License // |
| 17 | +// along with this program; if not, write to the Free Software // |
| 18 | +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // |
| 19 | +// // |
| 20 | +///////////////////////////////////////////////////////////////////////////// |
| 21 | + |
| 22 | +#include <windows.h> |
| 23 | + |
| 24 | +#include "sqlite3.h" |
| 25 | + |
| 26 | +#include "NPP/PluginInterface.h" |
| 27 | +#include "NppSnippets.h" |
| 28 | +#include "Database.h" |
| 29 | +#include "WaitCursor.h" |
| 30 | + |
| 31 | +WCHAR g_dbFile[MAX_PATH]; |
| 32 | +sqlite3* g_db = NULL; |
| 33 | + |
| 34 | +///////////////////////////////////////////////////////////////////////////// |
| 35 | +// Map the column names to column numbers |
| 36 | + |
| 37 | +map<string,int> ResolveColumnNames(sqlite3_stmt* stmt) |
| 38 | +{ |
| 39 | + map<string,int> names; |
| 40 | + |
| 41 | + for (int i = 0; i < sqlite3_column_count(stmt); i++) |
| 42 | + names[sqlite3_column_name(stmt, i)] = i; |
| 43 | + |
| 44 | + return names; |
| 45 | +} |
| 46 | + |
| 47 | +///////////////////////////////////////////////////////////////////////////// |
| 48 | +// |
| 49 | + |
| 50 | +bool RunSQL(LPCSTR szSQL) |
| 51 | +{ |
| 52 | + if (sqlite3_exec(g_db, szSQL, NULL, NULL, NULL) != SQLITE_OK) |
| 53 | + { |
| 54 | + MsgBox(szSQL); |
| 55 | + MsgBox(sqlite3_errmsg(g_db)); |
| 56 | + return false; |
| 57 | + } |
| 58 | + return true; |
| 59 | +} |
| 60 | + |
| 61 | +///////////////////////////////////////////////////////////////////////////// |
| 62 | +// |
| 63 | + |
| 64 | +bool GetLongResult(LPCSTR szStmt, long& result) |
| 65 | +{ |
| 66 | + bool ret = false; |
| 67 | + |
| 68 | + sqlite3_stmt *stmt; |
| 69 | + sqlite3_prepare(g_db, szStmt, -1, &stmt, NULL); |
| 70 | + if (sqlite3_step(stmt) == SQLITE_ROW) |
| 71 | + { |
| 72 | + result = sqlite3_column_int(stmt, 0); |
| 73 | + ret = true; |
| 74 | + } |
| 75 | + |
| 76 | + sqlite3_finalize(stmt); |
| 77 | + return ret; |
| 78 | +} |
| 79 | + |
| 80 | +///////////////////////////////////////////////////////////////////////////// |
| 81 | +// |
| 82 | + |
| 83 | +bool BindText(sqlite3_stmt *stmt, const char* param, const WCHAR* var) |
| 84 | +{ |
| 85 | + int col = sqlite3_bind_parameter_index(stmt, param); |
| 86 | + |
| 87 | + int res = SQLITE_OK; |
| 88 | + if (var == NULL) |
| 89 | + { |
| 90 | + res = sqlite3_bind_null(stmt, col); |
| 91 | + } |
| 92 | + else if (wcslen(var) == 0) |
| 93 | + { |
| 94 | + res = sqlite3_bind_null(stmt, col); |
| 95 | + } |
| 96 | + else |
| 97 | + { |
| 98 | + res = sqlite3_bind_text16(stmt, col, var, -1, SQLITE_STATIC); |
| 99 | + } |
| 100 | + |
| 101 | + if (res != SQLITE_OK) |
| 102 | + { |
| 103 | + MsgBox(sqlite3_errmsg(g_db)); |
| 104 | + return false; |
| 105 | + } |
| 106 | + |
| 107 | + return true; |
| 108 | +} |
| 109 | + |
| 110 | +///////////////////////////////////////////////////////////////////////////// |
| 111 | +// |
| 112 | + |
| 113 | +bool BindInt(sqlite3_stmt *stmt, const char* param, const int i, bool null) |
| 114 | +{ |
| 115 | + int col = sqlite3_bind_parameter_index(stmt, param); |
| 116 | + |
| 117 | + int res = (null ? sqlite3_bind_null(stmt, col) : sqlite3_bind_int(stmt, col, i)); |
| 118 | + |
| 119 | + if (res != SQLITE_OK) |
| 120 | + { |
| 121 | + MsgBox(sqlite3_errmsg(g_db)); |
| 122 | + return false; |
| 123 | + } |
| 124 | + |
| 125 | + return true; |
| 126 | +} |
| 127 | + |
| 128 | +///////////////////////////////////////////////////////////////////////////// |
| 129 | +// |
| 130 | + |
| 131 | +bool CloseDB() |
| 132 | +{ |
| 133 | + bool ret = sqlite3_close(g_db) == SQLITE_OK; |
| 134 | + g_db = NULL; |
| 135 | + return ret; |
| 136 | +} |
| 137 | + |
| 138 | +///////////////////////////////////////////////////////////////////////////// |
| 139 | +// |
| 140 | + |
| 141 | +bool OpenDB() |
| 142 | +{ |
| 143 | + // Is the filename filled? |
| 144 | + if (wcslen(g_dbFile) == 0) |
| 145 | + return false; |
| 146 | + |
| 147 | + // Open the database |
| 148 | + int rc = sqlite3_open16(g_dbFile, &g_db); |
| 149 | + if (rc != SQLITE_OK) |
| 150 | + { |
| 151 | + MsgBox(sqlite3_errmsg(g_db)); |
| 152 | + sqlite3_close(g_db); |
| 153 | + return false; |
| 154 | + } |
| 155 | + |
| 156 | + // Make sure the database has the right version |
| 157 | + if (!CheckDBVersion()) |
| 158 | + { |
| 159 | + MsgBox(sqlite3_errmsg(g_db)); |
| 160 | + sqlite3_close(g_db); |
| 161 | + return false; |
| 162 | + } |
| 163 | + |
| 164 | + // Make use the foreign key constraints are turned on |
| 165 | + if (!RunSQL("PRAGMA foreign_keys = ON")) |
| 166 | + { |
| 167 | + MsgBox(sqlite3_errmsg(g_db)); |
| 168 | + sqlite3_close(g_db); |
| 169 | + return false; |
| 170 | + } |
| 171 | + |
| 172 | + return true; |
| 173 | +} |
| 174 | + |
| 175 | +///////////////////////////////////////////////////////////////////////////// |
| 176 | +// Compress the database |
| 177 | + |
| 178 | +bool VacuumDB() |
| 179 | +{ |
| 180 | + return RunSQL("VACUUM;"); |
| 181 | +} |
| 182 | + |
| 183 | +///////////////////////////////////////////////////////////////////////////// |
| 184 | +// Upgrade the database from schema version 1 to 2 |
| 185 | + |
| 186 | +static void UpgradeDatabase_1_2() |
| 187 | +{ |
| 188 | + // Create the LibraryLang table and fill it |
| 189 | + RunSQL("BEGIN TRANSACTION;"); |
| 190 | + RunSQL("CREATE TABLE LibraryLang(LibraryID INTEGER NOT NULL REFERENCES Library(LibraryID) ON DELETE CASCADE ON UPDATE CASCADE,Lang INTEGER NOT NULL,LastUsed BOOL NOT NULL DEFAULT 0,PRIMARY KEY (LibraryID, Lang));"); |
| 191 | + RunSQL("INSERT INTO LibraryLang SELECT LibraryID, Lang, LastUsed FROM Library;"); |
| 192 | + RunSQL("COMMIT TRANSACTION;"); |
| 193 | + |
| 194 | + // Delete the "Lang" and "LastUsed" column from the Library table |
| 195 | + RunSQL("BEGIN TRANSACTION;"); |
| 196 | + RunSQL("DROP INDEX LibLang;"); // Index not needed anymore |
| 197 | + RunSQL("CREATE TEMPORARY TABLE Library_backup(LibraryID INTEGER,Name TEXT,CreatedBy TEXT,Comments TEXT,SortBy INTEGER);"); |
| 198 | + RunSQL("INSERT INTO Library_backup SELECT LibraryID,Name,CreatedBy,Comments,SortBy FROM Library;"); |
| 199 | + RunSQL("DROP TABLE Library;"); |
| 200 | + RunSQL("CREATE TABLE Library (LibraryID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,Name TEXT NOT NULL,CreatedBy TEXT,Comments TEXT,SortBy INTEGER NOT NULL DEFAULT 0);"); |
| 201 | + RunSQL("INSERT INTO Library SELECT LibraryID,Name,CreatedBy,Comments,SortBy FROM Library_backup;"); |
| 202 | + RunSQL("DROP TABLE Library_backup;"); |
| 203 | + RunSQL("COMMIT TRANSACTION;"); |
| 204 | + |
| 205 | + // Throw away these old indices. New ones will be created later |
| 206 | + RunSQL("DROP INDEX SnipName;"); |
| 207 | + RunSQL("DROP INDEX SnipSort;"); |
| 208 | + |
| 209 | + // Add a primary key table to Snippets |
| 210 | + RunSQL("BEGIN TRANSACTION;"); |
| 211 | + RunSQL("CREATE TEMPORARY TABLE Snippets_backup(LibraryID INTEGER,Name TEXT,BeforeSelection TEXT,AfterSelection TEXT,ReplaceSelection BOOL,NewDocument BOOL,NewDocumentLang INTEGER,Sort INTEGER);"); |
| 212 | + RunSQL("INSERT INTO Snippets_backup SELECT LibraryID,Name,BeforeSelection,AfterSelection,ReplaceSelection,NewDocument,NewDocumentLang,Sort FROM Snippets;"); |
| 213 | + RunSQL("DROP TABLE Snippets;"); |
| 214 | + RunSQL("CREATE TABLE Snippets(SnippetID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,LibraryID INTEGER NOT NULL REFERENCES Library(LibraryID) ON DELETE CASCADE ON UPDATE CASCADE,Name TEXT NOT NULL,BeforeSelection TEXT NOT NULL,AfterSelection TEXT,ReplaceSelection BOOL NOT NULL DEFAULT 0,NewDocument BOOL NOT NULL DEFAULT 0,NewDocumentLang INTEGER,Sort INTEGER);"); |
| 215 | + RunSQL("INSERT INTO Snippets(LibraryID,Name,BeforeSelection,AfterSelection,ReplaceSelection,NewDocument,NewDocumentLang,Sort) SELECT LibraryID,Name,BeforeSelection,AfterSelection,ReplaceSelection,NewDocument,NewDocumentLang,Sort FROM Snippets_backup;"); |
| 216 | + RunSQL("DROP TABLE Snippets_backup;"); |
| 217 | + RunSQL("COMMIT TRANSACTION;"); |
| 218 | + |
| 219 | + // Create new indices |
| 220 | + RunSQL("CREATE INDEX SnipName ON Snippets(LibraryID, Name, Sort);"); |
| 221 | + RunSQL("CREATE INDEX SnipSort ON Snippets(LibraryID, Sort, Name);"); |
| 222 | + |
| 223 | + // We are at schema version 2 |
| 224 | + RunSQL("PRAGMA user_version = 2;"); |
| 225 | +} |
| 226 | + |
| 227 | +///////////////////////////////////////////////////////////////////////////// |
| 228 | +// Upgrade the database from schema version 2 to 3 |
| 229 | + |
| 230 | +static void UpgradeDatabase_2_3() |
| 231 | +{ |
| 232 | + // Add the new tabel to store which library is last used for with language |
| 233 | + RunSQL("BEGIN TRANSACTION;"); |
| 234 | + RunSQL("CREATE TABLE LangLastUsed(Lang INTEGER PRIMARY KEY NOT NULL,LibraryID INTEGER NOT NULL REFERENCES Library(LibraryID) ON DELETE CASCADE ON UPDATE CASCADE);"); |
| 235 | + RunSQL("INSERT INTO LangLastUsed SELECT Lang,LibraryID FROM LibraryLang WHERE LastUsed = 1;"); |
| 236 | + RunSQL("COMMIT TRANSACTION;"); |
| 237 | + |
| 238 | + // Delete the LastUsed column from the LibraryLang table |
| 239 | + RunSQL("BEGIN TRANSACTION;"); |
| 240 | + RunSQL("CREATE TEMPORARY TABLE LibraryLang_backup(LibraryID INTEGER,Lang INTEGER);"); |
| 241 | + RunSQL("INSERT INTO LibraryLang_backup SELECT LibraryID, Lang FROM LibraryLang;"); |
| 242 | + RunSQL("DROP TABLE LibraryLang;"); |
| 243 | + RunSQL("CREATE TABLE LibraryLang(LibraryID INTEGER NOT NULL REFERENCES Library(LibraryID) ON DELETE CASCADE ON UPDATE CASCADE,Lang INTEGER NOT NULL,PRIMARY KEY (LibraryID, Lang));"); |
| 244 | + RunSQL("INSERT INTO LibraryLang SELECT LibraryID, Lang FROM LibraryLang_backup;"); |
| 245 | + RunSQL("DROP TABLE LibraryLang_backup;"); |
| 246 | + RunSQL("COMMIT TRANSACTION;"); |
| 247 | + |
| 248 | + // We are at schema version 3 |
| 249 | + RunSQL("PRAGMA user_version = 3;"); |
| 250 | +} |
| 251 | + |
| 252 | +///////////////////////////////////////////////////////////////////////////// |
| 253 | +// |
| 254 | + |
| 255 | +bool CheckDBVersion() |
| 256 | +{ |
| 257 | + long schema_version = 0; |
| 258 | + if (!GetLongResult("PRAGMA user_version;", schema_version)) |
| 259 | + return false; |
| 260 | + |
| 261 | + WaitCursor wait(false); |
| 262 | + switch (schema_version) |
| 263 | + { |
| 264 | + case 0: |
| 265 | + // Database probably not found |
| 266 | + return false; |
| 267 | + |
| 268 | + case 1: |
| 269 | + wait.Show(); |
| 270 | + UpgradeDatabase_1_2(); |
| 271 | + // fall through |
| 272 | + |
| 273 | + case 2: |
| 274 | + wait.Show(); |
| 275 | + UpgradeDatabase_2_3(); |
| 276 | + VacuumDB(); |
| 277 | + // fall through |
| 278 | + |
| 279 | + case 3: |
| 280 | + // The right version! |
| 281 | + return true; |
| 282 | + } |
| 283 | + |
| 284 | + // Running with an newer database! |
| 285 | + return false; |
| 286 | +} |
0 commit comments