Summary
For some bracket-quoted T-SQL, a FOREIGN KEY ... REFERENCES clause causes the child table to be
dropped from the graph entirely and produces a self-referencing edge on the parent, tagged
EXTRACTED.
EXTRACTED means "explicit in the source". There is no Customer → Customer reference anywhere in
the input. So this is not a missing edge — it is a fabricated edge carrying the highest confidence
grade, which is worse, because the grade is what a reader uses to decide whether to trust it
without checking.
The identical input with brackets removed is extracted correctly.
Repro
s.sql, alone in a directory:
CREATE TABLE [dbo].[Customer] (
[CustomerId] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(100) NULL
);
GO
CREATE TABLE [dbo].[Invoice] (
[InvoiceId] INT NOT NULL PRIMARY KEY,
[CustomerId] INT NOT NULL,
CONSTRAINT [FK_Invoice_Customer] FOREIGN KEY ([CustomerId])
REFERENCES [dbo].[Customer] ([CustomerId])
);
GO
graphify . --code-only --force
Actual
labels: ['s.sql', 'dbo].[Customer']
edges : [('s', 'contains', 's_dbo_customer', 'EXTRACTED'),
('s_dbo_customer', 'references', 's_dbo_customer', 'EXTRACTED')]
[dbo].[Invoice] is absent from the graph. The FK it declares appears as Customer → Customer.
Expected
The same file with brackets removed gives the right answer, so this is the behaviour to match:
labels: ['s.sql', 'dbo.Customer', 'dbo.Invoice', ...]
edges : [..., ('s_dbo_invoice', 'references', 's_dbo_customer', 'EXTRACTED')]
What I could and could not isolate
Reproduces deterministically, including with --force in a fresh directory, so it is not an
incremental-cache artefact.
I could not isolate the trigger. Four plausible causes were each tested and refuted — nearby
variants all extract correctly:
| Hypothesis |
Test |
Result |
| Objects after the first are dropped |
three bracketed CREATE TABLE, no FK |
all three extracted — refuted |
| A bracketed FK breaks extraction generally |
[dbo].[Beta] FK → [dbo].[Alpha] |
correct edge — refuted |
| FK column name matching the referenced column name |
AlphaId → AlphaId vs AlphaId → Id |
both correct — refuted |
A parameterised column type (NVARCHAR(100)) breaks statement boundaries |
with and without |
both correct — refuted |
So the minimal failing case above is the smallest input I have that fails, and structurally similar
inputs succeed. I did not want to guess further at a mechanism in someone else's parser — the repro
is deterministic, so hopefully it is quicker to bisect from inside.
Why this matters for schema corpora
The stated value of the SQL path is that declared foreign keys become high-confidence edges, and
that is exactly the case failing here. A schema with many FKs is the corpus where the tool should be
strongest; instead some tables vanish silently and some relationships are replaced by self-loops
that look authoritative. Both failures are invisible in the build log — the run exits 0 and reports
a node and edge count that looks reasonable.
For scale, the schema we are indexing declares ~1,365 foreign keys.
Environment
- graphify 0.9.39,
tree_sitter_sql present (confirmed importable)
- Python 3.14.2, Windows 11
--code-only, so this is the local AST path with no LLM involved
Related
The bracket-quoted identifier label mangling (dbo].[Customer) is filed separately. It is visible
in the output above but does not appear to share a trigger with this — every bracketed case mangles
labels, only some drop nodes.
Summary
For some bracket-quoted T-SQL, a
FOREIGN KEY ... REFERENCESclause causes the child table to bedropped from the graph entirely and produces a self-referencing edge on the parent, tagged
EXTRACTED.EXTRACTEDmeans "explicit in the source". There is noCustomer → Customerreference anywhere inthe input. So this is not a missing edge — it is a fabricated edge carrying the highest confidence
grade, which is worse, because the grade is what a reader uses to decide whether to trust it
without checking.
The identical input with brackets removed is extracted correctly.
Repro
s.sql, alone in a directory:CREATE TABLE [dbo].[Customer] ( [CustomerId] INT NOT NULL PRIMARY KEY, [Name] NVARCHAR(100) NULL ); GO CREATE TABLE [dbo].[Invoice] ( [InvoiceId] INT NOT NULL PRIMARY KEY, [CustomerId] INT NOT NULL, CONSTRAINT [FK_Invoice_Customer] FOREIGN KEY ([CustomerId]) REFERENCES [dbo].[Customer] ([CustomerId]) ); GOActual
[dbo].[Invoice]is absent from the graph. The FK it declares appears asCustomer → Customer.Expected
The same file with brackets removed gives the right answer, so this is the behaviour to match:
What I could and could not isolate
Reproduces deterministically, including with
--forcein a fresh directory, so it is not anincremental-cache artefact.
I could not isolate the trigger. Four plausible causes were each tested and refuted — nearby
variants all extract correctly:
CREATE TABLE, no FK[dbo].[Beta]FK →[dbo].[Alpha]AlphaId→AlphaIdvsAlphaId→IdNVARCHAR(100)) breaks statement boundariesSo the minimal failing case above is the smallest input I have that fails, and structurally similar
inputs succeed. I did not want to guess further at a mechanism in someone else's parser — the repro
is deterministic, so hopefully it is quicker to bisect from inside.
Why this matters for schema corpora
The stated value of the SQL path is that declared foreign keys become high-confidence edges, and
that is exactly the case failing here. A schema with many FKs is the corpus where the tool should be
strongest; instead some tables vanish silently and some relationships are replaced by self-loops
that look authoritative. Both failures are invisible in the build log — the run exits 0 and reports
a node and edge count that looks reasonable.
For scale, the schema we are indexing declares ~1,365 foreign keys.
Environment
tree_sitter_sqlpresent (confirmed importable)--code-only, so this is the local AST path with no LLM involvedRelated
The bracket-quoted identifier label mangling (
dbo].[Customer) is filed separately. It is visiblein the output above but does not appear to share a trigger with this — every bracketed case mangles
labels, only some drop nodes.