001package gudusoft.gsqlparser.dlineage.dataflow.model; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.ESetOperatorType; 005import gudusoft.gsqlparser.TCustomSqlStatement; 006import gudusoft.gsqlparser.TSourceToken; 007import gudusoft.gsqlparser.dlineage.util.DlineageUtil; 008import gudusoft.gsqlparser.dlineage.util.Pair3; 009import gudusoft.gsqlparser.nodes.TCTE; 010import gudusoft.gsqlparser.nodes.TFunctionCall; 011import gudusoft.gsqlparser.nodes.TJoinList; 012import gudusoft.gsqlparser.pp.utils.SourceTokenSearcher; 013import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 014import gudusoft.gsqlparser.sqlenv.TSQLEnv; 015import gudusoft.gsqlparser.stmt.*; 016import gudusoft.gsqlparser.stmt.hive.THiveLoad; 017import gudusoft.gsqlparser.stmt.mssql.TCreateExternalDataSourceStmt; 018import gudusoft.gsqlparser.stmt.mssql.TMssqlCreateFunction; 019import gudusoft.gsqlparser.stmt.mssql.TMssqlDeclare; 020import gudusoft.gsqlparser.stmt.mssql.TMssqlExecute; 021import gudusoft.gsqlparser.stmt.snowflake.TCreateStageStmt; 022import gudusoft.gsqlparser.stmt.snowflake.TCreateStreamStmt; 023import gudusoft.gsqlparser.stmt.snowflake.TSnowflakeCopyIntoStmt; 024import gudusoft.gsqlparser.stmt.teradata.TTeradataCreateProcedure; 025import gudusoft.gsqlparser.util.Logger; 026import gudusoft.gsqlparser.util.LoggerFactory; 027import gudusoft.gsqlparser.util.SQLUtil; 028 029import java.io.ByteArrayInputStream; 030import java.io.IOException; 031import java.util.ArrayDeque; 032import java.util.ArrayList; 033import java.util.Deque; 034import java.util.List; 035import java.util.Properties; 036 037public class Process { 038 private static final Logger logger = LoggerFactory.getLogger(Process.class); 039 private long id; 040 protected String server; 041 protected String schema; 042 protected String database; 043 private String name; 044 private String procedureName; 045 private Long procedureId; 046 private String queryHashId; 047 private String type; 048 private String customType; 049 private TCustomSqlStatement gspObject; 050 private Pair3<Long, Long, String> startPosition; 051 private Pair3<Long, Long, String> endPosition; 052 private List<Object> targetColumns = new ArrayList<Object>(); 053 private List<Transform> transforms = new ArrayList<Transform>(); 054 055 public Process(TCustomSqlStatement gspObject) { 056 057 if (gspObject == null) { 058 throw new IllegalArgumentException("Process arguments can't be null."); 059 } 060 061 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 062 063 this.gspObject = gspObject; 064 065 TSourceToken startToken = gspObject.getStartToken(); 066 TSourceToken endToken = gspObject.getEndToken(); 067 if (startToken != null) { 068 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 069 ModelBindingManager.getGlobalHash()); 070 } 071 072 if (endToken != null) { 073 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 074 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 075 } 076 077 this.schema = ModelBindingManager.getGlobalSchema(); 078 this.database = ModelBindingManager.getGlobalDatabase(); 079 this.queryHashId = SQLUtil.stringToMD5(gspObject.asCanonical().replaceAll("\r?\n", "\r\n")); 080 081 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 082 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 083 boolean supportSchema = TSQLEnv.supportSchema(vendor); 084 085 fillSchemaInfo(); 086 087 if (!supportCatalog) { 088 this.database = null; 089 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 090 this.database = getDefaultDatabase(); 091 } 092 093 if (!supportSchema) { 094 this.schema = null; 095 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 096 this.schema = getDefaultSchema(); 097 } 098 099 String procedureParent = getProcedureParentName(gspObject); 100 if (procedureParent != null) { 101 procedureName = procedureParent; 102 Procedure procedure = ModelBindingManager.get().getRoutineModel(getParentProcedure(gspObject)); 103 if (procedure != null) { 104 this.procedureId = procedure.getId(); 105 } 106 } else { 107 procedureName = "batchQueries"; 108 } 109 110 TCustomSqlStatement stmt = DlineageUtil.getTopStmt(ModelBindingManager.getGlobalStmtStack().peek()); 111 String sqlComment = null; 112 try { 113 sqlComment = stmt.getCommentBeforeNode(); 114 } catch (Exception e) { 115 } 116 if (!SQLUtil.isEmpty(sqlComment) && sqlComment.indexOf("process") != -1) { 117 Properties properties = new Properties(); 118 try { 119 properties.load( 120 new ByteArrayInputStream(sqlComment.replace("--", "").trim().replace(",", "\n").getBytes())); 121 if (properties.containsKey("process_label")) { 122 this.customType = properties.getProperty("process_label"); 123 } else if (properties.containsKey("process")) { 124 this.customType = properties.getProperty("process"); 125 } 126 } catch (IOException e) { 127 logger.error("load sql comment properties failed.", e); 128 } 129 } 130 131 if (gspObject instanceof TCreateTableSqlStatement) { 132 this.type = "Create Table"; 133 } else if (gspObject instanceof TCreateViewSqlStatement) { 134 this.type = "Create View"; 135 } else if (gspObject instanceof TUpdateSqlStatement) { 136 this.type = "Update"; 137 } else if (gspObject instanceof TMergeSqlStatement) { 138 this.type = "Merge"; 139 } else if (gspObject instanceof TInsertSqlStatement) { 140 this.type = "Insert"; 141 } else if (gspObject instanceof TSelectSqlStatement 142 && ((TSelectSqlStatement) gspObject).getIntoClause() != null) { 143 this.type = "Select Into"; 144 } else if (gspObject instanceof TCreateTriggerStmt) { 145 this.type = "Create Trigger"; 146 } else if (gspObject instanceof TCreateFunctionStmt) { 147 this.type = "Create Function"; 148 } else if (gspObject instanceof TMssqlCreateFunction) { 149 this.type = "Create Function"; 150 } else if (gspObject instanceof TCreateProcedureStmt) { 151 this.type = "Create Procedure"; 152 } else if (gspObject instanceof THiveLoad) { 153 this.type = "Hive Load"; 154 } else if (gspObject instanceof TSnowflakeCopyIntoStmt) { 155 this.type = "Copy Into"; 156 } else if (gspObject instanceof TAlterTableStatement) { 157 this.type = "Alter Table"; 158 } else if (gspObject instanceof TRenameStmt) { 159 this.type = "Rename Table"; 160 } else if (gspObject instanceof TMssqlDeclare) { 161 this.type = "Mssql Declare"; 162 } else if (gspObject instanceof TCreateStageStmt) { 163 this.type = "Create Stage"; 164 } else if (gspObject instanceof TCreateSynonymStmt) { 165 this.type = "Create Synonym"; 166 } else if (gspObject instanceof TCreateExternalDataSourceStmt) { 167 this.type = "Create Datasource"; 168 } else if (gspObject instanceof TCreateStreamStmt) { 169 this.type = "Create Stream"; 170 } else if (gspObject instanceof TCreateDatabaseSqlStatement) { 171 this.type = "Create Database"; 172 } else if (gspObject instanceof TCreateSchemaSqlStatement) { 173 this.type = "Create Schema"; 174 } else if (gspObject instanceof TMssqlExecute && ((TMssqlExecute)gspObject).getModuleName().toString().equalsIgnoreCase("sp_rename")) { 175 this.type = "Rename Table"; 176 } else if (gspObject instanceof TCallStatement){ 177 this.type = "Function Call"; 178 } else { 179 this.type = gspObject.getClass().getSimpleName(); 180 } 181 182 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 183 this.server = getDefaultServer(); 184 } 185 186 if (stmt.getStatements() != null) { 187 for (int i = 0; i < stmt.getStatements().size(); i++) { 188 TCustomSqlStatement subquery = stmt.getStatements().get(i); 189 if (subquery instanceof TSelectSqlStatement) { 190 TSelectSqlStatement select = (TSelectSqlStatement) subquery; 191 appendTransform(select, ESetOperatorType.none); 192 } 193 } 194 } 195 } 196 197 public Process(TFunctionCall gspObject) { 198 199 if (gspObject == null) { 200 throw new IllegalArgumentException("Process arguments can't be null."); 201 } 202 203 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 204 205 206 TSourceToken startToken = gspObject.getStartToken(); 207 TSourceToken endToken = gspObject.getEndToken(); 208 if (startToken != null) { 209 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 210 ModelBindingManager.getGlobalHash()); 211 } 212 213 if (endToken != null) { 214 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 215 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 216 } 217 218 TCustomSqlStatement stmt = DlineageUtil.getTopStmt(ModelBindingManager.getGlobalStmtStack().peek()); 219 this.gspObject = stmt; 220 221 this.schema = ModelBindingManager.getGlobalSchema(); 222 this.database = ModelBindingManager.getGlobalDatabase(); 223 224 if (stmt != null) { 225 this.queryHashId = SQLUtil.stringToMD5(stmt.asCanonical().replaceAll("\r?\n", "\r\n")); 226 } 227 228 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 229 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 230 boolean supportSchema = TSQLEnv.supportSchema(vendor); 231 232 fillSchemaInfo(); 233 234 if (!supportCatalog) { 235 this.database = null; 236 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 237 this.database = getDefaultDatabase(); 238 } 239 240 if (!supportSchema) { 241 this.schema = null; 242 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 243 this.schema = getDefaultSchema(); 244 } 245 246 String procedureParent = getProcedureParentName(stmt); 247 if (procedureParent != null) { 248 procedureName = procedureParent; 249 Procedure procedure = ModelBindingManager.get().getRoutineModel(getParentProcedure(stmt)); 250 if (procedure != null) { 251 this.procedureId = procedure.getId(); 252 } 253 } else { 254 procedureName = "batchQueries"; 255 } 256 257 String sqlComment = null; 258 try { 259 sqlComment = stmt.getCommentBeforeNode(); 260 } catch (Exception e) { 261 } 262 if (!SQLUtil.isEmpty(sqlComment) && sqlComment.indexOf("process") != -1) { 263 Properties properties = new Properties(); 264 try { 265 properties.load( 266 new ByteArrayInputStream(sqlComment.replace("--", "").trim().replace(",", "\n").getBytes())); 267 if (properties.containsKey("process_label")) { 268 this.customType = properties.getProperty("process_label"); 269 } else if (properties.containsKey("process")) { 270 this.customType = properties.getProperty("process"); 271 } 272 } catch (IOException e) { 273 logger.error("load sql comment properties failed.", e); 274 } 275 } 276 277 this.type = "Function Call"; 278 279 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 280 this.server = getDefaultServer(); 281 } 282 } 283 284 private void appendTransform(TSelectSqlStatement select, ESetOperatorType operatorType) { 285 // Iterative traversal of the UNION tree to avoid StackOverflow with deeply nested unions. 286 Deque<Object[]> stack = new ArrayDeque<>(); 287 stack.push(new Object[]{select, operatorType}); 288 289 while (!stack.isEmpty()) { 290 Object[] entry = stack.pop(); 291 TSelectSqlStatement current = (TSelectSqlStatement) entry[0]; 292 ESetOperatorType currentOp = (ESetOperatorType) entry[1]; 293 294 if (currentOp != ESetOperatorType.none) { 295 Transform transform = new Transform(); 296 transform.setType(currentOp.name()); 297 transform.setCodeString(current.toString()); 298 transform.setStartToken(current.getStartToken()); 299 transform.setEndToken(current.getEndToken()); 300 transforms.add(transform); 301 } else { 302 if (current.getCteList() != null && current.getCteList().size() > 0) { 303 for (int i = 0; i < current.getCteList().size(); i++) { 304 TCTE cte = current.getCteList().getCTE(i); 305 Transform transform = new Transform(); 306 transform.setType(Transform.CTE); 307 transform.setCodeString(cte.toString()); 308 transform.setStartToken(cte.getStartToken()); 309 transform.setEndToken(cte.getEndToken()); 310 transforms.add(transform); 311 } 312 } 313 if (current.getSetOperatorType() != ESetOperatorType.none) { 314 // Push right first so left is processed first (stack is LIFO) 315 if (current.getRightStmt() != null) { 316 ESetOperatorType rightOp = current.getRightStmt().getSetOperatorType() != ESetOperatorType.none 317 ? ESetOperatorType.none : current.getSetOperatorType(); 318 stack.push(new Object[]{current.getRightStmt(), rightOp}); 319 } 320 if (current.getLeftStmt() != null) { 321 ESetOperatorType leftOp = current.getLeftStmt().getSetOperatorType() != ESetOperatorType.none 322 ? ESetOperatorType.none : current.getSetOperatorType(); 323 stack.push(new Object[]{current.getLeftStmt(), leftOp}); 324 } 325 } else { 326 if (current.getJoins() != null && current.getJoins().size() > 0) { 327 TJoinList joins = current.getJoins(); 328 TSourceToken startToken = joins.getStartToken(); 329 TSourceToken fromToken = SourceTokenSearcher.backforwardSearch(startToken, 10, "from"); 330 if (fromToken == null) { 331 if (startToken.getAstext().equalsIgnoreCase("from")) { 332 fromToken = startToken; 333 } else { 334 continue; 335 } 336 } 337 StringBuilder builder = new StringBuilder(); 338 if (joins.getEndToken().posinlist < fromToken.container.size() 339 && joins.getEndToken().posinlist >= fromToken.posinlist) { 340 for (int j = fromToken.posinlist; j <= joins.getEndToken().posinlist; j++) { 341 builder.append(fromToken.container.get(j)); 342 } 343 } else { 344 System.err.println("Handle statement transform error, statemet is:"); 345 System.err.println(current.toString()); 346 } 347 Transform transform = new Transform(); 348 transform.setType(Transform.FROM); 349 transform.setCodeString(builder.toString()); 350 transform.setStartToken(fromToken); 351 transform.setEndToken(joins.getEndToken()); 352 transforms.add(transform); 353 } 354 } 355 } 356 } 357 } 358 359 private TSelectSqlStatement getFirstSubquery(TSelectSqlStatement select) { 360 // Iterative left-chain walk to avoid StackOverflow with deeply nested unions. 361 TSelectSqlStatement current = select; 362 while (current.getSetOperatorType() != ESetOperatorType.none) { 363 current = current.getLeftStmt(); 364 } 365 return current; 366 } 367 368 private TSelectSqlStatement getLastSubquery(TSelectSqlStatement select) { 369 // Iterative right-chain walk to avoid StackOverflow with deeply nested unions. 370 TSelectSqlStatement current = select; 371 while (current.getSetOperatorType() != ESetOperatorType.none) { 372 current = current.getRightStmt(); 373 } 374 return current; 375 } 376 377 private void fillSchemaInfo() { 378 TCustomSqlStatement stmt = DlineageUtil.getTopStmt(ModelBindingManager.getGlobalStmtStack().peek()); 379 String sqlComment = null; 380 try { 381 sqlComment = stmt.getCommentBeforeNode(); 382 } catch (Exception e) { 383 } 384 if (!SQLUtil.isEmpty(sqlComment) && (sqlComment.indexOf("db") != -1 || sqlComment.indexOf("schema") != -1)) { 385 Properties properties = new Properties(); 386 try { 387 properties.load( 388 new ByteArrayInputStream(sqlComment.replace("--", "").trim().replace(",", "\n").getBytes())); 389 if (SQLUtil.isEmpty(this.server) && properties.containsKey("db-instance")) { 390 this.server = properties.getProperty("db-instance"); 391 } 392 if (SQLUtil.isEmpty(this.database) && properties.containsKey("db")) { 393 this.database = properties.getProperty("db"); 394 if (this.database.indexOf(".") != -1) { 395 this.database = SQLUtil.quoteDottedName(ModelBindingManager.getGlobalOption().getVendor(), ESQLDataObjectType.dotCatalog, this.database); 396 } 397 } 398 if (SQLUtil.isEmpty(this.schema) && properties.containsKey("schema")) { 399 this.schema = properties.getProperty("schema"); 400 if (this.schema.indexOf(".") != -1) { 401 this.schema = SQLUtil.quoteDottedName(ModelBindingManager.getGlobalOption().getVendor(), ESQLDataObjectType.dotSchema, this.schema); 402 } 403 } 404 } catch (IOException e) { 405 logger.error("load sql comment properties failed.", e); 406 } 407 } 408 } 409 410 protected String getDefaultServer() { 411 String defaultServer = null; 412 if (ModelBindingManager.getGlobalSQLEnv() != null) { 413 defaultServer = ModelBindingManager.getGlobalSQLEnv().getDefaultServerName(); 414 } 415 if (!SQLUtil.isEmpty(defaultServer)) 416 return defaultServer; 417 return TSQLEnv.DEFAULT_SERVER_NAME; 418 } 419 420 protected String getDefaultSchema() { 421 return ModelBindingManager.getEffectiveDefaultSchema(); 422 } 423 424 protected String getDefaultDatabase() { 425 return ModelBindingManager.getEffectiveDefaultDatabase(); 426 } 427 428 private String getProcedureParentName(TCustomSqlStatement stmt) { 429 return getProcedureParentName(stmt, false); 430 } 431 432 /** 433 * The name of the procedure enclosing {@code stmt}, falling back to the global procedure when the chain of parents 434 * ends without one. {@code inGlobalProcedure} is set once that fallback is taken: the global procedure's own chain 435 * can end at a statement with no parent too, and falling back again from there led straight back to the global 436 * procedure, until the stack overflowed. A Snowflake {@code CREATE TASK ... AS BEGIN ... END} does that: its block 437 * is the global procedure, has no name, and its parent, the task, is no procedure. The function is deterministic, so 438 * a second fallback never ended; every walk that ended before returns what it returned. 439 */ 440 private String getProcedureParentName(TCustomSqlStatement stmt, boolean inGlobalProcedure) { 441 if (stmt instanceof TStoredProcedureSqlStatement) { 442 if (((TStoredProcedureSqlStatement) stmt).getStoredProcedureName() != null) { 443 return ((TStoredProcedureSqlStatement) stmt).getStoredProcedureName().toString(); 444 } 445 } 446 447 if (stmt == null) { 448 return null; 449 } 450 451 if (stmt instanceof TTeradataCreateProcedure) { 452 if (((TTeradataCreateProcedure) stmt).getProcedureName() != null) { 453 return ((TTeradataCreateProcedure) stmt).getProcedureName().toString(); 454 } 455 } 456 457 if (stmt instanceof TStoredProcedureSqlStatement) { 458 if (((TStoredProcedureSqlStatement) stmt).getStoredProcedureName() != null) { 459 return ((TStoredProcedureSqlStatement) stmt).getStoredProcedureName().toString(); 460 } 461 462 if (stmt instanceof TCommonBlock) { 463 if (((TCommonBlock) stmt).getBlockBody().getParentObjectName() instanceof TStoredProcedureSqlStatement) { 464 stmt = (TStoredProcedureSqlStatement) ((TCommonBlock) stmt).getBlockBody().getParentObjectName(); 465 } 466 else { 467 return getProcedureParentName(stmt.getParentStmt(), inGlobalProcedure); 468 } 469 } 470 471 return null; 472 } 473 474 stmt = stmt.getParentStmt(); 475 if (stmt == null) { 476 if (!inGlobalProcedure && ModelBindingManager.getGlobalProcedure() != null) { 477 return getProcedureParentName(ModelBindingManager.getGlobalProcedure(), true); 478 } 479 else { 480 return null; 481 } 482 } 483 484 485 if (stmt instanceof TCommonBlock) { 486 if (((TCommonBlock) stmt).getBlockBody().getParentObjectName() instanceof TStoredProcedureSqlStatement) { 487 stmt = (TStoredProcedureSqlStatement) ((TCommonBlock) stmt).getBlockBody().getParentObjectName(); 488 } 489 } 490 491 return getProcedureParentName(stmt, inGlobalProcedure); 492 } 493 494 private TStoredProcedureSqlStatement getParentProcedure(TCustomSqlStatement stmt) { 495 if (stmt == null) { 496 return null; 497 } 498 499 if (stmt instanceof TStoredProcedureSqlStatement) { 500 if (((TStoredProcedureSqlStatement) stmt).getStoredProcedureName() != null) { 501 return ((TStoredProcedureSqlStatement) stmt); 502 } 503 504 if (stmt instanceof TCommonBlock) { 505 if (((TCommonBlock) stmt).getBlockBody().getParentObjectName() instanceof TStoredProcedureSqlStatement) { 506 return (TStoredProcedureSqlStatement) ((TCommonBlock) stmt).getBlockBody().getParentObjectName(); 507 } 508 else { 509 return getParentProcedure(stmt.getParentStmt()); 510 } 511 } 512 513 return null; 514 } 515 516 stmt = stmt.getParentStmt(); 517 if (stmt == null) { 518 if(ModelBindingManager.getGlobalProcedure()!=null) { 519 return ModelBindingManager.getGlobalProcedure(); 520 } 521 else { 522 return null; 523 } 524 } 525 526 if (stmt instanceof TCommonBlock) { 527 if (((TCommonBlock) stmt).getBlockBody().getParentObjectName() instanceof TStoredProcedureSqlStatement) { 528 stmt = (TStoredProcedureSqlStatement) ((TCommonBlock) stmt).getBlockBody().getParentObjectName(); 529 } 530 } 531 532 return getParentProcedure(stmt); 533 } 534 535 public Pair3<Long, Long, String> getStartPosition() { 536 return startPosition; 537 } 538 539 public Pair3<Long, Long, String> getEndPosition() { 540 return endPosition; 541 } 542 543 public TCustomSqlStatement getGspObject() { 544 return gspObject; 545 } 546 547 public long getId() { 548 return id; 549 } 550 551 public String getSchema() { 552 return schema; 553 } 554 555 public String getDatabase() { 556 return database; 557 } 558 559 public String getName() { 560 return name; 561 } 562 563 public String getType() { 564 return type; 565 } 566 567 public void setName(String name) { 568 this.name = name; 569 } 570 571 public void setType(String type) { 572 this.type = type; 573 } 574 575 public String getProcedureName() { 576 return procedureName; 577 } 578 579 public void setProcedureName(String procedureName) { 580 this.procedureName = procedureName; 581 } 582 583 public String getQueryHashId() { 584 return queryHashId; 585 } 586 587 public void setQueryHashId(String queryHashId) { 588 this.queryHashId = queryHashId; 589 } 590 591 public void appendColumn(Object tableColumn) { 592 if (tableColumn != null && !targetColumns.contains(tableColumn)) { 593 targetColumns.add(tableColumn); 594 } 595 } 596 597 public List<Object> getTargetColumns() { 598 return targetColumns; 599 } 600 601 public String getCustomType() { 602 return customType; 603 } 604 605 public Long getProcedureId() { 606 return procedureId; 607 } 608 609 public String getServer() { 610 return server; 611 } 612 613 public List<Transform> getTransforms() { 614 return transforms; 615 } 616}