Skip to content

feat: build a directed graph out of schema#2

Merged
viveknathani merged 4 commits intomasterfrom
feat/graph
Jan 8, 2026
Merged

feat: build a directed graph out of schema#2
viveknathani merged 4 commits intomasterfrom
feat/graph

Conversation

@viveknathani
Copy link
Copy Markdown
Owner

Description

  • Implemented Build function to construct schema graph from database metadata
  • Added tests

Testing:
go test ./graph -v

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Adds a DatabaseName field to SchemaGraph and implements Build(db *database.Database) that populates Nodes from db.Tables, constructs Edges from foreign-key constraints (only when referenced tables exist), and returns a populated SchemaGraph. (<=50 words)

Changes

Cohort / File(s) Summary
Graph implementation
graph/graph.go
Added DatabaseName string to SchemaGraph. Changed function signature to Build(db *database.Database) and implemented two-pass construction: first populate Nodes from db.Tables, then create Edges for foreign-key constraints only if the referenced table exists; returns populated SchemaGraph.
Graph tests
graph/graph_test.go
New unit tests (TestBuild) covering empty DB, single-table, and multi-table (foreign-key) scenarios; assert DatabaseName, per-node contents, and edge construction.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 I hopped through rows and named the land,
Linked tiny keys with a careful hand,
Nodes in a line, edges in a chain,
I stored the database—forgot my grain,
A rabbit's graph, tidy and grand 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing the Build function to construct a directed graph from database schema metadata.
Description check ✅ Passed The description is related to the changeset, clearly stating that the Build function was implemented and tests were added.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cbd942 and 1d5160e.

📒 Files selected for processing (1)
  • graph/graph.go
🧰 Additional context used
🧬 Code graph analysis (1)
graph/graph.go (1)
database/database.go (3)
  • Table (43-47)
  • Database (50-53)
  • ForeignKey (19-19)
🔇 Additional comments (5)
graph/graph.go (5)

14-18: Well-structured schema graph representation.

The use of a map for Nodes enables efficient O(1) lookups when validating foreign key references, and the separation of concerns between nodes and edges is clear.


20-23: Good defensive programming with nil check.

The nil guard prevents potential panics and establishes a clear contract for the function.


28-32: First pass correctly populates the nodes map.

The use of index-based addressing (&db.Tables[i]) properly avoids Go's loop variable capture issue while building the node references.


34-51: Verify the silent handling of invalid foreign key references.

The implementation correctly checks whether the referenced table exists before creating an edge (line 40), which prevents invalid edges. However, the silent skipping when a referenced table doesn't exist in the graph may hide data quality issues. Consider:

  1. Is this intentional behavior for handling partial schemas or circular dependencies across databases?
  2. Should there be logging or a returned error/warning list for skipped constraints?
  3. What happens if constraint.ReferenceTable is an empty string, or if constraint.Columns/constraint.ReferenceColumns are empty?

The current implementation is safe but potentially opaque. If silent skipping is intentional, consider documenting this behavior in a comment.


53-57: Clean construction and return of the schema graph.

All fields are properly populated from the constructed nodes and edges.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @graph/graph.go:
- Line 22: The edges slice is declared as a nil slice with "var edges
[]ForeignKeyEdge" which fails DeepEqual-based tests; replace its initialization
with a non-nil empty slice (e.g., use "edges := []ForeignKeyEdge{}" or "edges :=
make([]ForeignKeyEdge, 0)") so the variable edges (of type []ForeignKeyEdge) is
an empty but non-nil slice matching test expectations.
🧹 Nitpick comments (3)
graph/graph.go (2)

20-20: Add nil check for db parameter.

The function doesn't validate that db is non-nil, which could cause a panic at line 24.

🛡️ Proposed fix
 func Build(db *database.Database) *SchemaGraph {
+	if db == nil {
+		return &SchemaGraph{
+			Nodes: make(map[TableName]*database.Table),
+			Edges: []ForeignKeyEdge{},
+		}
+	}
 	nodes := make(map[TableName]*database.Table)
 	var edges []ForeignKeyEdge

28-38: Consider adding validation for foreign key references for defensive programming.

The function creates edges without verifying that constraint.ReferenceTable exists in the nodes map. While the current data flow (PostgreSQL InspectSchema) guarantees all ReferenceTable values point to existing tables due to database-enforced referential integrity, adding explicit validation would make this assumption clear and protect against issues if Build is called with incomplete or manually constructed Database objects.

Consider adding a check to skip or log dangling references:

if _, exists := nodes[TableName(constraint.ReferenceTable)]; !exists {
    // Handle dangling reference: skip, log warning, or error
    continue
}

This would also make the code more resilient if the graph package is used in other contexts beyond its current unused state.

graph/graph_test.go (1)

10-161: Good test coverage for core scenarios.

The tests comprehensively validate the Build function with three key scenarios: empty database, single table, and foreign key relationships. The assertions properly check DatabaseName, Nodes, and Edges.

Consider adding test cases for edge scenarios:

  • Multiple foreign keys from the same table
  • Self-referencing foreign key (table referencing itself)
  • Composite foreign keys (multi-column)
  • Foreign key referencing non-existent table (if this should be handled)
📋 Example test case for multiple foreign keys
{
	name: "table with multiple foreign keys",
	db: &database.Database{
		Name: "test_db",
		Tables: []database.Table{
			{
				Name: "users",
				Columns: []database.Column{
					{Name: "id", Type: "integer", IsNullable: false},
				},
				Constraints: []database.Constraint{
					{Kind: database.PrimaryKey, Columns: []string{"id"}},
				},
			},
			{
				Name: "addresses",
				Columns: []database.Column{
					{Name: "id", Type: "integer", IsNullable: false},
				},
				Constraints: []database.Constraint{
					{Kind: database.PrimaryKey, Columns: []string{"id"}},
				},
			},
			{
				Name: "orders",
				Columns: []database.Column{
					{Name: "id", Type: "integer", IsNullable: false},
					{Name: "user_id", Type: "integer", IsNullable: false},
					{Name: "shipping_address_id", Type: "integer", IsNullable: false},
				},
				Constraints: []database.Constraint{
					{Kind: database.PrimaryKey, Columns: []string{"id"}},
					{
						Kind:             database.ForeignKey,
						Columns:          []string{"user_id"},
						ReferenceTable:   "users",
						ReferenceColumns: []string{"id"},
					},
					{
						Kind:             database.ForeignKey,
						Columns:          []string{"shipping_address_id"},
						ReferenceTable:   "addresses",
						ReferenceColumns: []string{"id"},
					},
				},
			},
		},
	},
	expected: &SchemaGraph{
		DatabaseName: "test_db",
		Nodes: map[TableName]*database.Table{
			// ... nodes mapping
		},
		Edges: []ForeignKeyEdge{
			{
				FromTable:        "orders",
				ToTable:          "users",
				Columns:          []string{"user_id"},
				ReferenceColumns: []string{"id"},
			},
			{
				FromTable:        "orders",
				ToTable:          "addresses",
				Columns:          []string{"shipping_address_id"},
				ReferenceColumns: []string{"id"},
			},
		},
	},
},
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f115659 and 7fcbdfc.

📒 Files selected for processing (2)
  • graph/graph.go
  • graph/graph_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
graph/graph.go (1)
database/database.go (3)
  • Table (43-47)
  • Database (50-53)
  • ForeignKey (19-19)
🔇 Additional comments (1)
graph/graph.go (1)

24-26: LGTM!

Taking the address of the slice element correctly avoids copying table data while ensuring the pointer remains valid.

Comment thread graph/graph.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @graph/graph.go:
- Around line 28-38: The foreign-key edge creation loop (iterating
table.Constraints and building ForeignKeyEdge using constraint.ReferenceTable)
can produce dangling edges when referenced tables are in other schemas; before
appending the edge to edges, verify the referenced table exists in your
nodes/tables map (the same map populated by getTables())—e.g., resolve
TableName(constraint.ReferenceTable) against the nodes map or db.Tables and skip
the constraint if the target is missing; alternatively, update
getTables()/getAllForeignKeys() to include cross-schema references consistently,
but do not append edges for referenceTable values that are not present in the
nodes map.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fcbdfc and 9205610.

📒 Files selected for processing (1)
  • graph/graph.go
🧰 Additional context used
🧬 Code graph analysis (1)
graph/graph.go (1)
database/database.go (3)
  • Table (43-47)
  • Database (50-53)
  • ForeignKey (19-19)
🔇 Additional comments (2)
graph/graph.go (2)

15-17: Well-designed schema graph structure.

The SchemaGraph struct provides a clean representation with efficient lookup via the Nodes map and clear foreign key relationships in Edges. The DatabaseName field adds useful context for tracking the source database.


24-26: LGTM! Correct pointer handling.

Using &db.Tables[i] to obtain a pointer to the slice element is the correct approach to avoid copying the Table struct while ensuring a stable reference.

Comment thread graph/graph.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @graph/graph.go:
- Around line 20-49: Build currently adds edges only if the referenced table was
already seen, causing order-dependent missing edges and it also dereferences db
without a nil check; update Build to first check for nil db, then perform a
two-pass approach: first iterate db.Tables to populate the nodes map (use
TableName(table.Name) as before), then in a second loop iterate all tables and
their Constraints and for each database.ForeignKey create a ForeignKeyEdge
(FromTable, ToTable using TableName(constraint.ReferenceTable), Columns,
ReferenceColumns) and append it to edges — this ensures edges are created
regardless of table order and uses the fully populated nodes map to optionally
verify referenced tables exist.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9205610 and 3cbd942.

📒 Files selected for processing (1)
  • graph/graph.go
🧰 Additional context used
🧬 Code graph analysis (1)
graph/graph.go (2)
database/database.go (5)
  • Table (43-47)
  • Database (50-53)
  • ForeignKey (19-19)
  • InspectSchema (56-58)
  • InspectSchema (84-99)
database/database_test.go (1)
  • TestInspectSchema (14-119)
🔇 Additional comments (1)
graph/graph.go (1)

14-18: LGTM! Well-structured schema graph representation.

The field additions are appropriate:

  • DatabaseName tracks the database context
  • Nodes as a map enables efficient table lookups
  • Edges as a slice provides a straightforward representation of foreign key relationships

Comment thread graph/graph.go
@viveknathani viveknathani merged commit dc50e98 into master Jan 8, 2026
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Jan 10, 2026
@viveknathani viveknathani deleted the feat/graph branch January 10, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant