Skip to content
Open
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
20 changes: 4 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/lib/PostgresMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Parser from './Parser.js'
import PostgresMetaColumnPrivileges from './PostgresMetaColumnPrivileges.js'
import PostgresMetaColumns from './PostgresMetaColumns.js'
import PostgresMetaConfig from './PostgresMetaConfig.js'
import PostgresMetaDependencyGraph from './PostgresMetaDependencyGraph.js'
import PostgresMetaExtensions from './PostgresMetaExtensions.js'
import PostgresMetaForeignTables from './PostgresMetaForeignTables.js'
import PostgresMetaFunctions from './PostgresMetaFunctions.js'
Expand Down Expand Up @@ -30,6 +31,7 @@ export default class PostgresMeta {
columnPrivileges: PostgresMetaColumnPrivileges
columns: PostgresMetaColumns
config: PostgresMetaConfig
dependencyGraph: PostgresMetaDependencyGraph
extensions: PostgresMetaExtensions
foreignTables: PostgresMetaForeignTables
functions: PostgresMetaFunctions
Expand Down Expand Up @@ -58,6 +60,7 @@ export default class PostgresMeta {
this.columnPrivileges = new PostgresMetaColumnPrivileges(this.query)
this.columns = new PostgresMetaColumns(this.query)
this.config = new PostgresMetaConfig(this.query)
this.dependencyGraph = new PostgresMetaDependencyGraph(this.query)
this.extensions = new PostgresMetaExtensions(this.query)
this.foreignTables = new PostgresMetaForeignTables(this.query)
this.functions = new PostgresMetaFunctions(this.query)
Expand Down
99 changes: 99 additions & 0 deletions src/lib/PostgresMetaDependencyGraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { filterByList } from './helpers.js'
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
import type { PostgresMetaResult, DependencyGraphNode, DependencyGraphEdge } from './types.js'
import {
DEPENDENCY_GRAPH_NODES_SQL,
DEPENDENCY_GRAPH_EDGES_SQL,
} from './sql/dependency_graph.sql.js'

/**
* Options for querying the dependency graph
*/
export interface DependencyGraphOptions {
/** Include system schemas (pg_catalog, information_schema, etc.) */
includeSystemSchemas?: boolean
/** Only include objects from these schemas */
includedSchemas?: string[]
/** Exclude objects from these schemas */
excludedSchemas?: string[]
/** Only include these object types (table, view, function, etc.) */
includedTypes?: string[]
}

/**
* Result containing nodes (database objects) and edges (dependencies)
*/
export interface DependencyGraphResult {
nodes: DependencyGraphNode[]
edges: DependencyGraphEdge[]
}

/**
* Queries PostgreSQL system catalogs to build a dependency graph of database objects.
* Supports tables, views, functions, triggers, policies, indexes, sequences, and custom types.
*/
export default class PostgresMetaDependencyGraph {
query: (sql: string) => Promise<PostgresMetaResult<unknown>>

constructor(query: (sql: string) => Promise<PostgresMetaResult<unknown>>) {
this.query = query
}

async get({
includeSystemSchemas = false,
includedSchemas,
excludedSchemas,
includedTypes,
}: DependencyGraphOptions = {}): Promise<PostgresMetaResult<DependencyGraphResult>> {
const schemaFilter = filterByList(
includedSchemas,
excludedSchemas,
!includeSystemSchemas ? DEFAULT_SYSTEM_SCHEMAS : undefined
)

// Build type filter if specified
let typeFilter: string | undefined
if (includedTypes && includedTypes.length > 0) {
const typeList = includedTypes.map((t) => `'${t}'`).join(', ')
typeFilter = `IN (${typeList})`
}

// Fetch nodes
const nodesSql = DEPENDENCY_GRAPH_NODES_SQL({ schemaFilter, typeFilter })
const nodesResult = (await this.query(nodesSql)) as PostgresMetaResult<DependencyGraphNode[]>
if (nodesResult.error) {
return { data: null, error: nodesResult.error }
}

// Fetch edges
const edgesSql = DEPENDENCY_GRAPH_EDGES_SQL({ schemaFilter })
const edgesResult = (await this.query(edgesSql)) as PostgresMetaResult<
{ id: string; source_id: number; target_id: number; type: string; label: string }[]
>
if (edgesResult.error) {
return { data: null, error: edgesResult.error }
}

// Create a set of valid node IDs for filtering edges
const nodeIds = new Set(nodesResult.data.map((n) => n.id))

// Transform edges to use node IDs and filter out edges with missing nodes
const edges: DependencyGraphEdge[] = edgesResult.data
.filter((e) => nodeIds.has(e.source_id) && nodeIds.has(e.target_id))
.map((e) => ({
id: e.id,
source: e.source_id,
target: e.target_id,
type: e.type as DependencyGraphEdge['type'],
label: e.label,
}))

return {
data: {
nodes: nodesResult.data,
edges,
},
error: null,
}
}
}
3 changes: 3 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ export {
PostgresType,
PostgresVersion,
PostgresView,
DependencyGraphNode,
DependencyGraphEdge,
DependencyGraph,
} from './types.js'
Loading