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}