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.algorithm : map; 19 import std.exception : collectException; 20 import std.format : format; 21 22 public import miniorm : toSqliteDateTime, fromSqLiteDateTime, spinSql; 23 24 import dextool.type : AbsolutePath, Path; 25 import dextool.plugin.mutate.backend.type; 26 27 import dextool.plugin.mutate.backend.database.schema; 28 import dextool.plugin.mutate.type : MutationOrder; 29 public import dextool.plugin.mutate.backend.database.standalone; 30 public import dextool.plugin.mutate.backend.database.type; 31 32 /** Wrapper for a sqlite3 database that provide a uniform, easy-to-use 33 * interface for the mutation testing plugin. 34 */ 35 struct Database { 36 import std.conv : to; 37 import std.datetime : SysTime; 38 import std.exception : collectException; 39 import std.typecons : Nullable; 40 import dextool.plugin.mutate.backend.database.standalone : SDatabase = Database; 41 import dextool.plugin.mutate.backend.type : MutationPoint, Mutation, Checksum; 42 43 SDatabase db; 44 alias db this; 45 46 static auto make(AbsolutePath db) @safe { 47 return Database(SDatabase.make(db)); 48 } 49 50 /** Get the next mutation from the worklist to test by the highest 51 * priority. 52 * 53 * The chosen point is randomised. 54 * 55 * Params: 56 * kind = kind of mutation to retrieve. 57 */ 58 NextMutationEntry nextMutation(const(Mutation.Kind)[] kinds, const uint maxParallel) @trusted { 59 import dextool.plugin.mutate.backend.type; 60 61 typeof(return) rval; 62 63 immutable sql = format(" 64 SELECT * FROM 65 (SELECT 66 t0.id, 67 t0.kind, 68 t3.compile_time_ms, 69 t3.test_time_ms, 70 t1.offset_begin, 71 t1.offset_end, 72 t1.line, 73 t1.column, 74 t2.path, 75 t2.lang 76 FROM %1$s t0,%2$s t1,%3$s t2,%4$s t3, %5$s t4 77 WHERE 78 t0.st_id = t3.id AND 79 t3.id = t4.id AND 80 t0.mp_id == t1.id AND 81 t1.file_id == t2.id 82 ORDER BY t4.prio DESC LIMIT %6$s) 83 ORDER BY RANDOM() LIMIT 1", mutationTable, mutationPointTable, 84 filesTable, mutationStatusTable, mutantWorklistTable, maxParallel); 85 auto stmt = db.prepare(sql); 86 auto res = stmt.get.execute; 87 if (res.empty) { 88 rval.st = NextMutationEntry.Status.done; 89 return rval; 90 } 91 92 auto v = res.front; 93 94 auto mp = MutationPoint(Offset(v.peek!uint(4), v.peek!uint(5))); 95 mp.mutations = [Mutation(v.peek!long(1).to!(Mutation.Kind))]; 96 auto pkey = MutationId(v.peek!long(0)); 97 auto file = Path(v.peek!string(8)); 98 auto sloc = SourceLoc(v.peek!uint(6), v.peek!uint(7)); 99 auto lang = v.peek!long(9).to!Language; 100 101 rval.entry = MutationEntry(pkey, file, sloc, mp, 102 MutantTimeProfile(v.peek!long(2).dur!"msecs", v.peek!long(3).dur!"msecs"), lang); 103 104 return rval; 105 } 106 107 /// Iterate over the mutants of `kinds` in oldest->newest datum order. 108 void iterateMutantStatus(const Mutation.Kind[] kinds, 109 void delegate(const Mutation.Status, const SysTime added) dg) @trusted { 110 immutable sql = format("SELECT t1.status,t1.added_ts FROM %s t0, %s t1 111 WHERE 112 t0.st_id = t1.id AND 113 t0.kind IN (%(%s,%)) 114 ORDER BY t1.added_ts", 115 mutationTable, mutationStatusTable, kinds.map!(a => cast(int) a)); 116 auto stmt = db.prepare(sql); 117 try { 118 foreach (ref r; stmt.get.execute) { 119 dg(r.peek!int(0).to!(Mutation.Status), r.peek!string(1).fromSqLiteDateTime); 120 } 121 } catch (Exception e) { 122 logger.error(e.msg).collectException; 123 } 124 } 125 126 void iterateMutants(const Mutation.Kind[] kinds, void delegate(const ref IterateMutantRow) dg) @trusted { 127 import dextool.plugin.mutate.backend.utility : checksum; 128 129 immutable all_mutants = format("SELECT 130 t0.id, 131 t3.status, 132 t0.kind, 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 stmt = db.prepare(all_mutants); 156 foreach (ref r; stmt.get.execute) { 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(3), r.peek!uint(4)); 162 d.mutationPoint = MutationPoint(offset, null); 163 d.file = r.peek!string(9).Path; 164 d.fileChecksum = checksum(r.peek!long(10), r.peek!long(11)); 165 d.sloc = SourceLoc(r.peek!uint(5), r.peek!uint(6)); 166 d.slocEnd = SourceLoc(r.peek!uint(7), r.peek!uint(8)); 167 d.lang = r.peek!long(12).to!Language; 168 169 if (r.peek!long(13) != 0) { 170 d.attrs = MutantMetaData(d.id, MutantAttr(NoMut.init)); 171 } 172 dg(d); 173 } 174 } catch (Exception e) { 175 logger.error(e.msg).collectException; 176 } 177 } 178 179 FileRow[] getDetailedFiles() @trusted { 180 import std.array : appender; 181 import dextool.plugin.mutate.backend.utility : checksum; 182 183 enum files_q = format("SELECT t0.path, t0.checksum0, t0.checksum1, t0.lang, t0.id FROM %s t0", 184 filesTable); 185 auto app = appender!(FileRow[])(); 186 auto stmt = db.prepare(files_q); 187 foreach (ref r; stmt.get.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.get.bind(":path", cast(string) file); 234 foreach (ref r; stmt.get.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 Language lang; 259 MutantMetaData attrs; 260 } 261 262 struct FileRow { 263 Path file; 264 Checksum fileChecksum; 265 Language lang; 266 FileId id; 267 } 268 269 struct FileMutantRow { 270 MutationId id; 271 Mutation mutation; 272 MutationPoint mutationPoint; 273 SourceLoc sloc; 274 SourceLoc slocEnd; 275 Language lang; 276 }