{"record":{"id":"b1cb65aa80e529cd","repo":"bytebase/bytebase","slug":"unsupported-query-node-type-t","errorCode":null,"errorMessage":"unsupported query node type %T","messagePattern":"unsupported query node type %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/parser/snowflake/query_span_extractor.go","lineNumber":193,"sourceCode":"\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tq.ctes = q.ctes[:originalCTECount]\n\t\t\t}()\n\t\t}\n\t\treturn q.extractPseudoTableFromSetOperation(n)\n\tcase *ast.ResultScanStmt:\n\t\t// FAIL CLOSED: a result-pipe (->>) query reads the previous statement's\n\t\t// result set, which has no resolvable schema here; resolving the trailing\n\t\t// SELECT against $1 would produce wrong lineage. Same posture as PIVOT.\n\t\treturn nil, errors.New(\"result-pipe (->>) statements are not supported for query span extraction yet\")\n\tcase *ast.ShowStmt:\n\t\t// Only reachable for SHOW ... ->> <query> (a plain SHOW classifies\n\t\t// SelectInfoSchema and returns before extraction). Same fail-closed\n\t\t// posture as ResultScanStmt: $1's schema is not resolvable.\n\t\treturn nil, errors.New(\"result-pipe (->>) statements are not supported for query span extraction yet\")\n\tdefault:\n\t\treturn nil, errors.Errorf(\"unsupported query node type %T\", node)\n\t}\n}\n\n// leftmostSelect descends a set-operation chain's left spine to the SelectStmt\n// that textually leads the statement (where omni attaches a leading WITH).\nfunc leftmostSelect(node ast.Node) *ast.SelectStmt {\n\tswitch n := node.(type) {\n\tcase *ast.SelectStmt:\n\t\treturn n\n\tcase *ast.SetOperationStmt:\n\t\treturn leftmostSelect(n.Left)\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// isRecursiveWith reports whether any CTE in the WITH list carries the RECURSIVE\n// flag. Snowflake applies RECURSIVE to the whole WITH list, so a single","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/parser/snowflake/query_span_extractor.go#L175-L211","documentation":"extractPseudoTableFromQueryNode handles only SELECT, set operations, ResultScanStmt, and ShowStmt nodes; any other AST node reaching it is rejected with errors.Errorf(\"unsupported query node type %T\"). It is a fail-closed guard in the Snowflake query span extractor: the node got classified as base.Select (so it wasn't short-circuited earlier) but the extractor cannot compute result columns for that statement shape. The ShowStmt/ResultScanStmt branches exist specifically for SHOW ... ->> query and related result-pipe statements whose $1 input schema cannot be resolved.","triggerScenarios":"Calling GetQuerySpan with a Snowflake statement that the classifier marks as a query but extractPseudoTableFromQueryNode has no case for — i.e. any node type other than *ast.SelectStmt, *ast.SetOperationStmt, *ast.ResultScanStmt, or *ast.ShowStmt (e.g. unusual command forms, PIVOT/other constructs depending on parser version, or a node type newly emitted by a parser upgrade).","commonSituations":"Feeding non-SELECT commands (CALL, INSERT ... SELECT edge shapes, EXPLAIN variants) that the type classifier still labels Select; parser version drift after an upgrade introduces a new statement type not yet handled; BUG-containing queries mixing constructs the extractor doesn't model; SHOW ... ->> (...) result-pipe queries hitting the fail-closed branch.","solutions":["Restrict GetQuerySpan input to plain SELECT statements or set operations (UNION/INTERSECT/EXCEPT) — the only fully supported shapes.","Identify the concrete node type from the %T value in the message and check whether that construct is intentionally unsupported (e.g. result-pipe ->>); rewrite the query without it.","Handle USE/SET and non-SELECT statements before calling the extractor; non-Select query types already return early and should not reach extraction.","If the node type is a legitimate SELECT-like statement, file/patch the extractor to add a case for it in extractPseudoTableFromQueryNode.","Pin the parser/omni version consistently with the extractor version to avoid new unhandled AST node types."],"exampleFix":"// before\nspan, err := extractor.GetQuerySpan(ctx, \"SHOW TABLES IN SCHEMA s ->> $1\")\n// after: run the result-pipe query outside lineage, extract span for the plain SELECT\nspan, err := extractor.GetQuerySpan(ctx, \"SELECT id, name FROM s.tbl\")","handlingStrategy":"validation","validationCode":"// Go: pre-screen statements the extractor cannot model\nfunc supportedForSpan(stmt string) bool {\n\ts := strings.ToUpper(strings.TrimSpace(stmt))\n\treturn strings.HasPrefix(s, \"SELECT\") || strings.HasPrefix(s, \"WITH\") ||\n\t\tstrings.Contains(s, \"UNION\") || strings.Contains(s, \"INTERSECT\") || strings.Contains(s, \"EXCEPT\")\n}","typeGuard":null,"tryCatchPattern":"span, err := extractor.GetQuerySpan(ctx, stmt)\nif err != nil {\n\tif strings.HasPrefix(err.Error(), \"unsupported query node type\") ||\n\t\tstrings.Contains(err.Error(), \"result-pipe (->>) statements are not supported\") {\n\t\tlog.Printf(\"statement shape unsupported for lineage: %v\", err)\n\t\treturn nil // skip lineage for this statement\n\t}\n\treturn err\n}","preventionTips":["Only send SELECT/WITH/set-operation statements to GetQuerySpan.","Rewrite or exclude SHOW ... ->> $1 result-pipe queries; their input schema is unresolvable.","Classify statements with getQueryType upstream and skip non-query types before extraction.","After parser upgrades, re-run lineage tests to catch newly introduced unhandled AST node types."],"tags":["snowflake","query-span","unsupported-statement","parser"],"backgroundTag":"unsupported-operation","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}