Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/components/catalog/CatalogSqlPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FormEvent, ReactElement, useState } from "react";
import { Link as RouterLink } from "react-router-dom";
import { FormEvent, ReactElement, useEffect, useRef, useState } from "react";
import { Link as RouterLink, useLocation } from "react-router-dom";
import type {
TapSchemaEntry,
TapSyncResponse,
Expand All @@ -23,18 +23,24 @@ interface CatalogSqlPanelProps {
onSqlChange: (sql: string) => void;
schemas?: TapSchemaEntry[];
loggedIn: boolean;
permalinkRunKey?: string | null;
onQueryRun?: (sql: string) => void;
}

export function CatalogSqlPanel({
sql,
onSqlChange,
schemas,
loggedIn,
permalinkRunKey,
onQueryRun,
}: CatalogSqlPanelProps): ReactElement {
const [result, setResult] = useState<TapSyncResponse | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const runShortcut = runQueryShortcutLabel();
const location = useLocation();
const didAutoRun = useRef(false);

async function runQuery(): Promise<void> {
if (!loggedIn || loading) {
Expand All @@ -50,6 +56,7 @@ export function CatalogSqlPanel({
setLoading(true);
setError(null);
setResult(null);
onQueryRun?.(trimmed);

try {
const payload = await executeSqlQuery(trimmed);
Expand All @@ -68,6 +75,21 @@ export function CatalogSqlPanel({
await runQuery();
}

useEffect(() => {
didAutoRun.current = false;
}, [location.key]);

useEffect(() => {
if (!permalinkRunKey || !loggedIn || didAutoRun.current) {
return;
}
if (sql.trim() !== permalinkRunKey.trim()) {
return;
}
didAutoRun.current = true;
void runQuery();
}, [permalinkRunKey, loggedIn, sql]);

const tableData = result ? syncPayloadToTable(result) : null;
const rowCount = tableData?.rows.length ?? 0;

Expand Down
11 changes: 11 additions & 0 deletions src/lib/tap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,14 @@ export function syncPayloadToTable(payload: TapSyncResponse): {
export function defaultSelectForTable(tableName: string, limit = 25): string {
return `SELECT * FROM ${tableName} LIMIT ${limit}`;
}

export function parseSqlPermalink(raw: string): string {
const trimmed = raw.trim();
if (
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
(trimmed.startsWith("'") && trimmed.endsWith("'"))
) {
return trimmed.slice(1, -1);
}
return trimmed;
}
40 changes: 37 additions & 3 deletions src/pages/DataCatalog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { ReactElement, useEffect, useMemo, useState } from "react";
import { useMatch, useNavigate, useParams } from "react-router-dom";
import {
ReactElement,
useEffect,
useLayoutEffect,
useMemo,
useState,
} from "react";
import {
useMatch,
useNavigate,
useParams,
useSearchParams,
} from "react-router-dom";
import { tapSync, tapTables } from "../clients/backend/sdk.gen";
import type {
ListTapTablesResponse,
Expand Down Expand Up @@ -30,6 +41,7 @@ import {
DEFAULT_SQL_EXAMPLE,
defaultSelectForTable,
formatApiError,
parseSqlPermalink,
} from "../lib/tap";

async function fetchTablesList(): Promise<ListTapTablesResponse> {
Expand Down Expand Up @@ -309,7 +321,9 @@ export function DataCatalogPage(): ReactElement {
tableName?: string;
}>();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const isQueryMode = Boolean(useMatch("/data-catalog/query"));
const permalinkSql = searchParams.get("q");
const [filter, setFilter] = useState("");
const [sqlDraft, setSqlDraft] = useState(DEFAULT_SQL_EXAMPLE);
const loggedIn = isLoggedIn();
Expand All @@ -332,6 +346,13 @@ export function DataCatalogPage(): ReactElement {
: "Data catalog | HyperLEDA";
}, [isQueryMode]);

useLayoutEffect(() => {
if (!isQueryMode || !permalinkSql) {
return;
}
setSqlDraft(parseSqlPermalink(permalinkSql));
}, [isQueryMode, permalinkSql]);

const {
data: tablesPayload,
loading: tablesLoading,
Expand Down Expand Up @@ -364,8 +385,17 @@ export function DataCatalogPage(): ReactElement {
function openSqlEditor(sql?: string): void {
if (sql) {
setSqlDraft(sql);
navigate({
pathname: "/data-catalog/query",
search: `?q=${encodeURIComponent(sql)}`,
});
return;
}
navigate("/data-catalog/query");
navigate({ pathname: "/data-catalog/query", search: "" });
}

function handleQueryRun(sql: string): void {
setSearchParams({ q: sql }, { replace: true });
}

function handleSelect(nextSchema: string, nextTable: string): void {
Expand Down Expand Up @@ -413,6 +443,10 @@ export function DataCatalogPage(): ReactElement {
onSqlChange={setSqlDraft}
schemas={tablesPayload?.schemas}
loggedIn={loggedIn}
permalinkRunKey={
permalinkSql ? parseSqlPermalink(permalinkSql) : null
}
onQueryRun={handleQueryRun}
/>
);
}
Expand Down
Loading