D-Scanner/cache.d

218 lines
5.5 KiB
D

// Copyright Brian Schott (Sir Alaran) 2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module cache;
import etc.c.sqlite3;
import std.c.stdlib;
import std.datetime;
import std.file;
import std.uuid;
import std.array;
import std.string;
import std.conv;
import std.d.lexer;
import location;
import parser;
import types;
private sqlite3* database;
version (Posix)
{
private immutable char* DB_PATH = "~/.dscanner/cache.db";
}
else version (Windows)
{
pragma(msg, "Caching not supported on Windows yet");
immutable string DB_PATH = "";
}
private enum Queries : string
{
getUpdateTime = "select mtime from files where filepath = ?",
insertContainer = "insert into containers values ()",
deleteContainer = "delete from containers where fileId = ?",
deleteSymbol = "delete from symbols where containerId = ?",
deleteFile = "delete from files where path = ?",
getPublicImports = "select importedId from publicImports where importerId = ?",
getModuleId = "select id from files where path = ?",
getContainersByModule = "select id from containers where fileId = ?"
}
private sqlite3* getDatabase()
{
if (database !is null)
return database;
int status = sqlite3_open(DB_PATH, &database);
if (status != SQLITE_OK)
{
throw new Exception("Could not open %s: %s".format(DB_PATH,
sqlite3_errmsg(database)));
}
return database;
}
void closeDatabase()
{
if (database !is null)
{
sqlite3_close(database);
database = null;
}
}
private long getCachedModTime(sqlite3* db, sqlite3_stmt* statement, string filePath)
{
bindText(statement, 1, filePath);
if (sqlite3_step(statement) != SQLITE_ROW)
throw new Exception("%s".format(sqlite3_errmsg(db)));
return sqlite3_column_int64(statement, 1);
}
/**
* Updates the sqlite database with current autocomplete information for the
* given modules.
*/
void updateCache(string dirs[], string moduleNames[])
{
string[] filePaths;
foreach (moduleName; moduleNames)
{
string path = findAbsPath(dirs, moduleName);
if (path is null)
continue;
filePaths ~= path;
}
sqlite3* db = getDatabase();
sqlite3_stmt* statement;
scope(exit) { if (statement) sqlite3_finalize(statement); }
char* pzTail;
scope(exit) { if (pzTail) free(pzTail); }
sqlite3_prepare_v2(db, Queries.getUpdateTime.toStringz(),
cast(int) Queries.getUpdateTime.length + 1, &statement, &pzTail);
foreach (string filePath; filePaths)
{
immutable long mtime = getCachedModTime(db, statement, filePath);
SysTime timeLastModified = timeLastModified(filePath);
// if the times match, we don't need to update the cache.
if (timeLastModified.stdTime == mtime)
continue;
// // re-parse the module
// Module m = parseModule(byToken(readText(filePath)).array());
//
// updateCache(m);
sqlite3_reset(statement);
}
}
private void updateCache(const Module m)
in
{
assert(m !is null);
}
body
{
}
private string[] getImportedModules(string modulePath, sqlite3_stmt* statement = null)
{
auto app = appender!(string[])();
sqlite3* db = getDatabase();
bool statementAllocated = false;
scope(exit) { if (statementAllocated && statement !is null) sqlite3_finalize(statement); }
if (statement is null)
{
statementAllocated = true;
char* pzTail;
scope(exit) { if (pzTail) free(pzTail); }
sqlite3_prepare_v2(db, Queries.getPublicImports.toStringz(),
cast(int) Queries.getPublicImports.length + 1, &statement, &pzTail);
}
string moduleId = getModuleIdFromPath(modulePath);
bindText(statement, 1, moduleId);
while (sqlite3_step(statement) == SQLITE_ROW)
{
app.put(to!string(sqlite3_column_text(statement, 1)));
}
sqlite3_reset(statement);
foreach (string imported; app.data)
{
string[] r = getImportedModules(imported, statement);
}
return app.data;
}
private string getModuleIdFromPath(string filePath)
{
sqlite3* db = getDatabase();
sqlite3_stmt* statement;
char* pzTail;
scope(exit) if (pzTail) free(pzTail);
sqlite3_prepare_v2(db, Queries.getModuleId.toStringz(),
cast(int) Queries.getModuleId.length + 1, &statement,
&pzTail);
bindText(statement, 1, filePath);
if (sqlite3_step(statement) != SQLITE_ROW)
return null;
return to!string(sqlite3_column_text(statement, 1));
}
/**
* Returns: the container IDs of the containers that have
* been imported
*/
public string[] getContainersImported(string modulePath)
{
immutable string moduleId = getModuleIdFromPath(modulePath);
sqlite3* db = getDatabase();
sqlite3_stmt* statement;
char* pzTail;
scope(exit) if (pzTail) free(pzTail);
string[] moduleIds = getImportedModules(modulePath);
string[] containerIds;
foreach (string id; moduleIds)
{
containerIds ~= getContainersByModule(id);
}
return containerIds;
}
private string[] getContainersByModule(string moduleId)
{
sqlite3* db = getDatabase();
sqlite3_stmt* statement;
scope(exit) if (statement !is null) sqlite3_finalize(statement);
char* pzTail;
prepareStatement(db, statement, Queries.getContainersByModule);
bindText(statement, 1, moduleId);
string[] rVal;
while (sqlite3_step(statement) == SQLITE_ROW)
{
rVal ~= to!string(sqlite3_column_text(statement, 1));
}
return rVal;
}
private void prepareStatement(sqlite3* db, sqlite3_stmt* statement, string query)
{
char* pzTail;
scope(exit) if (pzTail) free(pzTail);
sqlite3_prepare_v2(db, query.toStringz(), cast(int) query.length + 1,
&statement, &pzTail);
}
private void bindText(sqlite3_stmt* statement, int argPos, string text)
{
sqlite3_bind_text(statement, argPos, text.toStringz(),
cast(int) text.length + 1, SQLITE_TRANSIENT);
}