001package gudusoft.gsqlparser.dlineage.dynamicsql;
002
003/**
004 * Resource bounds for {@code resolveDynamicSqlLineage}.
005 *
006 * <p>The evaluated dynamic-SQL string is effectively attacker-controlled (it is
007 * built from runtime parameter values), so every dimension that an adversarial
008 * or pathological procedure could blow up is capped. When a bound is exceeded
009 * the affected site fails <em>closed</em> to an unresolved diagnostic — never a
010 * hang, never an OOM, never a fabricated edge.
011 */
012public final class DynamicLineageOptions {
013
014    private int maxResolvedSqlLength = 1_000_000;   // chars in a single materialized statement string
015    private int maxSites = 256;                     // dynamic-SQL EXEC sites resolved per call
016    private int maxBindings = 1024;                 // parameter bindings accepted per call
017    private int maxValueSetSize = 8;                // correlated constant-relation variants
018    // literal rows retained per table variable or #temp table: a configuration registry a call binds is looked up
019    // by its WHERE, so its size costs little; 64 refused a 100-row registry even when bound (Mantis 4962, spec §9)
020    private int maxConstRelationRows = 1024;
021    private int maxExpansionDepth = 256;            // function/CASE nesting depth before failing closed
022    private long timeoutMillis = 10_000;            // wall-clock budget for the whole resolve call
023    private boolean showImplicitSchema = true;      // qualify unqualified names with currentDatabase/defaultSchema
024    private boolean caseSensitiveCompare = false;   // literal relation queries compare strings case-sensitively
025    private DynamicSqlTrustMode trustMode = DynamicSqlTrustMode.LEGACY;
026
027    public static DynamicLineageOptions createDefault() {
028        return new DynamicLineageOptions();
029    }
030
031    public int getMaxResolvedSqlLength() {
032        return maxResolvedSqlLength;
033    }
034
035    public DynamicLineageOptions setMaxResolvedSqlLength(int v) {
036        this.maxResolvedSqlLength = v;
037        return this;
038    }
039
040    public int getMaxSites() {
041        return maxSites;
042    }
043
044    public DynamicLineageOptions setMaxSites(int v) {
045        this.maxSites = v;
046        return this;
047    }
048
049    public int getMaxBindings() {
050        return maxBindings;
051    }
052
053    public DynamicLineageOptions setMaxBindings(int v) {
054        this.maxBindings = v;
055        return this;
056    }
057
058    /**
059     * Maximum number of distinct statically-enumerated values (or correlated
060     * row tuples) retained by the dynamic-SQL evaluator. An attempted union or
061     * product above this bound widens to today's UNKNOWN result; it is never
062     * silently truncated.
063     */
064    public int getMaxValueSetSize() {
065        return maxValueSetSize;
066    }
067
068    public DynamicLineageOptions setMaxValueSetSize(int v) {
069        this.maxValueSetSize = v;
070        return this;
071    }
072
073    /**
074     * Maximum number of literal rows retained for one T-SQL table variable or local temporary table (default 1024);
075     * an INSERT beyond it makes the relation opaque.
076     */
077    public int getMaxConstRelationRows() {
078        return maxConstRelationRows;
079    }
080
081    public DynamicLineageOptions setMaxConstRelationRows(int v) {
082        this.maxConstRelationRows = v;
083        return this;
084    }
085
086    /** Compatibility alias for callers that use the longer relation-cap name. */
087    public int getMaxConstantRelationRows() {
088        return getMaxConstRelationRows();
089    }
090
091    public DynamicLineageOptions setMaxConstantRelationRows(int v) {
092        return setMaxConstRelationRows(v);
093    }
094
095    public int getMaxExpansionDepth() {
096        return maxExpansionDepth;
097    }
098
099    public DynamicLineageOptions setMaxExpansionDepth(int v) {
100        this.maxExpansionDepth = v;
101        return this;
102    }
103
104    public long getTimeoutMillis() {
105        return timeoutMillis;
106    }
107
108    public DynamicLineageOptions setTimeoutMillis(long v) {
109        this.timeoutMillis = v;
110        return this;
111    }
112
113    public boolean isShowImplicitSchema() {
114        return showImplicitSchema;
115    }
116
117    public DynamicLineageOptions setShowImplicitSchema(boolean v) {
118        this.showImplicitSchema = v;
119        return this;
120    }
121
122    /**
123     * Whether a query over a literal relation (a table variable or temporary table the procedure fills with literal
124     * rows) compares strings case-sensitively. Default false: SQL Server's default {@code _CI_AS} collations compare
125     * case-insensitively; trailing spaces are ignored either way (Mantis 4962).
126     */
127    public boolean isCaseSensitiveCompare() {
128        return caseSensitiveCompare;
129    }
130
131    public DynamicLineageOptions setCaseSensitiveCompare(boolean v) {
132        this.caseSensitiveCompare = v;
133        return this;
134    }
135
136    /**
137     * Controls observation of edges from a materialized string that still has
138     * unresolved fragments. Both modes preserve publication; SHADOW adds
139     * diagnostics. The default is {@link DynamicSqlTrustMode#LEGACY} for
140     * backwards compatibility.
141     */
142    public DynamicSqlTrustMode getTrustMode() {
143        return trustMode;
144    }
145
146    public DynamicLineageOptions setTrustMode(DynamicSqlTrustMode v) {
147        this.trustMode = v == null ? DynamicSqlTrustMode.LEGACY : v;
148        return this;
149    }
150}