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}