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