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
1 change: 0 additions & 1 deletion server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ declare const __dirname: string
// Injected by esbuild define
declare const __APP_VERSION__: string
declare const __DEV__: boolean
declare const __GIT_COMMIT__: string
const app = express()

// Parse command line arguments
Expand Down
4 changes: 3 additions & 1 deletion server/lib/connection-cache.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { createClient } from './db'

// In-memory cache for connection runtime information
export interface ConnectionInfo {
version: string // PostgreSQL major version (e.g., "16")
Expand Down Expand Up @@ -50,7 +52,7 @@ export function clearConnectionCache(connectionId?: string): void {
* @returns PostgreSQL major version (e.g., "16")
* @throws Error if version cannot be extracted
*/
export async function testAndCacheConnection(client: any, connectionId: string): Promise<string> {
export async function testAndCacheConnection(client: ReturnType<typeof createClient>, connectionId: string): Promise<string> {
// Test connectivity
await client`SELECT 1`

Expand Down
2 changes: 1 addition & 1 deletion server/lib/schema-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ async function buildSchemaContext(
lines.push(`PostgreSQL ${version}`)
lines.push('')

for (const [_key, table] of schemaMap) {
for (const table of schemaMap.values()) {
// Table header
lines.push(`${table.schema}.${table.table} (${table.objectType})`)
if (table.comment) {
Expand Down
4 changes: 1 addition & 3 deletions server/services/query-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async function getColumnMetadata(
}

// Get table info, primary key columns, column nullability, and columns with defaults
let tableInfo = new Map<number, { schema: string; table: string; pkColumns: Set<string>; notNullColumns: Set<string>; defaultColumns: Set<string> }>();
const tableInfo = new Map<number, { schema: string; table: string; pkColumns: Set<string>; notNullColumns: Set<string>; defaultColumns: Set<string> }>();
if (tableOids.length > 0) {
const tableRows = await client`
SELECT
Expand Down Expand Up @@ -1072,8 +1072,6 @@ export const queryServiceHandlers: ServiceImpl<typeof QueryService> = {
// Check which objects are referenced in the function definition
// Look for patterns like: schema.name, "schema"."name", or just name (for tables in search_path)
const deps: Array<{ schema: string; name: string; type: string; arguments: string }> = [];
const definitionLower = definition.toLowerCase();

for (const obj of objectsResult) {
const schema = obj.schema as string;
const name = obj.name as string;
Expand Down
7 changes: 3 additions & 4 deletions src/components/Gutter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Home, Settings } from 'lucide-react'
import type { ComponentType } from 'react'
import { Home, Settings, type LucideIcon } from 'lucide-react'
import { useNavigate, useLocation } from 'react-router-dom'
import { Button } from './ui/button'
import { useMemo } from 'react'
Expand Down Expand Up @@ -41,12 +40,12 @@ export default function Gutter({ activeItem, onItemClick }: GutterProps) {
}
}

const gutterIcons: { id: GutterItem; Icon: ComponentType<{ size?: number }>; label: string }[] = [
const gutterIcons: { id: GutterItem; Icon: LucideIcon; label: string }[] = [
{ id: 'home', Icon: Home, label: 'Organization Home' },
{ id: 'settings', Icon: Settings, label: 'Organization Settings' },
]

const renderButton = ({ id, Icon, label }: { id: GutterItem; Icon: ComponentType<any>; label: string }) => {
const renderButton = ({ id, Icon, label }: { id: GutterItem; Icon: LucideIcon; label: string }) => {
const isActive = activeItem === id
return (
<Button
Expand Down
24 changes: 23 additions & 1 deletion src/components/sql-editor/ObjectTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,29 @@ function ObjectItem({
}

export function ObjectTree(props: ObjectTreeProps) {
const { tables, views, materializedViews, functions, procedures, connectionId: _connectionId, ...shared } = props
const {
tables,
views,
materializedViews,
functions,
procedures,
searchQuery,
selectedObject,
onObjectSelect,
onObjectDoubleClick,
onViewSchema,
onGenerateSQL,
schema,
} = props
const shared = {
searchQuery,
selectedObject,
onObjectSelect,
onObjectDoubleClick,
onViewSchema,
onGenerateSQL,
schema,
}

return (
<ScrollArea className="flex-1">
Expand Down
4 changes: 2 additions & 2 deletions src/components/sql-editor/pg-autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ function hasQualifierDot(completion: Completion): boolean {
* Only keywords and dot-qualifiers retrigger — tables/columns do not,
* since the user may want to type `;`, alias, or other non-space characters.
*/
function wrapCompletionsForRetrigger(completions: Completion[], _section: SQLSection): Completion[] {
function wrapCompletionsForRetrigger(completions: Completion[]): Completion[] {
return completions.map((c) => {
// Wrap keywords that are in the retrigger set
if (c.type === 'keyword' && RETRIGGER_KEYWORDS.has(c.label)) {
Expand Down Expand Up @@ -665,7 +665,7 @@ async function pgCompletionSource(ctx: CompletionContext): Promise<CompletionRes
let completions = filteredSuggestions.map(toCompletion)

// Wrap keywords/tables that should retrigger autocomplete
completions = wrapCompletionsForRetrigger(completions, updatedResult.context.section)
completions = wrapCompletionsForRetrigger(completions)

// Calculate the correct 'from' position for completion replacement
// When completing after "table.", we only want to replace the part after the dot
Expand Down
5 changes: 4 additions & 1 deletion src/components/ui/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,10 @@ function CommandEmpty({
function CommandPanel({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
className="-mx-px relative min-h-0 rounded-t-xl border bg-popover bg-clip-padding shadow-xs [clip-path:inset(0_1px)] before:pointer-events-none before:absolute before:inset-0 before:rounded-t-[calc(var(--radius-xl)-1px)] **:data-[slot=scroll-area-scrollbar]:mt-2 dark:bg-clip-border dark:before:shadow-[0_-1px_--theme(--color-white/8%)]"
className={cn(
"-mx-px relative min-h-0 rounded-t-xl border bg-popover bg-clip-padding shadow-xs [clip-path:inset(0_1px)] before:pointer-events-none before:absolute before:inset-0 before:rounded-t-[calc(var(--radius-xl)-1px)] **:data-[slot=scroll-area-scrollbar]:mt-2 dark:bg-clip-border dark:before:shadow-[0_-1px_--theme(--color-white/8%)]",
className,
)}
{...props}
/>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function SheetViewport({
side === "left" && "flex justify-start",
side === "right" && "flex justify-end",
inset && "sm:p-4",
className,
)}
data-slot="sheet-viewport"
{...props}
Expand Down
4 changes: 3 additions & 1 deletion src/lib/auth-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export async function getProviders(): Promise<AuthProvider[]> {
const data = await res.json()
return data.providers || []
}
} catch {}
} catch {
return []
}
return []
}
5 changes: 2 additions & 3 deletions src/lib/sql/autocomplete/candidate-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export function generateCandidates(
return generateSelectColumnCandidates(context, scope, schema)

case 'FROM_TABLE':
return generateFromTableCandidates(scope, schema, context)
return generateFromTableCandidates(scope, schema)

case 'JOIN_TABLE':
case 'INSERT_TABLE':
Expand Down Expand Up @@ -453,7 +453,7 @@ function generateColumnCandidatesForTable(
* Generate candidates for FROM clause.
* Includes tables and clause transition keywords (WHERE, JOIN, etc.)
*/
function generateFromTableCandidates(scope: ScopeInfo, schema: SchemaInfo, _context: CursorContext): Candidate[] {
function generateFromTableCandidates(scope: ScopeInfo, schema: SchemaInfo): Candidate[] {
const candidates: Candidate[] = []

// 1. Tables (primary suggestions)
Expand Down Expand Up @@ -1055,4 +1055,3 @@ function generateDefaultCandidates(scope: ScopeInfo, schema: SchemaInfo): Candid

return candidates
}

13 changes: 6 additions & 7 deletions src/lib/sql/autocomplete/scope-analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export function analyzeScope(
}

// Extract tables and aliases from tokens
const tablesFromTokens = extractTablesFromTokens(tokenized, sql)
const tablesFromTokens = extractTablesFromTokens(tokenized)
scopeInfo.availableTables = tablesFromTokens

// Extract CTEs if present
const ctesFromTokens = extractCTEsFromTokens(tokenized, sql)
const ctesFromTokens = extractCTEsFromTokens(tokenized)
scopeInfo.ctes = ctesFromTokens

// Add CTE names as virtual tables
Expand All @@ -80,7 +80,7 @@ export function analyzeScope(
if (pgQueryResult.valid) {
scopeInfo.isPgQueryValid = true
// Merge pg_query results with token-based results
mergePgQueryResults(scopeInfo, pgQueryResult, schema)
mergePgQueryResults(scopeInfo, pgQueryResult)
}

// Resolve columns for tables in scope
Expand All @@ -92,7 +92,7 @@ export function analyzeScope(
/**
* Extract table references and aliases from tokens.
*/
function extractTablesFromTokens(tokenized: TokenizedSQL, _sql: string): TableRef[] {
function extractTablesFromTokens(tokenized: TokenizedSQL): TableRef[] {
const tables: TableRef[] = []
const tokens = tokenized.tokens

Expand Down Expand Up @@ -223,7 +223,7 @@ function isInFromClause(tokens: Token[], index: number): boolean {
/**
* Extract CTEs from WITH clause.
*/
function extractCTEsFromTokens(tokenized: TokenizedSQL, _sql: string): CTERef[] {
function extractCTEsFromTokens(tokenized: TokenizedSQL): CTERef[] {
const ctes: CTERef[] = []
const tokens = tokenized.tokens

Expand Down Expand Up @@ -387,8 +387,7 @@ function extractFromPgQueryAST(
*/
function mergePgQueryResults(
scopeInfo: ScopeInfo,
pgQueryResult: PgQueryResult,
_schema: SchemaInfo
pgQueryResult: PgQueryResult
): void {
// Add any tables from pg_query not found in token analysis
for (const pgTable of pgQueryResult.tables) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/sql/autocomplete/section-detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ function calculateSubqueryDepth(tokens: Token[]): number {
/**
* Check if cursor is at a position expecting a table alias.
*/
export function isExpectingAlias(tokenized: TokenizedSQL, _cursorPosition: number): boolean {
export function isExpectingAlias(tokenized: TokenizedSQL): boolean {
const tokens = getTokensUpToCursor(tokenized, true)
if (tokens.length < 2) return false

Expand Down
4 changes: 2 additions & 2 deletions src/lib/sql/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1709,7 +1709,7 @@ function transformCreateFunction(raw: Record<string, unknown>, source: string):
if (defexpr) {
// Try to extract the source text for the default expression
// For now, we'll use a simplified approach
defaultVal = extractExprSource(defexpr, source)
defaultVal = extractExprSource(defexpr)
}

const fp: FunctionParameter = {
Expand Down Expand Up @@ -1890,7 +1890,7 @@ function extractNumericValue(node: Record<string, unknown>): number | null {
return null
}

function extractExprSource(expr: unknown, _fullSource: string): string | null {
function extractExprSource(expr: unknown): string | null {
// Try to extract source text based on location info
if (!expr || typeof expr !== 'object') return null
const obj = expr as Record<string, unknown>
Expand Down
38 changes: 19 additions & 19 deletions src/lib/sql/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
ColumnDef,
TableConstraint,
Expr,
GroupingSet,
FromClause,
SortExpr,
TargetExpr,
Expand Down Expand Up @@ -77,7 +78,7 @@ function formatStatement(stmt: Statement): string {
case 'alter_table': return formatAlterTable(stmt)
case 'create_function': return formatCreateFunction(stmt)
case 'unknown': return stmt.source
// DDL statements - preserve original source (formatting not yet implemented)
// Statements without dedicated formatting support yet preserve original source.
case 'drop':
case 'create_view':
case 'create_index':
Expand All @@ -92,14 +93,12 @@ function formatStatement(stmt: Statement): string {
case 'grant':
case 'revoke':
case 'refresh_matview':
// Utility statements - preserve original source
case 'explain':
case 'copy':
case 'set':
case 'show':
case 'transaction':
case 'vacuum':
// Admin statements - preserve original source
case 'create_role':
case 'alter_role':
case 'drop_role':
Expand Down Expand Up @@ -237,31 +236,32 @@ function formatFrom(from: FromClause): string {
}
}

function formatGroupByItem(item: any): string {
// Check if it's a GroupingSet
if (item && typeof item === 'object' && 'kind' in item &&
(item.kind === 'rollup' || item.kind === 'cube' || item.kind === 'sets' || item.kind === 'empty')) {
const gs = item as import('./core').GroupingSet
function isGroupingSet(item: Expr | GroupingSet): item is GroupingSet {
return item.kind === 'rollup' || item.kind === 'cube' || item.kind === 'sets' || item.kind === 'empty'
}

if (gs.kind === 'empty') {
function formatGroupByItem(item: Expr | GroupingSet): string {
// Check if it's a GroupingSet
if (isGroupingSet(item)) {
if (item.kind === 'empty') {
return '()'
}

if (gs.kind === 'rollup') {
if (gs.content.length === 0) return 'ROLLUP ()'
const content = gs.content.map(formatGroupByItem).join(', ')
if (item.kind === 'rollup') {
if (item.content.length === 0) return 'ROLLUP ()'
const content = item.content.map(formatGroupByItem).join(', ')
return `ROLLUP (${content})`
}

if (gs.kind === 'cube') {
if (gs.content.length === 0) return 'CUBE ()'
const content = gs.content.map(formatGroupByItem).join(', ')
if (item.kind === 'cube') {
if (item.content.length === 0) return 'CUBE ()'
const content = item.content.map(formatGroupByItem).join(', ')
return `CUBE (${content})`
}

if (gs.kind === 'sets') {
if (gs.content.length === 0) return 'GROUPING SETS ()'
const content = gs.content.map(item => {
if (item.kind === 'sets') {
if (item.content.length === 0) return 'GROUPING SETS ()'
const content = item.content.map(item => {
// Each item in GROUPING SETS needs to be wrapped in parentheses
// unless it's already a GroupingSet (empty, rollup, cube, or nested sets)
const formatted = formatGroupByItem(item)
Expand All @@ -276,7 +276,7 @@ function formatGroupByItem(item: any): string {
}

// Otherwise it's a regular expression
return formatExpr(item)
return formatExpr(item as Expr)
}

function formatTableRef(t: TableRef): string {
Expand Down
2 changes: 1 addition & 1 deletion website/src/components/sections/faqs-accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function Faq({
answer,
...props
}: { question: ReactNode; answer: ReactNode } & ComponentProps<'div'>) {
let autoId = useId()
const autoId = useId()
id = id || autoId

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function Faq({
answer,
...props
}: { question: ReactNode; answer: ReactNode } & ComponentProps<'div'>) {
let autoId = useId()
const autoId = useId()
id = id || autoId

return (
Expand Down
2 changes: 1 addition & 1 deletion website/src/components/sections/stats-with-graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function Stat({
}

export function StatsWithGraph({ children, ...props }: ComponentProps<typeof Section>) {
let pathId = useId()
const pathId = useId()

return (
<Section {...props}>
Expand Down
Loading