Move db contents heuristics to separate file

This commit is contained in:
Koen Vlaswinkel
2023-06-12 11:07:25 +02:00
parent b0c18b3300
commit 754fa675f9
6 changed files with 116 additions and 97 deletions

View File

@@ -29,11 +29,13 @@ import {
withProgress,
} from "../common/vscode/progress";
import {
isLikelyDatabaseRoot,
isLikelyDbLanguageFolder,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
} from "../helpers";
import {
isLikelyDatabaseRoot,
isLikelyDbLanguageFolder,
} from "./local-databases/db-contents-heuristics";
import { extLogger } from "../common";
import {
importArchiveDatabase,

View File

@@ -10,7 +10,7 @@ import {
zipArchiveScheme,
} from "../../common/vscode/archive-filesystem-provider";
import { DatabaseItem, PersistedDatabaseItem } from "./database-item";
import { isLikelyDatabaseRoot } from "../../helpers";
import { isLikelyDatabaseRoot } from "./db-contents-heuristics";
import { stat } from "fs-extra";
import { pathsEqual } from "../../pure/files";
import { DatabaseContents } from "./database-contents";

View File

@@ -0,0 +1,35 @@
import { pathExists } from "fs-extra";
import { basename, join } from "path";
import { glob } from "glob";
/**
* The following functions al heuristically determine metadata about databases.
*/
/**
* Heuristically determines if the directory passed in corresponds
* to a database root. A database root is a directory that contains
* a codeql-database.yml or (historically) a .dbinfo file. It also
* contains a folder starting with `db-`.
*/
export async function isLikelyDatabaseRoot(maybeRoot: string) {
const [a, b, c] = await Promise.all([
// databases can have either .dbinfo or codeql-database.yml.
pathExists(join(maybeRoot, ".dbinfo")),
pathExists(join(maybeRoot, "codeql-database.yml")),
// they *must* have a db-{language} folder
glob("db-*/", { cwd: maybeRoot }),
]);
return (a || b) && c.length > 0;
}
/**
* A language folder is any folder starting with `db-` that is itself not a database root.
*/
export async function isLikelyDbLanguageFolder(dbPath: string) {
return (
basename(dbPath).startsWith("db-") && !(await isLikelyDatabaseRoot(dbPath))
);
}

View File

@@ -5,7 +5,6 @@ import {
writeFile,
opendir,
} from "fs-extra";
import { glob } from "glob";
import { join, basename, dirname } from "path";
import { dirSync } from "tmp-promise";
import { Uri, window as Window, workspace, env, WorkspaceFolder } from "vscode";
@@ -353,10 +352,6 @@ export async function prepareCodeTour(
}
}
/**
* The following functions al heuristically determine metadata about databases.
*/
/**
* Returns the initial contents for an empty query, based on the language of the selected
* databse.
@@ -378,34 +373,6 @@ export function getInitialQueryContents(language: string, dbscheme: string) {
return language ? `import ${language}\n\nselect ""` : 'select ""';
}
/**
* Heuristically determines if the directory passed in corresponds
* to a database root. A database root is a directory that contains
* a codeql-database.yml or (historically) a .dbinfo file. It also
* contains a folder starting with `db-`.
*/
export async function isLikelyDatabaseRoot(maybeRoot: string) {
const [a, b, c] = await Promise.all([
// databases can have either .dbinfo or codeql-database.yml.
pathExists(join(maybeRoot, ".dbinfo")),
pathExists(join(maybeRoot, "codeql-database.yml")),
// they *must* have a db-{language} folder
glob("db-*/", { cwd: maybeRoot }),
]);
return (a || b) && c.length > 0;
}
/**
* A language folder is any folder starting with `db-` that is itself not a database root.
*/
export async function isLikelyDbLanguageFolder(dbPath: string) {
return (
basename(dbPath).startsWith("db-") && !(await isLikelyDatabaseRoot(dbPath))
);
}
export function isQueryLanguage(language: string): language is QueryLanguage {
return Object.values(QueryLanguage).includes(language as QueryLanguage);
}

View File

@@ -0,0 +1,76 @@
import * as tmp from "tmp";
import { join } from "path";
import { mkdirSync, writeFileSync } from "fs-extra";
import {
isLikelyDatabaseRoot,
isLikelyDbLanguageFolder,
} from "../../../../src/databases/local-databases/db-contents-heuristics";
describe("isLikelyDatabaseRoot", () => {
let dir: tmp.DirResult;
beforeEach(() => {
dir = tmp.dirSync();
});
afterEach(() => {
dir.removeCallback();
});
it("should likely be a database root: codeql-database.yml", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
});
it("should likely be a database root: .dbinfo", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
});
it("should likely NOT be a database root: empty dir", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
});
it("should likely NOT be a database root: no db language folder", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
});
});
describe("isLikelyDbLanguageFolder", () => {
let dir: tmp.DirResult;
beforeEach(() => {
dir = tmp.dirSync();
});
afterEach(() => {
dir.removeCallback();
});
it("should find likely db language folder", async () => {
const dbFolder = join(dir.name, "db-python");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
// not a db folder since there is a db-python folder inside this one
expect(await isLikelyDbLanguageFolder(dbFolder)).toBe(false);
const nestedDbPythonFolder = join(dbFolder, "db-python");
expect(await isLikelyDbLanguageFolder(nestedDbPythonFolder)).toBe(true);
});
});

View File

@@ -4,7 +4,6 @@ import * as tmp from "tmp";
import { join } from "path";
import {
writeFileSync,
mkdirSync,
ensureDirSync,
symlinkSync,
writeFile,
@@ -16,8 +15,6 @@ import {
getFirstWorkspaceFolder,
getInitialQueryContents,
isFolderAlreadyInWorkspace,
isLikelyDatabaseRoot,
isLikelyDbLanguageFolder,
prepareCodeTour,
showBinaryChoiceDialog,
showBinaryChoiceWithUrlDialog,
@@ -66,64 +63,6 @@ describe("helpers", () => {
});
});
describe("likely database tests", () => {
let dir: tmp.DirResult;
beforeEach(() => {
dir = tmp.dirSync();
});
afterEach(() => {
dir.removeCallback();
});
it("should likely be a database root: codeql-database.yml", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
});
it("should likely be a database root: .dbinfo", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
});
it("should likely NOT be a database root: empty dir", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
});
it("should likely NOT be a database root: no db language folder", async () => {
const dbFolder = join(dir.name, "db");
mkdirSync(dbFolder);
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
});
it("should find likely db language folder", async () => {
const dbFolder = join(dir.name, "db-python");
mkdirSync(dbFolder);
mkdirSync(join(dbFolder, "db-python"));
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
// not a db folder since there is a db-python folder inside this one
expect(await isLikelyDbLanguageFolder(dbFolder)).toBe(false);
const nestedDbPythonFolder = join(dbFolder, "db-python");
expect(await isLikelyDbLanguageFolder(nestedDbPythonFolder)).toBe(true);
});
});
it("should report stream progress", () => {
const progressSpy = jest.fn();
const mockReadable = {