Skip to content

Commit

Permalink
feat: group fts5 table
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal committed Jul 2, 2024
1 parent 86f98e1 commit e303e92
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 7 deletions.
57 changes: 52 additions & 5 deletions src/components/gui/schema-sidebar-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ interface SchemaListProps {
search: string;
}

type DatabaseSchemaItemWithIndentation = DatabaseSchemaItem & {
indentation?: number;
};

type DatabaseSchemaTreeNode = {
node: DatabaseSchemaItem;
sub: DatabaseSchemaItem[];
node: DatabaseSchemaItemWithIndentation;
sub: DatabaseSchemaItemWithIndentation[];
};

interface SchemaViewItemProps {
Expand All @@ -30,6 +34,7 @@ interface SchemaViewItemProps {
onClick: () => void;
onContextMenu: React.MouseEventHandler;
indentation?: boolean;
badge?: string;
}

function SchemaViewItem({
Expand All @@ -42,6 +47,7 @@ function SchemaViewItem({
onContextMenu,
indentation,
item,
badge,
}: Readonly<SchemaViewItemProps>) {
const regex = new RegExp(
"(" + (highlight ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + ")",
Expand Down Expand Up @@ -83,6 +89,7 @@ function SchemaViewItem({
<div className="w-2 border-l ml-2 2 h-full border-dashed"></div>
)}
<Icon className={cn("mr-2 h-4 w-4", selected ? "" : iconClassName)} />

<span>
{splitedText.map((text, idx) => {
return text.toLowerCase() === (highlight ?? "").toLowerCase() ? (
Expand All @@ -93,6 +100,12 @@ function SchemaViewItem({
<span key={idx}>{text}</span>
);
})}

{badge && (
<span className="bg-red-500 text-white rounded p-0.5 px-1 ml-1 text-xs font-mono font-normal">
{badge}
</span>
)}
</span>
</div>
);
Expand Down Expand Up @@ -151,11 +164,42 @@ export default function SchemaList({ search }: Readonly<SchemaListProps>) {
let tree: DatabaseSchemaTreeNode[] = [];
const treeHash: Record<string, DatabaseSchemaTreeNode> = {};

const excludeTables = new Set();
const ftsTables: string[] = [];
const ftsSuffix = ["_config", "_content", "_data", "_docsize", "_idx"];

// Scan for FTS5
for (const item of schema) {
if (item.name && item.tableSchema?.fts5) {
const tableName = item.name;
ftsTables.push(tableName);
for (const suffix of ftsSuffix) {
excludeTables.add(tableName + suffix);
}
}
}

for (const item of schema) {
if (item.type === "table" || item.type === "view") {
const node = { node: item, sub: [] };
treeHash[item.name] = node;
tree.push(node);

if (item.name && !excludeTables.has(item.name)) {
tree.push(node);
}
}
}

// Grouping FTS5 table
for (const ftsTableName of ftsTables) {
const ftsSubgroup = treeHash[ftsTableName].sub;
if (ftsSubgroup) {
for (const suffix of ftsSuffix) {
const ftsSubTable = treeHash[ftsTableName + suffix];
if (ftsSubTable) {
treeHash[ftsTableName].sub.push(ftsSubTable.node);
}
}
}
}

Expand All @@ -177,7 +221,9 @@ export default function SchemaList({ search }: Readonly<SchemaListProps>) {
return foundName || foundInChildren;
});

return tree.map((r) => [r.node, ...r.sub]).flat();
return tree
.map((r) => [r.node, ...r.sub.map((d) => ({ ...d, indentation: 1 }))])
.flat();
}, [schema, search]);

return (
Expand Down Expand Up @@ -217,9 +263,10 @@ export default function SchemaList({ search }: Readonly<SchemaListProps>) {
title={item.name}
iconClassName={iconClassName}
icon={icon}
indentation={item.type === "trigger"}
indentation={!!item.indentation}
selected={schemaIndex === selectedIndex}
onClick={() => setSelectedIndex(schemaIndex)}
badge={item.tableSchema?.fts5 ? "fts5" : undefined}
/>
);
})}
Expand Down
6 changes: 6 additions & 0 deletions src/drivers/base-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,19 @@ export interface DatabaseTableColumnConstraint {
foreignKey?: DatabaseForeignKeyClause;
}

export interface DatabaseTableFts5 {
content?: string;
contentRowId?: string;
}

export interface DatabaseTableSchema {
columns: DatabaseTableColumn[];
pk: string[];
autoIncrement: boolean;
tableName?: string;
constraints?: DatabaseTableColumnConstraint[];
createScript?: string;
fts5?: DatabaseTableFts5;
}

export type TriggerWhen = "BEFORE" | "AFTER" | "INSTEAD_OF";
Expand Down
27 changes: 27 additions & 0 deletions src/drivers/sqlite/sql-parse-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,30 @@ it("parse create table with table constraints", () => {
],
} as DatabaseTableSchema);
});

it("parse fts5 virtual table", () => {
const sql = `create virtual table name_fts using fts5(name, tokenize='trigram');`;
expect(p(sql)).toEqual({
tableName: "name_fts",
autoIncrement: false,
pk: [],
columns: [],
constraints: [],
fts5: {},
} as DatabaseTableSchema);
});

it("parse fts5 virtual table with external content", () => {
const sql = `create virtual table name_fts using fts5(name, tokenize='trigram', content='student', content_rowid='id');`;
expect(p(sql)).toEqual({
tableName: "name_fts",
autoIncrement: false,
pk: [],
columns: [],
constraints: [],
fts5: {
content: "'student'",
contentRowId: "'id'",
},
} as DatabaseTableSchema);
});
52 changes: 52 additions & 0 deletions src/drivers/sqlite/sql-parse-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
DatabaseTableColumnConstraint,
DatabaseTableSchema,
SqlOrder,
DatabaseTableFts5,
} from "@/drivers/base-driver";
import { unescapeIdentity } from "./sql-helper";
import { sqliteDialect } from "@/drivers/sqlite/sqlite-dialect";
Expand Down Expand Up @@ -456,6 +457,43 @@ function parseTableDefinition(cursor: Cursor): {
return { columns, constraints };
}

function parseFTS5(cursor: Cursor | null): DatabaseTableFts5 {
if (!cursor) return {};

let content: string | undefined;
let contentRowId: string | undefined;

const ptr = cursor;
while (!ptr.end()) {
if (ptr.match("content")) {
ptr.next();
if (ptr.match("=")) {
ptr.next();
if (!ptr.end()) {
content = unescapeIdentity(ptr.read());
ptr.next();
}
}
} else if (ptr.match("content_rowid")) {
ptr.next();
if (ptr.match("=")) {
ptr.next();
if (!ptr.end()) {
contentRowId = unescapeIdentity(ptr.read());
ptr.next();
}
}
}

ptr.next();
}

return {
content,
contentRowId,
};
}

// Our parser follows this spec
// https://www.sqlite.org/lang_createtable.html
export function parseCreateTableScript(sql: string): DatabaseTableSchema {
Expand All @@ -469,10 +507,23 @@ export function parseCreateTableScript(sql: string): DatabaseTableSchema {
cursor.expectKeyword("CREATE");
cursor.expectKeywordOptional("TEMP");
cursor.expectKeywordOptional("TEMPORARY");
cursor.expectKeywordOptional("VIRTUAL");
cursor.expectKeyword("TABLE");
cursor.expectKeywordsOptional(["IF", "NOT", "EXIST"]);
const tableName = cursor.consumeIdentifier();

// Check for FTS5
let fts5: DatabaseTableFts5 | undefined;

if (cursor.match("USING")) {
cursor.next();
if (cursor.match("FTS5")) {
cursor.next();
fts5 = parseFTS5(cursor.enterParens());
cursor.next();
}
}

const defCursor = cursor.enterParens();
const defs = defCursor
? parseTableDefinition(defCursor)
Expand All @@ -489,5 +540,6 @@ export function parseCreateTableScript(sql: string): DatabaseTableSchema {
...defs,
pk,
autoIncrement,
fts5,
};
}
3 changes: 1 addition & 2 deletions src/drivers/sqljs-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default class SqljsDriver extends SqliteLikeBaseDriver {

const startTime = Date.now();
const s = this.db.prepare(sql, bind);
const endTime = Date.now();

// Do the transform result here
const headerName = s.getColumnNames();
Expand Down Expand Up @@ -71,8 +72,6 @@ export default class SqljsDriver extends SqliteLikeBaseDriver {
);
}

const endTime = Date.now();

return {
headers,
rows,
Expand Down

0 comments on commit e303e92

Please sign in to comment.