001package gudusoft.gsqlparser.dlineage.dataflow.model;
002
003import java.util.Arrays;
004import java.util.HashSet;
005import java.util.Set;
006
007import gudusoft.gsqlparser.EDbVendor;
008import gudusoft.gsqlparser.dlineage.dataflow.listener.DataFlowHandleListener;
009import gudusoft.gsqlparser.dlineage.dynamicsql.DynamicSqlTrustMode;
010
011public class Option implements Cloneable {
012
013    private EDbVendor vendor;
014    private EDbVendor powerQueryInnerVendor;
015    private DataFlowHandleListener handleListener;
016    private boolean simpleOutput;
017    private boolean textFormat = false;
018    private boolean showJoin = false;
019    private boolean ignoreRecordSet = false;
020    private boolean ignoreTemporaryTable = false;
021    private boolean ignoreTopSelect = false;
022    private boolean ignoreUnusedSynonym = true;
023    private boolean showCallRelation = false;
024    private boolean simpleShowFunction = false;
025    private boolean simpleShowSynonym = false;
026    private boolean simpleShowUdfFunctionOnly = false;
027    private boolean simpleShowCursor = false;
028    private boolean simpleShowVariable = false;
029    private boolean simpleRetainIntermediate = false;
030    private boolean simpleShowTopSelectResultSet = false;
031    private boolean linkOrphanColumnToFirstTable = false;
032    private boolean ignoreCoordinate = false;
033    private boolean transform = false;
034    private boolean transformCoordinate = false;
035    /**
036     * Whether SQL Server parsers created by {@code DataFlowAnalyzer} accept
037     * colon-prefixed bind variables such as {@code :name}, {@code :1}, and
038     * {@code :@name}. This compatibility extension is disabled by default.
039     */
040    private boolean enableMssqlColonBindVariables = false;
041    /**
042     * Whether {@code DataFlowAnalyzer} should collect the immutable semantic
043     * sidecar consumed by the SLPC adapter.
044     *
045     * <p>This is deliberately opt-in. The established dlineage XML/JSON model
046     * remains the default execution path, with no AST traversal or sidecar
047     * bookkeeping added for callers that do not request SLPC projection.
048     * Parallel analysis currently forces this option off because merged legacy
049     * IDs are remapped and cannot yet be correlated safely to worker evidence.</p>
050     */
051    private boolean collectAuthoritativeLineageEvidence = false;
052    private boolean showImplicitSchema = false;
053    private boolean showCountTableColumn = true;
054    private boolean showConstantTable = false;
055    private boolean showCaseWhenAsDirect = true;
056    private String filePathDatabase;
057    private String filePathSchema;
058    private long startId = 0;
059    private boolean output = true;
060    private boolean traceSQL = false;
061    private boolean traceProcedure = false;
062    private int parallel = Runtime.getRuntime().availableProcessors() / 2 - 1;
063    private String defaultServer;
064    private String defaultDatabase;
065    private String defaultSchema;
066    private boolean showERDiagram = false;
067    private Set<ResultSetType> targetResultSetTypes = new HashSet<ResultSetType>(); 
068    private Set<String> filterRelationTypes = new HashSet<String>();
069    private Set<String> simpleShowRelationTypes = new HashSet<String>();
070    private boolean sqlflowIgnoreFunction = false;
071    private DynamicSqlTrustMode dynamicSqlTrustMode = DynamicSqlTrustMode.LEGACY;
072    /** Whether execution statements may analyze embedded/generated SQL text.
073     *  Defaults true for compatibility. Routine-summary extraction disables
074     *  it so dynamic relationships cannot lose their site provenance when
075     *  collapsed into an ordinary base-summary edge. */
076    private boolean analyzeDynamicSql = true;
077
078    private Set<String> excludedProcedureNames = new HashSet<String>();
079
080    /**
081     * Wildcard patterns to exclude procedures from analysis.
082     * <p>
083     * Supports the following wildcard characters:
084     * <ul>
085     *   <li><code>*</code> - Matches any sequence of characters (e.g., <code>SCHEMA1.*</code> matches all procedures in SCHEMA1)</li>
086     *   <li><code>?</code> - Matches any single character (e.g., <code>SCHEMA?.PROC?</code> matches SCHEMA1.PROC1, SCHEMA2.PROC2, etc.)</li>
087     *   <li><code>.</code> - Reserved as schema/procedure name separator (e.g., <code>SCHEMA.PROC</code> matches a two-segment qualified name)</li>
088     * </ul>
089     * <p>
090     * Examples:
091     * <ul>
092     *   <li><code>"SCHEMA1.*"</code> - Excludes all procedures in SCHEMA1</li>
093     *   <li><code>"*.SP_*"</code> - Excludes all procedures starting with SP_ in any schema</li>
094     *   <li><code>"*_TEST"</code> - Excludes all procedures ending with _TEST in any schema</li>
095     *   <li><code>"SCHEMA?.PROC?"</code> - Excludes procedures like SCHEMA1.PROC1, SCHEMA2.PROC2, etc.</li>
096     *   <li><code>"DB.SCHEMA.PROC"</code> - Excludes a specific three-segment procedure</li>
097     * </ul>
098     * <p>
099     * Note: Pattern matching is case-insensitive. Quoted identifiers (double quotes, square brackets, backticks, single quotes)
100     * will be automatically stripped before matching.
101     */
102    private Set<String> excludedProcedurePatterns = new HashSet<String>();
103    private boolean showCandidateTable = true;
104    private boolean ignoreInsertIntoValues = true;
105    private boolean normalizeOutput = false;
106    /*
107     * Whether to trace all table positions. Default is false.
108     */
109    private boolean traceTablePosition = false;
110
111    private boolean enablePipelinedStitching = true;
112
113    /**
114     * B3 (routine-summary-scc-design §3): object-level per-overload variable
115     * pools — the identity-first createVariable path. OFF by default: LEGACY
116     * keeps the historical shared-key merge of same-named overload formals;
117     * only the SHADOW/ENHANCED pipelines switch this on. Folds into
118     * ProceduralLineageOptions in B6.
119     */
120    private boolean identityFirstVariablePools = false;
121    private int maxPipelinedExpansionDepth = 8;
122    private int maxStitchedSourcesPerColumn = 64;
123
124    private AnalyzeMode analyzeMode;
125
126    public EDbVendor getVendor() {
127        return vendor;
128    }
129
130    public void setVendor(EDbVendor vendor) {
131        this.vendor = vendor;
132    }
133
134    /**
135     * Explicit SQL dialect for the inner SQL embedded in Power Query
136     * {@code Value.NativeQuery()} calls and for navigation-chain synthetic
137     * SELECTs. When set, this takes precedence over the connector-based
138     * inference performed by {@code TPowerQueryAnalyzer}; when {@code null}
139     * (default), inference is used.
140     */
141    public EDbVendor getPowerQueryInnerVendor() {
142        return powerQueryInnerVendor;
143    }
144
145    public void setPowerQueryInnerVendor(EDbVendor powerQueryInnerVendor) {
146        this.powerQueryInnerVendor = powerQueryInnerVendor;
147    }
148
149    public DataFlowHandleListener getHandleListener() {
150        return handleListener;
151    }
152
153    public void setHandleListener(DataFlowHandleListener handleListener) {
154        this.handleListener = handleListener;
155    }
156
157    public boolean isSimpleOutput() {
158        return simpleOutput;
159    }
160
161    public void setSimpleOutput(boolean simpleOutput) {
162        this.simpleOutput = simpleOutput;
163    }
164
165    public boolean isTextFormat() {
166        return textFormat;
167    }
168
169    public void setTextFormat(boolean textFormat) {
170        this.textFormat = textFormat;
171    }
172
173    public boolean isShowJoin() {
174        return showJoin;
175    }
176
177    public void setShowJoin(boolean showJoin) {
178        this.showJoin = showJoin;
179    }
180
181    /**
182     * When true, every non-RESOLVED dynamic-SQL site (including SHADOW's PARTIAL sites) is also
183     * mirrored into an {@link ErrorInfo} (type {@link ErrorInfo#DYNAMIC_SQL_UNRESOLVED}) so callers reading
184     * {@code getErrorMessages()} can see them. Default false, so existing callers are unaffected;
185     * the structured list is always available via {@code DataFlowAnalyzer.getDynamicSqlSites()}.
186     */
187    private boolean reportDynamicSqlSitesAsErrors = false;
188
189    public boolean isReportDynamicSqlSitesAsErrors() {
190        return reportDynamicSqlSitesAsErrors;
191    }
192
193    public void setReportDynamicSqlSitesAsErrors(boolean reportDynamicSqlSitesAsErrors) {
194        this.reportDynamicSqlSitesAsErrors = reportDynamicSqlSitesAsErrors;
195    }
196
197    /**
198     * Observation policy for incompletely materialized dynamic SQL. The default
199     * {@link DynamicSqlTrustMode#LEGACY} retains historical output. SHADOW keeps
200     * the exact same model and adds incompleteness diagnostics.
201     */
202    public DynamicSqlTrustMode getDynamicSqlTrustMode() {
203        return dynamicSqlTrustMode;
204    }
205
206    public void setDynamicSqlTrustMode(DynamicSqlTrustMode dynamicSqlTrustMode) {
207        this.dynamicSqlTrustMode = dynamicSqlTrustMode == null
208                ? DynamicSqlTrustMode.LEGACY : dynamicSqlTrustMode;
209    }
210
211    /**
212     * Controls inner analysis of dynamic SQL text. When false, ordinary
213     * static statements and static routine calls are still analyzed, but
214     * EXEC-string, sp_executesql, EXECUTE IMMEDIATE, DBMS_SQL.PARSE, and
215     * similar embedded-text expansions publish no relationships.
216     */
217    public boolean isAnalyzeDynamicSql() {
218        return analyzeDynamicSql;
219    }
220
221    public void setAnalyzeDynamicSql(boolean analyzeDynamicSql) {
222        this.analyzeDynamicSql = analyzeDynamicSql;
223    }
224
225    public boolean isIgnoreRecordSet() {
226        return ignoreRecordSet;
227    }
228
229    public void setIgnoreRecordSet(boolean ignoreRecordSet) {
230        this.ignoreRecordSet = ignoreRecordSet;
231    }
232
233    public boolean isLinkOrphanColumnToFirstTable() {
234        return linkOrphanColumnToFirstTable;
235    }
236
237    public void setLinkOrphanColumnToFirstTable(boolean linkOrphanColumnToFirstTable) {
238        this.linkOrphanColumnToFirstTable = linkOrphanColumnToFirstTable;
239    }
240
241    /**
242     * When true, dlineage ASSUMES that an external script
243     * ({@code sp_execute_external_script}) passes its input dataset through to the
244     * declared {@code WITH RESULT SETS} output positionally, emitting
245     * {@link gudusoft.gsqlparser.dlineage.dataflow.model.EffectType#external_script_passthrough}
246     * edges from each @input_data_1 column to the corresponding output column.
247     *
248     * <p>The script body (Python/R/…) is opaque and may reorder, drop or
249     * synthesize columns, so these edges are an assumption, not proven lineage —
250     * hence the dedicated effect type. Default {@code true}: column-level lineage
251     * flows THROUGH the script (e.g. across an {@code INSERT ... EXEC} that
252     * consumes the procedure's result set), with every assumed edge marked
253     * {@code external_script_passthrough} so it stays distinguishable from proven
254     * lineage. Set to {@code false} to suppress the assumption: the input query's
255     * source lineage is still emitted, but it stops at the script boundary (it is
256     * not connected to the output columns).
257     */
258    private boolean assumeExternalScriptPassthrough = true;
259
260    public boolean isAssumeExternalScriptPassthrough() {
261        return assumeExternalScriptPassthrough;
262    }
263
264    public void setAssumeExternalScriptPassthrough(boolean assumeExternalScriptPassthrough) {
265        this.assumeExternalScriptPassthrough = assumeExternalScriptPassthrough;
266    }
267
268    public boolean isIgnoreCoordinate() {
269        return ignoreCoordinate;
270    }
271
272    public void setIgnoreCoordinate(boolean ignoreCoordinate) {
273        this.ignoreCoordinate = ignoreCoordinate;
274    }
275
276    public boolean isTransform() {
277        return transform;
278    }
279
280    public void setTransform(boolean transform) {
281        this.transform = transform;
282    }
283
284    public boolean isTransformCoordinate() {
285        return transformCoordinate;
286    }
287
288    public void setTransformCoordinate(boolean transformCoordinate) {
289        this.transformCoordinate = transformCoordinate;
290    }
291
292    public boolean isMssqlColonBindVariablesEnabled() {
293        return enableMssqlColonBindVariables;
294    }
295
296    public void setEnableMssqlColonBindVariables(boolean enabled) {
297        this.enableMssqlColonBindVariables = enabled;
298    }
299
300    public boolean isCollectAuthoritativeLineageEvidence() {
301        return collectAuthoritativeLineageEvidence;
302    }
303
304    public void setCollectAuthoritativeLineageEvidence(boolean collectAuthoritativeLineageEvidence) {
305        this.collectAuthoritativeLineageEvidence = collectAuthoritativeLineageEvidence;
306    }
307
308    public boolean isSimpleShowFunction() {
309        return simpleShowFunction;
310    }
311
312    public void setSimpleShowFunction(boolean simpleShowFunction) {
313        this.simpleShowFunction = simpleShowFunction;
314    }
315
316    public boolean isSimpleShowSynonym() {
317        return simpleShowSynonym;
318    }
319
320    public void setSimpleShowSynonym(boolean simpleShowSynonym) {
321        this.simpleShowSynonym = simpleShowSynonym;
322    }
323
324    public boolean isSimpleShowTopSelectResultSet() {
325        return simpleShowTopSelectResultSet;
326    }
327
328    public void setSimpleShowTopSelectResultSet(boolean simpleShowTopSelectResultSet) {
329        this.simpleShowTopSelectResultSet = simpleShowTopSelectResultSet;
330    }
331
332    public boolean isShowImplicitSchema() {
333        return showImplicitSchema;
334    }
335
336    public void setShowImplicitSchema(boolean showImplicitSchema) {
337        this.showImplicitSchema = showImplicitSchema;
338    }
339
340    public boolean isShowCountTableColumn() {
341        return showCountTableColumn;
342    }
343
344    public void setShowCountTableColumn(boolean showCountTableColumn) {
345        this.showCountTableColumn = showCountTableColumn;
346    }
347
348    public boolean isShowConstantTable() {
349        return showConstantTable;
350    }
351
352    public void setShowConstantTable(boolean showConstantTable) {
353        this.showConstantTable = showConstantTable;
354    }
355
356    public long getStartId() {
357        return startId;
358    }
359
360    public void setStartId(long startId) {
361        this.startId = startId;
362    }
363
364    public boolean isOutput() {
365        return output;
366    }
367
368    public void setOutput(boolean output) {
369        this.output = output;
370    }
371
372    public int getParallel() {
373        return parallel;
374    }
375
376    public void setParallel(int parallel) {
377        this.parallel = parallel;
378    }
379
380    public boolean isTraceSQL() {
381        return traceSQL;
382    }
383
384    public void setTraceSQL(boolean traceSQL) {
385        this.traceSQL = traceSQL;
386    }
387
388    public boolean isIgnoreTopSelect() {
389                return ignoreTopSelect;
390        }
391
392        public void setIgnoreTopSelect(boolean ignoreTopSelect) {
393                this.ignoreTopSelect = ignoreTopSelect;
394        }
395        
396        public void setShowCallRelation(boolean showCallRelation) {
397                this.showCallRelation = showCallRelation;
398        }
399        
400        public boolean isShowCallRelation() {
401                return showCallRelation;
402        }
403
404        public String getFilePathDatabase() {
405                return filePathDatabase;
406        }
407
408        public void setFilePathDatabase(String filePathDatabase) {
409                this.filePathDatabase = filePathDatabase;
410        }
411
412        public String getFilePathSchema() {
413                return filePathSchema;
414        }
415
416        public void setFilePathSchema(String filePathSchema) {
417                this.filePathSchema = filePathSchema;
418        }
419
420        public String getDefaultServer() {
421                return defaultServer;
422        }
423
424        public void setDefaultServer(String defaultServer) {
425                this.defaultServer = defaultServer;
426        }
427
428        public String getDefaultDatabase() {
429                return defaultDatabase;
430        }
431
432        public void setDefaultDatabase(String defaultDatabase) {
433                this.defaultDatabase = defaultDatabase;
434        }
435
436        public String getDefaultSchema() {
437                return defaultSchema;
438        }
439
440        public void setDefaultSchema(String defaultSchema) {
441                this.defaultSchema = defaultSchema;
442        }
443
444    public boolean isShowERDiagram() {
445        return showERDiagram;
446    }
447
448    public void setShowERDiagram(boolean showERDiagram) {
449        this.showERDiagram = showERDiagram;
450    }
451
452    public boolean isIgnoreTemporaryTable() {
453        return ignoreTemporaryTable;
454    }
455
456    public void setIgnoreTemporaryTable(boolean ignoreTemporaryTable) {
457        this.ignoreTemporaryTable = ignoreTemporaryTable;
458    }
459    
460    public void showResultSetTypes(ResultSetType... types) {
461        if(types!=null) {
462                targetResultSetTypes.addAll(Arrays.asList(types));
463        }
464    }
465    
466        public void showResultSetTypes(String... types) {
467                if (types != null) {
468                        for (String type : types) {
469                                ResultSetType resultSetType = ResultSetType.of(type);
470                                if (resultSetType != null) {
471                                        targetResultSetTypes.add(resultSetType);
472                                }
473                        }
474                }
475        }
476    
477    public boolean containsResultSetType(ResultSetType type) {
478        return targetResultSetTypes.contains(type);
479    }
480
481    @Override
482    public Object clone() throws CloneNotSupportedException {
483        return super.clone();
484    }
485    
486        public void filterRelationTypes(String... types) {
487                if (types != null) {
488                        for (String type : types) {
489                                RelationshipType relationshipType = RelationshipType.of(type);
490                                if (relationshipType != null) {
491                                        filterRelationTypes.add(relationshipType.name());
492                                }
493                        }
494                }
495        }
496
497        public Set<String> getFilterRelationTypes() {
498                return filterRelationTypes;
499        }
500
501    public boolean isSimpleShowCursor() {
502        return simpleShowCursor;
503    }
504
505    public void setSimpleShowCursor(boolean simpleShowCursor) {
506        this.simpleShowCursor = simpleShowCursor;
507    }
508
509    public boolean isSimpleShowVariable() {
510        return simpleShowVariable;
511    }
512
513    public void setSimpleShowVariable(boolean simpleShowVariable) {
514        this.simpleShowVariable = simpleShowVariable;
515    }
516
517    public boolean isSimpleRetainIntermediate() {
518        return simpleRetainIntermediate;
519    }
520
521    public void setSimpleRetainIntermediate(boolean simpleRetainIntermediate) {
522        this.simpleRetainIntermediate = simpleRetainIntermediate;
523    }
524
525    public boolean isSimpleShowUdfFunctionOnly() {
526        return simpleShowUdfFunctionOnly;
527    }
528
529    public void setSimpleShowUdfFunctionOnly(boolean simpleShowUdfFunctionOnly) {
530        this.simpleShowUdfFunctionOnly = simpleShowUdfFunctionOnly;
531    }
532
533        public boolean isTraceProcedure() {
534                return traceProcedure;
535        }
536
537        public void setTraceProcedure(boolean traceProcedure) {
538                this.traceProcedure = traceProcedure;
539        }
540
541    public boolean isSqlflowIgnoreFunction() {
542        return sqlflowIgnoreFunction;
543    }
544
545    public void setSqlflowIgnoreFunction(boolean sqlflowIgnoreFunction) {
546        this.sqlflowIgnoreFunction = sqlflowIgnoreFunction;
547    }
548
549    public boolean isShowCaseWhenAsDirect() {
550        return showCaseWhenAsDirect;
551    }
552
553    public void setShowCaseWhenAsDirect(boolean showCaseWhenAsDirect) {
554        this.showCaseWhenAsDirect = showCaseWhenAsDirect;
555    }
556
557        public boolean isIgnoreUnusedSynonym() {
558                return ignoreUnusedSynonym;
559        }
560
561        public void setIgnoreUnusedSynonym(boolean ignoreUnusedSynonym) {
562                this.ignoreUnusedSynonym = ignoreUnusedSynonym;
563        }
564
565        public boolean isShowCandidateTable() {
566                return showCandidateTable;
567        }
568
569        public void setShowCandidateTable(boolean showCandidateTable) {
570                this.showCandidateTable = showCandidateTable;
571        }
572
573        public Set<String> getSimpleShowRelationTypes() {
574                return simpleShowRelationTypes;
575        }
576
577        public void setSimpleShowRelationTypes(String... types) {
578                if (types != null) {
579                        for (String type : types) {
580                                RelationshipType relationshipType = RelationshipType.of(type);
581                                if (relationshipType != null) {
582                                        simpleShowRelationTypes.add(relationshipType.name());
583                                }
584                        }
585                }
586        }
587        
588        public void setSimpleShowRelationTypes(RelationshipType... types) {
589                if (types != null) {
590                        for (RelationshipType relationshipType : types) {
591                                simpleShowRelationTypes.add(relationshipType.name());
592                        }
593                }
594        }
595
596        public AnalyzeMode getAnalyzeMode() {
597                return analyzeMode;
598        }
599
600        public void setAnalyzeMode(AnalyzeMode analyzeMode) {
601                this.analyzeMode = analyzeMode;
602        }
603
604        public boolean isIgnoreInsertIntoValues() {
605                return ignoreInsertIntoValues;
606        }
607
608        public void setIgnoreInsertIntoValues(boolean ignoreInsertIntoValues) {
609                this.ignoreInsertIntoValues = ignoreInsertIntoValues;
610        }
611
612    public boolean isNormalizeOutput() {
613        return normalizeOutput;
614    }
615
616    public void setNormalizeOutput(boolean normalizeOutput) {
617        this.normalizeOutput = normalizeOutput;
618    }
619
620        public boolean isTraceTablePosition() {
621                return traceTablePosition;
622        }
623
624        public void setTraceTablePosition(boolean traceTablePosition) {
625                this.traceTablePosition = traceTablePosition;
626        }
627
628        public boolean isEnablePipelinedStitching() {
629                return enablePipelinedStitching;
630        }
631
632        public boolean isIdentityFirstVariablePools() {
633                return identityFirstVariablePools;
634        }
635
636        public void setIdentityFirstVariablePools(boolean identityFirstVariablePools) {
637                this.identityFirstVariablePools = identityFirstVariablePools;
638        }
639
640        public void setEnablePipelinedStitching(boolean enablePipelinedStitching) {
641                this.enablePipelinedStitching = enablePipelinedStitching;
642        }
643
644        public int getMaxPipelinedExpansionDepth() {
645                return maxPipelinedExpansionDepth;
646        }
647
648        public void setMaxPipelinedExpansionDepth(int maxPipelinedExpansionDepth) {
649                this.maxPipelinedExpansionDepth = maxPipelinedExpansionDepth;
650        }
651
652        public int getMaxStitchedSourcesPerColumn() {
653        return maxStitchedSourcesPerColumn;
654    }
655
656    public void setMaxStitchedSourcesPerColumn(int maxStitchedSourcesPerColumn) {
657        this.maxStitchedSourcesPerColumn = maxStitchedSourcesPerColumn;
658    }
659
660    public Set<String> getExcludedProcedureNames() {
661        return excludedProcedureNames;
662    }
663
664    public void setExcludedProcedureNames(Set<String> excludedProcedureNames) {
665        this.excludedProcedureNames = excludedProcedureNames;
666    }
667
668    public void addExcludedProcedureName(String name) {
669        if (name != null && !name.isEmpty()) {
670            this.excludedProcedureNames.add(name);
671        }
672    }
673
674    public void addExcludedProcedureNames(String... names) {
675        if (names != null) {
676            for (String name : names) {
677                addExcludedProcedureName(name);
678            }
679        }
680    }
681
682    /**
683     * @see #excludedProcedurePatterns
684     */
685    public Set<String> getExcludedProcedurePatterns() {
686        return excludedProcedurePatterns;
687    }
688
689    /**
690     * @see #excludedProcedurePatterns
691     */
692    public void setExcludedProcedurePatterns(Set<String> excludedProcedurePatterns) {
693        this.excludedProcedurePatterns = excludedProcedurePatterns;
694    }
695
696    /**
697     * Adds a wildcard pattern to exclude procedures from analysis.
698     * @param pattern A wildcard pattern (e.g., "SCHEMA1.*", "*.SP_*", "*_TEST")
699     * @see #excludedProcedurePatterns
700     */
701    public void addExcludedProcedurePattern(String pattern) {
702        if (pattern != null && !pattern.isEmpty()) {
703            this.excludedProcedurePatterns.add(pattern);
704        }
705    }
706
707    /**
708     * Adds multiple wildcard patterns to exclude procedures from analysis.
709     * @param patterns Variable number of wildcard patterns
710     * @see #excludedProcedurePatterns
711     */
712    public void addExcludedProcedurePatterns(String... patterns) {
713        if (patterns != null) {
714            for (String pattern : patterns) {
715                addExcludedProcedurePattern(pattern);
716            }
717        }
718    }
719
720    /**
721     * Whether to automatically detect large files and delegate to
722     * {@link gudusoft.gsqlparser.dlineage.ParallelDataFlowAnalyzer} for parallel
723     * processing. When enabled, the {@link gudusoft.gsqlparser.dlineage.util.LargeFileDetector}
724     * checks the input scale against the configured thresholds and may split large
725     * files or route sharded manifests to the parallel analyzer. Default {@code false}.
726     */
727    private boolean autoDetectLargeFile = false;
728
729    public boolean isAutoDetectLargeFile() {
730        return autoDetectLargeFile;
731    }
732
733    public void setAutoDetectLargeFile(boolean autoDetectLargeFile) {
734        this.autoDetectLargeFile = autoDetectLargeFile;
735    }
736
737    /**
738     * Threshold for the number of {@link SqlInfo} entries that triggers large-file
739     * delegation. When the total SqlInfo count reaches this value, the analyzer
740     * delegates to parallel processing. Default {@code 1000}.
741     */
742    private int largeSqlInfoCountThreshold = 1000;
743
744    public int getLargeSqlInfoCountThreshold() {
745        return largeSqlInfoCountThreshold;
746    }
747
748    public void setLargeSqlInfoCountThreshold(int largeSqlInfoCountThreshold) {
749        this.largeSqlInfoCountThreshold = largeSqlInfoCountThreshold;
750    }
751
752    /**
753     * Threshold for the total SQL content size (in bytes) that triggers large-file
754     * delegation. When the sum of all SQL string lengths reaches this value, the
755     * analyzer delegates to parallel processing. Default {@code 25 * 1024 * 1024} (25 MB).
756     */
757    private long largeSqlTotalSizeThreshold = 25 * 1024 * 1024L;
758
759    public long getLargeSqlTotalSizeThreshold() {
760        return largeSqlTotalSizeThreshold;
761    }
762
763    public void setLargeSqlTotalSizeThreshold(long largeSqlTotalSizeThreshold) {
764        this.largeSqlTotalSizeThreshold = largeSqlTotalSizeThreshold;
765    }
766
767    /**
768     * Threshold for the number of queries in a sqlflow/grabit manifest that triggers
769     * large-file delegation. When the manifest contains this many queries, the analyzer
770     * delegates to parallel processing. Default {@code 1000}.
771     */
772    private int largeQueryCountThreshold = 1000;
773
774    public int getLargeQueryCountThreshold() {
775        return largeQueryCountThreshold;
776    }
777
778    public void setLargeQueryCountThreshold(int largeQueryCountThreshold) {
779        this.largeQueryCountThreshold = largeQueryCountThreshold;
780    }
781
782    /**
783     * Threshold for the number of shards in a sqlflow-sharded manifest that triggers
784     * large-file delegation. When the manifest references this many shard source files,
785     * the analyzer delegates to parallel processing. Default {@code 10}.
786     */
787    private int largeShardCountThreshold = 10;
788
789    public int getLargeShardCountThreshold() {
790        return largeShardCountThreshold;
791    }
792
793    public void setLargeShardCountThreshold(int largeShardCountThreshold) {
794        this.largeShardCountThreshold = largeShardCountThreshold;
795    }
796
797    /**
798     * Maximum size in MB for a single file before it is split into smaller chunks
799     * for parallel processing. Files larger than this threshold are split using
800     * {@link gudusoft.gsqlparser.util.FileSplitter}. Default {@code 5} (5 MB).
801     */
802    private int largeFileSplitSizeMB = 5;
803
804    public int getLargeFileSplitSizeMB() {
805        return largeFileSplitSizeMB;
806    }
807
808    public void setLargeFileSplitSizeMB(int largeFileSplitSizeMB) {
809        this.largeFileSplitSizeMB = largeFileSplitSizeMB;
810    }
811
812    /**
813     * Multiplier applied to {@link #largeFileSplitSizeMB} to calculate the
814     * byte threshold in {@link #isLargeSql(String)}. Default {@code 1.5}.
815     */
816    private double largeSqlThresholdMultiplier = 1.5;
817
818    public double getLargeSqlThresholdMultiplier() {
819        return largeSqlThresholdMultiplier;
820    }
821
822    public void setLargeSqlThresholdMultiplier(double largeSqlThresholdMultiplier) {
823        this.largeSqlThresholdMultiplier = largeSqlThresholdMultiplier;
824    }
825
826    public boolean isLargeSql(String sql) {
827        if (!autoDetectLargeFile || sql == null) {
828            return false;
829        }
830        return sql.length() > getLargeSqlThresholdBytes();
831    }
832
833    public long getLargeSqlThresholdBytes() {
834        return (long) (largeFileSplitSizeMB * largeSqlThresholdMultiplier * 1024 * 1024);
835    }
836
837    /**
838     * Estimated memory consumption per parallel task in MB, used to calculate the
839     * thread pool size in {@code ParallelDataFlowAnalyzer}. The pool size is capped
840     * so that {@code poolSize * estimatedMemoryPerTaskMB} does not exceed the JVM's
841     * available memory. Default {@code 2560} (2.5 GB).
842     */
843    private long estimatedMemoryPerTaskMB = 2560L;
844
845    public long getEstimatedMemoryPerTaskMB() {
846        return estimatedMemoryPerTaskMB;
847    }
848
849    public void setEstimatedMemoryPerTaskMB(long estimatedMemoryPerTaskMB) {
850        this.estimatedMemoryPerTaskMB = estimatedMemoryPerTaskMB;
851    }
852
853}