001package gudusoft.gsqlparser.dlineage.dataflow.model; 002 003import gudusoft.gsqlparser.dlineage.util.Pair3; 004 005/** 006 * A single dynamic-SQL execution site encountered by 007 * {@link gudusoft.gsqlparser.dlineage.DataFlowAnalyzer}, together with whether the analyzer 008 * could statically resolve it. This is diagnostic / catalog-completeness information only; it 009 * is not consumed by lineage computation. 010 * 011 * <p>{@link Status} is driven by what the analyzer actually did, so it never overstates: 012 * <ul> 013 * <li>{@code RESOLVED} — the argument was a compile-time string literal that folded and parsed; 014 * lineage was attempted on fully-known text.</li> 015 * <li>{@code PARSE_ERROR} — a folded literal argument whose {@code parse()} failed.</li> 016 * <li>{@code UNRESOLVED} — the site's SQL is not fully known: a runtime variable or expression 017 * argument (even when GSP partially folds it, leaving an unresolved parameter in the text), or 018 * text GSP did not fold at all. Lineage is partial or absent. The {@link #getReason()} string 019 * distinguishes these cases.</li> 020 * </ul> 021 * 022 * <p>{@code Status} corresponds 1:1 to the internal v2 IR 023 * {@code gudusoft.gsqlparser.ir.builder.common.DynamicSqlExtraction.Status} 024 * ({@code RESOLVED / AMBIGUOUS / PARSE_ERROR}); {@code UNRESOLVED} is the public spelling of 025 * {@code AMBIGUOUS}. 026 * 027 * <p>Positions follow the GSP convention: 1-based, end-exclusive, tagged with the per-statement 028 * file hash (same shape as {@link ErrorInfo}). 029 */ 030public final class DynamicSqlSite { 031 032 /** The syntactic form of the dynamic-exec site. */ 033 public enum Kind { 034 SP_EXECUTESQL, 035 EXEC_STRING, 036 EXECUTE_IMMEDIATE, 037 OTHER 038 } 039 040 /** Whether the analyzer resolved the dynamic SQL at this site. */ 041 public enum Status { 042 RESOLVED, 043 UNRESOLVED, 044 PARSE_ERROR 045 } 046 047 private final Kind kind; 048 private final Status status; 049 private final String reason; 050 private final Pair3<Long, Long, String> startPosition; 051 private final Pair3<Long, Long, String> endPosition; 052 private final int resolvedRelationshipCount; 053 private final int partialRelationshipCount; 054 055 public DynamicSqlSite(Kind kind, Status status, String reason, 056 Pair3<Long, Long, String> startPosition, Pair3<Long, Long, String> endPosition, 057 int resolvedRelationshipCount, int partialRelationshipCount) { 058 this.kind = kind; 059 this.status = status; 060 this.reason = reason; 061 this.startPosition = startPosition; 062 this.endPosition = endPosition; 063 this.resolvedRelationshipCount = resolvedRelationshipCount; 064 this.partialRelationshipCount = partialRelationshipCount; 065 } 066 067 public Kind getKind() { 068 return kind; 069 } 070 071 public Status getStatus() { 072 return status; 073 } 074 075 /** Human-readable explanation; {@code null} for {@link Status#RESOLVED}. */ 076 public String getReason() { 077 return reason; 078 } 079 080 public Pair3<Long, Long, String> getStartPosition() { 081 return startPosition; 082 } 083 084 public Pair3<Long, Long, String> getEndPosition() { 085 return endPosition; 086 } 087 088 /** 089 * Number of lineage edges this site produced into a <b>real</b> target column (a resolved base-table 090 * column, not a T-SQL variable or placeholder). Intermediate result-set hops are not counted. 091 * {@code > 0} iff the site's dynamic SQL yielded usable lineage. 092 */ 093 public int getResolvedRelationshipCount() { 094 return resolvedRelationshipCount; 095 } 096 097 /** 098 * Number of lineage edges this site produced into an <b>unresolved</b> target — a T-SQL variable or 099 * placeholder (e.g. {@code EXEC sp_executesql @sql} folded to {@code INSERT INTO @t SELECT ...}). These 100 * are partial: a source is known but the target object is not. 101 */ 102 public int getPartialRelationshipCount() { 103 return partialRelationshipCount; 104 } 105 106 /** 107 * True iff this site produced at least one resolved lineage edge (into a real target column). 108 * A site that produced only partial edges (unresolved variable target) or nothing returns false — 109 * letting a consumer tell, per site, "folded into real lineage" from "produced nothing usable". 110 */ 111 public boolean producedLineage() { 112 return resolvedRelationshipCount > 0; 113 } 114}