1 /** 2 Copyright: Copyright (c) 2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.world.db.sqliteworlddb; 7 8 public import sqlite.d2sqlite3; 9 10 import std.array : uninitializedArray; 11 import std.conv; 12 import std.stdio; 13 import voxelman.world.storage.coordinates : ChunkWorldPos; 14 import voxelman.utils.textformatter; 15 16 alias StatementHandle = size_t; 17 enum USE_WAL = false; 18 19 struct SqliteWorldDb 20 { 21 private Database db; 22 23 Statement perWorldInsertStmt; 24 Statement perWorldSelectStmt; 25 Statement perWorldDeleteStmt; 26 Statement* statementToReset; 27 28 //Statement perDimentionInsertStmt; 29 //Statement perDimentionSelectStmt; 30 //Statement perDimentionDeleteStmt; 31 32 Statement perChunkInsertStmt; 33 Statement perChunkSelectStmt; 34 Statement perChunkDeleteStmt; 35 36 private Statement[] statements; 37 38 //----------------------------------------------- 39 void open(string filename) 40 { 41 db = Database(filename); 42 43 static if (USE_WAL) { 44 db.execute("PRAGMA synchronous = normal"); 45 db.execute("PRAGMA journal_mode = wal"); 46 } else { 47 db.execute("PRAGMA synchronous = off"); 48 db.execute("PRAGMA journal_mode = memory"); 49 } 50 db.execute("PRAGMA count_changes = off"); 51 52 db.execute("PRAGMA temp_store = memory"); 53 db.execute(`PRAGMA page_size = "4096"; VACUUM`); 54 55 db.execute(perWorldTableCreate); 56 //db.execute(perDimentionTableCreate); 57 db.execute(perChunkTableCreate); 58 59 perWorldInsertStmt = db.prepare(perWorldTableInsert); 60 perWorldSelectStmt = db.prepare(perWorldTableSelect); 61 perWorldDeleteStmt = db.prepare(perWorldTableDelete); 62 63 //perDimentionInsertStmt = db.prepare(perDimentionTableInsert); 64 //perDimentionSelectStmt = db.prepare(perDimentionTableSelect); 65 //perDimentionDeleteStmt = db.prepare(perDimentionTableDelete); 66 67 perChunkInsertStmt = db.prepare(perChunkTableInsert); 68 perChunkSelectStmt = db.prepare(perChunkTableSelect); 69 perChunkDeleteStmt = db.prepare(perChunkTableDelete); 70 } 71 72 void close() 73 { 74 if (statementToReset) statementToReset.reset(); 75 destroy(perWorldInsertStmt); 76 destroy(perWorldSelectStmt); 77 destroy(perWorldDeleteStmt); 78 //destroy(perDimentionInsertStmt); 79 //destroy(perDimentionSelectStmt); 80 //destroy(perDimentionDeleteStmt); 81 destroy(perChunkInsertStmt); 82 destroy(perChunkSelectStmt); 83 destroy(perChunkDeleteStmt); 84 foreach(ref s; statements) 85 destroy(s); 86 destroy(statements); 87 //db.close(); 88 } 89 90 //----------------------------------------------- 91 // key should contain only alphanum chars and . 92 void savePerWorldData(string key, ubyte[] data) 93 { 94 perWorldInsertStmt.inject(key, data); 95 } 96 97 // Reset statement after returned data is no longer needed 98 ubyte[] loadPerWorldData(string key) 99 { 100 if (statementToReset) statementToReset.reset(); 101 statementToReset = &perWorldSelectStmt; 102 perWorldSelectStmt.bindAll(key); 103 auto result = perWorldSelectStmt.execute(); 104 if (result.empty) return null; 105 return result.front.peekNoDup!(ubyte[])(0); 106 } 107 void removePerWorldData(string key) 108 { 109 perWorldDeleteStmt.inject(key); 110 } 111 112 //void savePerDimentionData(string key, int dim, ubyte[] data) 113 114 //ubyte[] loadPerDimentionData(string key, int dim) 115 import voxelman.core.config; 116 void savePerChunkData(ulong cwp, ubyte[] data) 117 { 118 perChunkInsertStmt.inject(cast(long)cwp, data); 119 } 120 121 // Reset statement after returned data is no longer needed 122 ubyte[] loadPerChunkData(ulong cwp) 123 { 124 if (statementToReset) statementToReset.reset(); 125 statementToReset = &perChunkSelectStmt; 126 perChunkSelectStmt.bindAll(cast(long)cwp); 127 auto result = perChunkSelectStmt.execute(); 128 if (result.empty) return null; 129 return result.front.peekNoDup!(ubyte[])(0); 130 } 131 132 //----------------------------------------------- 133 void beginTxn() { 134 } 135 void abortTxn() { 136 } 137 void commitTxn() { 138 } 139 void execute(string sql) 140 { 141 db.execute(sql); 142 } 143 144 StatementHandle prepareStmt(string sql) 145 { 146 statements ~= db.prepare(sql); 147 return statements.length - 1; 148 } 149 150 ref Statement stmt(StatementHandle stmtHandle) 151 { 152 return statements[stmtHandle]; 153 } 154 } 155 156 enum bool withoutRowid = true; 157 enum string withoutRowidStr = withoutRowid ? ` without rowid;` : ``; 158 159 immutable perWorldTableCreate = ` 160 create table if not exists per_world_data ( 161 id text primary key, 162 data blob not null 163 )` ~ withoutRowidStr; 164 165 immutable perWorldTableInsert = `insert or replace into per_world_data values (:id, :value)`; 166 immutable perWorldTableSelect = `select data from per_world_data where id = :id`; 167 immutable perWorldTableDelete = `delete from per_world_data where id = :id`; 168 169 immutable perDimentionTableCreate = ` 170 create table if not exists per_dimention_data( 171 id text, 172 dimention integer, 173 data blob not null, 174 primary key (id, dimention) 175 )` ~ withoutRowidStr; 176 177 immutable perDimentionTableInsert = 178 `insert or replace into per_dimention_data values (:dim, :id, :value)`; 179 immutable perDimentionTableSelect = ` 180 select data from per_dimention_data where dimention = :dim and id = :id`; 181 immutable perDimentionTableDelete = ` 182 delete from per_dimention_data where dimention = :dim and id = :id`; 183 184 immutable perChunkTableCreate = ` 185 create table if not exists per_chunk_data( 186 id integer primary key, 187 data blob not null )`; 188 189 immutable perChunkTableInsert = `insert or replace into per_chunk_data values (:id, :value)`; 190 immutable perChunkTableSelect = `select data from per_chunk_data where id = :id`; 191 immutable perChunkTableDelete = `delete from per_chunk_data where id = :id`;