feat: add cross-database entity lookup for searching column values across all databases
Enables searching for a specific column value (e.g. carrier_id=123) across all databases on a PostgreSQL server. The backend creates temporary connection pools per database (semaphore-limited to 5), queries information_schema for matching columns, and executes read-only SELECTs with real-time progress events. Results are grouped by database/table in a new "Entity Lookup" tab. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useAppStore } from "@/stores/app-store";
|
||||
import { useConnections } from "@/hooks/use-connections";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { X, Table2, Code, Columns, Users, Activity } from "lucide-react";
|
||||
import { X, Table2, Code, Columns, Users, Activity, Search } from "lucide-react";
|
||||
|
||||
export function TabBar() {
|
||||
const { tabs, activeTabId, setActiveTabId, closeTab } = useAppStore();
|
||||
@@ -15,6 +15,7 @@ export function TabBar() {
|
||||
structure: <Columns className="h-3 w-3" />,
|
||||
roles: <Users className="h-3 w-3" />,
|
||||
sessions: <Activity className="h-3 w-3" />,
|
||||
lookup: <Search className="h-3 w-3" />,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -41,7 +42,14 @@ export function TabBar() {
|
||||
) : null;
|
||||
})()}
|
||||
{iconMap[tab.type]}
|
||||
<span className="max-w-[120px] truncate">{tab.title}</span>
|
||||
<span className="max-w-[150px] truncate">
|
||||
{tab.title}
|
||||
{tab.database && (
|
||||
<span className="ml-1 text-[10px] text-muted-foreground">
|
||||
{tab.database}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
className="ml-1 rounded-sm opacity-0 hover:bg-accent group-hover:opacity-100"
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ReadOnlyToggle } from "@/components/layout/ReadOnlyToggle";
|
||||
import { useAppStore } from "@/stores/app-store";
|
||||
import { useConnections, useReconnect } from "@/hooks/use-connections";
|
||||
import { toast } from "sonner";
|
||||
import { Database, Plus, RefreshCw } from "lucide-react";
|
||||
import { Database, Plus, RefreshCw, Search } from "lucide-react";
|
||||
import type { ConnectionConfig, Tab } from "@/types";
|
||||
import { getEnvironment } from "@/lib/environment";
|
||||
|
||||
@@ -16,7 +16,7 @@ export function Toolbar() {
|
||||
const [listOpen, setListOpen] = useState(false);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [editingConn, setEditingConn] = useState<ConnectionConfig | null>(null);
|
||||
const { activeConnectionId, addTab } = useAppStore();
|
||||
const { activeConnectionId, currentDatabase, addTab } = useAppStore();
|
||||
const { data: connections } = useConnections();
|
||||
const reconnectMutation = useReconnect();
|
||||
const activeConn = connections?.find((c) => c.id === activeConnectionId);
|
||||
@@ -38,11 +38,24 @@ export function Toolbar() {
|
||||
type: "query",
|
||||
title: "New Query",
|
||||
connectionId: activeConnectionId,
|
||||
database: currentDatabase ?? undefined,
|
||||
sql: "",
|
||||
};
|
||||
addTab(tab);
|
||||
};
|
||||
|
||||
const handleNewLookup = () => {
|
||||
if (!activeConnectionId) return;
|
||||
const tab: Tab = {
|
||||
id: crypto.randomUUID(),
|
||||
type: "lookup",
|
||||
title: "Entity Lookup",
|
||||
connectionId: activeConnectionId,
|
||||
database: currentDatabase ?? undefined,
|
||||
};
|
||||
addTab(tab);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -91,6 +104,17 @@ export function Toolbar() {
|
||||
New Query
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 gap-1.5"
|
||||
onClick={handleNewLookup}
|
||||
disabled={!activeConnectionId}
|
||||
>
|
||||
<Search className="h-3.5 w-3.5" />
|
||||
Entity Lookup
|
||||
</Button>
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
<span className="text-xs font-semibold text-muted-foreground tracking-wide">
|
||||
|
||||
Reference in New Issue
Block a user