001package gudusoft.gsqlparser.dlineage.dynamicsql;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006
007/**
008 * One column-level lineage edge produced by analyzing a resolved dynamic-SQL
009 * string, carried together with its dynamic provenance.
010 *
011 * <p>{@code origin} is always {@link #ORIGIN_DYNAMIC_RESOLVED}: it is GSP's
012 * evaluation fact ("this edge exists because we evaluated the proc with the
013 * given bindings and materialized the dynamic SQL"). The downstream consumer
014 * maps it to its own persisted origin / confidence model.
015 */
016public final class DynamicLineageEdge {
017
018    public static final String ORIGIN_DYNAMIC_RESOLVED = "DYNAMIC_RESOLVED";
019
020    private final String sourceColumn;   // e.g. JOURNALENGINE.dbo.RECON_AEG_01_QSP_....grootboeknummer
021    private final String targetColumn;   // e.g. JOURNALENGINE.dbo.RECON_JE_02_QSP_....grootboeknummer
022    private final String effectType;     // dlineage effectType (select / function / ...)
023    private final String relationType;   // dlineage relation type (fdd projected data / fdr influence)
024    private final String origin;
025    private final String sourceProc;
026    private final String dynamicSite;
027    private final String bindingHash;
028    private final List<SqlFragment> valueProvenance;
029
030    DynamicLineageEdge(String sourceColumn, String targetColumn, String effectType, String relationType,
031            String sourceProc, String dynamicSite, String bindingHash) {
032        this(sourceColumn, targetColumn, effectType, relationType, sourceProc,
033                dynamicSite, bindingHash, null);
034    }
035
036    DynamicLineageEdge(String sourceColumn, String targetColumn, String effectType, String relationType,
037            String sourceProc, String dynamicSite, String bindingHash,
038            List<SqlFragment> valueProvenance) {
039        this.sourceColumn = sourceColumn;
040        this.targetColumn = targetColumn;
041        this.effectType = effectType;
042        this.relationType = relationType;
043        this.origin = ORIGIN_DYNAMIC_RESOLVED;
044        this.sourceProc = sourceProc;
045        this.dynamicSite = dynamicSite;
046        this.bindingHash = bindingHash;
047        this.valueProvenance = valueProvenance == null || valueProvenance.isEmpty()
048                ? Collections.<SqlFragment>emptyList()
049                : Collections.unmodifiableList(new ArrayList<SqlFragment>(valueProvenance));
050    }
051
052    public String getSourceColumn() {
053        return sourceColumn;
054    }
055
056    public String getTargetColumn() {
057        return targetColumn;
058    }
059
060    public String getEffectType() {
061        return effectType;
062    }
063
064    public String getRelationType() {
065        return relationType;
066    }
067
068    /** True for projected data lineage (dlineage type {@code fdd}); false for join/filter influence ({@code fdr}). */
069    public boolean isProjectedData() {
070        return "fdd".equals(relationType);
071    }
072
073    public String getOrigin() {
074        return origin;
075    }
076
077    public String getSourceProc() {
078        return sourceProc;
079    }
080
081    public String getDynamicSite() {
082        return dynamicSite;
083    }
084
085    public String getBindingHash() {
086        return bindingHash;
087    }
088
089    /** Exact materialization fragments that justify this variant edge. */
090    public List<SqlFragment> getValueProvenance() {
091        return valueProvenance;
092    }
093
094    @Override
095    public String toString() {
096        return relationType + "/" + effectType + ": " + sourceColumn + " -> " + targetColumn
097                + "  [" + origin + " @ " + dynamicSite + "]";
098    }
099}