Version / build tested against
v0.5.0 (live repro against the released image). Code re-verified unchanged at origin/main @ 461c3adb — neither graph_parse/helpers.rs nor graph_parse/variants.rs was touched between the tag and that head.
Deployment mode
Origin — single node (local)
Engine(s) involved
Graph (overlay)
Summary
The graph DSL does not validate two of GRAPH TRAVERSE's arguments, in opposite directions. An unrecognized DIRECTION token is accepted silently and falls through to out, so a traversal returns a confidently wrong edge set with no error. Conversely, omitting the IN <collection> clause that 0.5.0 made mandatory aborts the whole graph parse, so the statement falls through to the general SQL parser and the user is told Expected: an SQL statement, found: GRAPH rather than which clause is missing. Both are argument-handling defects in the same module, and the first produces silently-wrong results.
Steps to reproduce
CREATE COLLECTION g (id TEXT PRIMARY KEY);
GRAPH INSERT EDGE IN g FROM 'a' TO 'b' TYPE 'knows' PROPERTIES '{}';
-- One directed edge a -> b. Traversing FROM 'b', only an inbound walk
-- can reach 'a'. That makes the direction argument observable.
GRAPH TRAVERSE IN g FROM 'b' DEPTH 1 DIRECTION IN;
-- reaches 'a' -- correct
GRAPH TRAVERSE IN g FROM 'b' DEPTH 1 DIRECTION INBOUND;
-- does NOT reach 'a', no error <- WRONG, silently treated as `out`
GRAPH TRAVERSE IN g FROM 'b' DEPTH 1 DIRECTION BANANA;
-- does NOT reach 'a', no error <- WRONG, arbitrary token accepted
-- Second symptom: omit the required IN clause.
GRAPH TRAVERSE FROM 'b' DEPTH 1;
-- ERROR 42601: parse error: sql parser error:
-- Expected: an SQL statement, found: GRAPH at Line: 1, Column: 1
Expected behavior
- A
DIRECTION token outside in / out / both is rejected with a parse error naming the offending value. A traversal never silently substitutes a direction the caller did not ask for.
- Omitting a required clause reports the missing clause — e.g.
GRAPH TRAVERSE requires IN <collection> — rather than a message stating the input is not an SQL statement at all.
Actual behavior
1. Unrecognized DIRECTION is silently coerced to out.
graph_parse/helpers.rs#L104-L114:
pub(super) fn direction_after(toks: &[Tok<'_>]) -> GraphDirection {
match word_after(toks, "DIRECTION")
.as_deref()
.map(str::to_ascii_uppercase)
.as_deref()
{
Some("IN") => GraphDirection::In,
Some("BOTH") => GraphDirection::Both,
_ => GraphDirection::Out,
}
}
The _ arm absorbs every unrecognized token along with the absent-clause case, and the function returns GraphDirection rather than Option/Result, so a caller cannot distinguish "defaulted" from "garbage". The return is used unvalidated by parse_traverse, parse_neighbors and parse_path
(graph_parse/variants.rs#L52-L90).
Defaulting an absent clause to out is reasonable and is not what this reports; accepting DIRECTION BANANA as out is. The measured table from the repro above:
| clause |
reaches a |
DIRECTION IN |
yes — correct |
DIRECTION INBOUND |
no — silently out |
DIRECTION BANANA |
no — silently out |
| (clause omitted) |
no — documented default |
2. A missing required IN <collection> is reported as "not an SQL statement".
graph_parse/variants.rs#L52-L57:
pub(super) fn parse_traverse(toks: &[Tok<'_>]) -> Option<NodedbStatement> {
let collection = quoted_after(toks, "IN")?;
The ? returns None, try_parse therefore reports no graph statement, and the input falls through to the general SQL parser, which cannot know GRAPH was ever valid. Since IN <collection> became mandatory in 0.5.0, this is the error every caller upgrading from 0.4.0 meets first, and it points away from the actual cause. parse_neighbors and parse_path share the shape.
Same class as two previously-closed reports, in a different handler and still present at 461c3adb: #89 (DSL handlers text-parsing their arguments and silently coercing) and #213 (CREATE VECTOR INDEX ignoring unrecognized option tokens).
What actually happened? (check all that are true)
Proposed severity
SEV-3 — Medium: feature wrong, but operational and a workaround exists
Valid tokens behave correctly and the workaround is to spell the token in the server's own vocabulary, which is why this is proposed as SEV-3 rather than SEV-2. The counter-argument is on the record for triage: the failure mode is silently-wrong rows rather than an error, which the severity dropdown lists under SEV-2.
Reproducibility
Always — every attempt
Last known-good version / commit (if a regression)
Not a regression — direction_after behaves identically at v0.4.0. The second symptom is new in v0.5.0 only because IN <collection> became required there.
Environment & logs
Linux x86_64, Docker, released image farhansyah/nodedb:0.5.0, single node, default configuration apart from a loopback NODEDB_HOST. Observed over pgwire. No server-side log line is emitted for either case.
Before submitting
Version / build tested against
v0.5.0(live repro against the released image). Code re-verified unchanged atorigin/main @ 461c3adb— neithergraph_parse/helpers.rsnorgraph_parse/variants.rswas touched between the tag and that head.Deployment mode
Origin — single node (local)
Engine(s) involved
Graph (overlay)
Summary
The graph DSL does not validate two of
GRAPH TRAVERSE's arguments, in opposite directions. An unrecognizedDIRECTIONtoken is accepted silently and falls through toout, so a traversal returns a confidently wrong edge set with no error. Conversely, omitting theIN <collection>clause that 0.5.0 made mandatory aborts the whole graph parse, so the statement falls through to the general SQL parser and the user is toldExpected: an SQL statement, found: GRAPHrather than which clause is missing. Both are argument-handling defects in the same module, and the first produces silently-wrong results.Steps to reproduce
Expected behavior
DIRECTIONtoken outsidein/out/bothis rejected with a parse error naming the offending value. A traversal never silently substitutes a direction the caller did not ask for.GRAPH TRAVERSE requires IN <collection>— rather than a message stating the input is not an SQL statement at all.Actual behavior
1. Unrecognized
DIRECTIONis silently coerced toout.graph_parse/helpers.rs#L104-L114:The
_arm absorbs every unrecognized token along with the absent-clause case, and the function returnsGraphDirectionrather thanOption/Result, so a caller cannot distinguish "defaulted" from "garbage". The return is used unvalidated byparse_traverse,parse_neighborsandparse_path(
graph_parse/variants.rs#L52-L90).Defaulting an absent clause to
outis reasonable and is not what this reports; acceptingDIRECTION BANANAasoutis. The measured table from the repro above:aDIRECTION INDIRECTION INBOUNDoutDIRECTION BANANAout2. A missing required
IN <collection>is reported as "not an SQL statement".graph_parse/variants.rs#L52-L57:The
?returnsNone,try_parsetherefore reports no graph statement, and the input falls through to the general SQL parser, which cannot knowGRAPHwas ever valid. SinceIN <collection>became mandatory in 0.5.0, this is the error every caller upgrading from 0.4.0 meets first, and it points away from the actual cause.parse_neighborsandparse_pathshare the shape.Same class as two previously-closed reports, in a different handler and still present at
461c3adb: #89 (DSL handlers text-parsing their arguments and silently coercing) and #213 (CREATE VECTOR INDEXignoring unrecognized option tokens).What actually happened? (check all that are true)
Proposed severity
SEV-3 — Medium: feature wrong, but operational and a workaround exists
Valid tokens behave correctly and the workaround is to spell the token in the server's own vocabulary, which is why this is proposed as SEV-3 rather than SEV-2. The counter-argument is on the record for triage: the failure mode is silently-wrong rows rather than an error, which the severity dropdown lists under SEV-2.
Reproducibility
Always — every attempt
Last known-good version / commit (if a regression)
Not a regression —
direction_afterbehaves identically atv0.4.0. The second symptom is new inv0.5.0only becauseIN <collection>became required there.Environment & logs
Linux x86_64, Docker, released image
farhansyah/nodedb:0.5.0, single node, default configuration apart from a loopbackNODEDB_HOST. Observed over pgwire. No server-side log line is emitted for either case.Before submitting
mainbuild (not a stale local branch).