1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 10 This file contains a sqlite3 wrapper that is responsible for providing *nice*, easy to use functions for accessing the data in the database. 11 12 This module may have dependencies on many internal mutation modules. 13 */ 14 module dextool.plugin.mutate.backend.database; 15 16 import core.time : Duration, dur; 17 import logger = std.experimental.logger; 18 import std.format : format; 19 20 import dextool.type : AbsolutePath, Path; 21 import dextool.plugin.mutate.backend.type; 22 23 import dextool.plugin.mutate.backend.database.schema; 24 public import dextool.plugin.mutate.backend.database.type; 25 public import dextool.plugin.mutate.backend.database.standalone : spinSqlQuery; 26 27 /** Wrapper for a sqlite3 database that provide a uniform, easy-to-use 28 * interface for the mutation testing plugin. 29 */ 30 struct Database { 31 import std.conv : to; 32 import std.exception : collectException; 33 import std.typecons : Nullable; 34 import dextool.plugin.mutate.backend.type : MutationPoint, Mutation, Checksum; 35 import dextool.plugin.mutate.type : MutationOrder; 36 import dextool.plugin.mutate.backend.database.standalone : SDatabase = Database; 37 38 SDatabase db; 39 alias db this; 40 41 private MutationOrder mut_order; 42 43 static auto make(AbsolutePath db, MutationOrder mut_order) @safe { 44 return Database(SDatabase.make(db), mut_order); 45 } 46 47 // Not movable. The database should only be passed around as a reference, 48 // if at all. 49 @disable this(this); 50 51 Nullable!Checksum getFileChecksum(const Path p) @trusted { 52 import dextool.plugin.mutate.backend.utility : checksum; 53 54 auto stmt = db.prepare("SELECT checksum0,checksum1 FROM files WHERE path=:path"); 55 stmt.bind(":path", cast(string) p); 56 auto res = stmt.execute; 57 58 typeof(return) rval; 59 if (!res.empty) { 60 rval = checksum(res.front.peek!long(0), res.front.peek!long(1)); 61 } 62 63 return rval; 64 } 65 66 /** Get the next mutation point + 1 mutant for it that has status unknown. 67 * 68 * TODO to run many instances in parallel the mutation should be locked. 69 * 70 * The chosen point is randomised. 71 * 72 * Params: 73 * kind = kind of mutation to retrieve. 74 */ 75 NextMutationEntry nextMutation(const(Mutation.Kind)[] kinds) @trusted { 76 import std.algorithm : map; 77 import std.exception : collectException; 78 import dextool.plugin.mutate.backend.type; 79 import dextool.type : FileName; 80 81 typeof(return) rval; 82 83 auto order = mut_order == MutationOrder.random ? "ORDER BY RANDOM()" : ""; 84 85 immutable sql = format("SELECT 86 t0.id, 87 t0.kind, 88 t3.time, 89 t1.offset_begin, 90 t1.offset_end, 91 t1.line, 92 t1.column, 93 t2.path, 94 t2.lang 95 FROM %s t0,%s t1,%s t2,%s t3 96 WHERE 97 t0.st_id = t3.id AND 98 t3.status == 0 AND 99 t0.mp_id == t1.id AND 100 t1.file_id == t2.id AND 101 t0.kind IN (%(%s,%)) %s LIMIT 1", mutationTable, mutationPointTable, 102 filesTable, mutationStatusTable, kinds.map!(a => cast(int) a), order); 103 auto stmt = db.prepare(sql); 104 auto res = stmt.execute; 105 if (res.empty) { 106 rval.st = NextMutationEntry.Status.done; 107 return rval; 108 } 109 110 auto v = res.front; 111 112 auto mp = MutationPoint(Offset(v.peek!uint(3), v.peek!uint(4))); 113 mp.mutations = [Mutation(v.peek!long(1).to!(Mutation.Kind))]; 114 auto pkey = MutationId(v.peek!long(0)); 115 auto file = Path(FileName(v.peek!string(7))); 116 auto sloc = SourceLoc(v.peek!uint(5), v.peek!uint(6)); 117 auto lang = v.peek!long(8).to!Language; 118 119 rval.entry = MutationEntry(pkey, file, sloc, mp, v.peek!long(2).dur!"msecs", lang); 120 121 return rval; 122 } 123 124 void iterateMutants(const Mutation.Kind[] kinds, void delegate(const ref IterateMutantRow) dg) @trusted { 125 import std.algorithm : map; 126 import dextool.plugin.mutate.backend.utility : checksum; 127 128 immutable all_mutants = format("SELECT 129 t0.id, 130 t3.status, 131 t0.kind, 132 t3.time, 133 t1.offset_begin, 134 t1.offset_end, 135 t1.line, 136 t1.column, 137 t1.line_end, 138 t1.column_end, 139 t2.path, 140 t2.checksum0, 141 t2.checksum1, 142 t2.lang, 143 t4.nomut 144 FROM %s t0,%s t1,%s t2, %s t3, %s t4 145 WHERE 146 t0.kind IN (%(%s,%)) AND 147 t0.st_id = t3.id AND 148 t0.mp_id = t1.id AND 149 t1.file_id = t2.id AND 150 t0.id = t4.mut_id 151 ORDER BY t3.status", mutationTable, mutationPointTable, 152 filesTable, mutationStatusTable, srcMetadataTable, kinds.map!(a => cast(int) a)); 153 154 try { 155 auto res = db.prepare(all_mutants).execute; 156 foreach (ref r; res) { 157 IterateMutantRow d; 158 d.id = MutationId(r.peek!long(0)); 159 d.mutation = Mutation(r.peek!int(2).to!(Mutation.Kind), 160 r.peek!int(1).to!(Mutation.Status)); 161 auto offset = Offset(r.peek!uint(4), r.peek!uint(5)); 162 d.mutationPoint = MutationPoint(offset, null); 163 d.file = r.peek!string(10); 164 d.fileChecksum = checksum(r.peek!long(11), r.peek!long(12)); 165 d.sloc = SourceLoc(r.peek!uint(6), r.peek!uint(7)); 166 d.slocEnd = SourceLoc(r.peek!uint(8), r.peek!uint(9)); 167 d.lang = r.peek!long(13).to!Language; 168 169 d.testCases = db.getTestCases(d.id); 170 171 if (r.peek!long(14) != 0) 172 d.attrs = MutantMetaData(d.id, MutantAttr(NoMut.init)); 173 dg(d); 174 } 175 } catch (Exception e) { 176 logger.error(e.msg).collectException; 177 } 178 } 179 180 FileRow[] getDetailedFiles() @trusted { 181 import std.array : appender; 182 import dextool.plugin.mutate.backend.utility : checksum; 183 184 enum files_q = format("SELECT t0.path, t0.checksum0, t0.checksum1, t0.lang, t0.id FROM %s t0", 185 filesTable); 186 auto app = appender!(FileRow[])(); 187 foreach (ref r; db.prepare(files_q).execute) { 188 auto fr = FileRow(r.peek!string(0).Path, checksum(r.peek!long(1), 189 r.peek!long(2)), r.peek!Language(3), r.peek!long(4).FileId); 190 app.put(fr); 191 } 192 193 return app.data; 194 } 195 196 /** Iterate over the mutants in a specific file. 197 * 198 * Mutants are guaranteed to be ordered by their starting offset in the 199 * file. 200 * 201 * Params: 202 * kinds = the type of mutation operators to have in the report 203 * file = the file to retrieve mutants from 204 * dg = callback for reach row 205 */ 206 void iterateFileMutants(const Mutation.Kind[] kinds, Path file, 207 void delegate(ref const FileMutantRow) dg) @trusted { 208 import std.algorithm : map; 209 210 immutable all_fmut = format("SELECT 211 t0.id, 212 t0.kind, 213 t3.status, 214 t1.offset_begin, 215 t1.offset_end, 216 t1.line, 217 t1.column, 218 t1.line_end, 219 t1.column_end, 220 t2.lang 221 FROM %s t0, %s t1, %s t2, %s t3 222 WHERE 223 t0.kind IN (%(%s,%)) AND 224 t0.st_id = t3.id AND 225 t0.mp_id = t1.id AND 226 t1.file_id = t2.id AND 227 t2.path = :path 228 ORDER BY t1.offset_begin 229 ", mutationTable, mutationPointTable, 230 filesTable, mutationStatusTable, kinds.map!(a => cast(int) a)); 231 232 auto stmt = db.prepare(all_fmut); 233 stmt.bind(":path", cast(string) file); 234 foreach (ref r; stmt.execute) { 235 FileMutantRow fr; 236 fr.id = MutationId(r.peek!long(0)); 237 fr.mutation = Mutation(r.peek!int(1).to!(Mutation.Kind), 238 r.peek!int(2).to!(Mutation.Status)); 239 auto offset = Offset(r.peek!uint(3), r.peek!uint(4)); 240 fr.mutationPoint = MutationPoint(offset, null); 241 fr.sloc = SourceLoc(r.peek!uint(5), r.peek!uint(6)); 242 fr.slocEnd = SourceLoc(r.peek!uint(7), r.peek!uint(8)); 243 fr.lang = r.peek!int(9).to!Language; 244 245 dg(fr); 246 } 247 } 248 } 249 250 struct IterateMutantRow { 251 MutationId id; 252 Mutation mutation; 253 MutationPoint mutationPoint; 254 Path file; 255 Checksum fileChecksum; 256 SourceLoc sloc; 257 SourceLoc slocEnd; 258 TestCase[] testCases; 259 Language lang; 260 MutantMetaData attrs; 261 } 262 263 struct FileRow { 264 Path file; 265 Checksum fileChecksum; 266 Language lang; 267 FileId id; 268 } 269 270 struct FileMutantRow { 271 MutationId id; 272 Mutation mutation; 273 MutationPoint mutationPoint; 274 SourceLoc sloc; 275 SourceLoc slocEnd; 276 Language lang; 277 }