001 002package gudusoft.gsqlparser.dlineage.dataflow.model; 003 004import gudusoft.gsqlparser.EDbVendor; 005import gudusoft.gsqlparser.ETableKind; 006import gudusoft.gsqlparser.ETableSource; 007import gudusoft.gsqlparser.TCustomSqlStatement; 008import gudusoft.gsqlparser.TSourceToken; 009import gudusoft.gsqlparser.dlineage.util.DlineageUtil; 010import gudusoft.gsqlparser.dlineage.util.Pair; 011import gudusoft.gsqlparser.dlineage.util.Pair3; 012import gudusoft.gsqlparser.nodes.TFunctionCall; 013import gudusoft.gsqlparser.nodes.TObjectName; 014import gudusoft.gsqlparser.nodes.TTable; 015import gudusoft.gsqlparser.sqlenv.*; 016import gudusoft.gsqlparser.stmt.TCreateTableSqlStatement; 017import gudusoft.gsqlparser.stmt.TCreateViewSqlStatement; 018import gudusoft.gsqlparser.util.Logger; 019import gudusoft.gsqlparser.util.LoggerFactory; 020import gudusoft.gsqlparser.util.SQLUtil; 021 022import java.io.ByteArrayInputStream; 023import java.io.IOException; 024import java.util.*; 025 026public class Table { 027 028 private static final Logger logger = LoggerFactory.getLogger(Table.class); 029 030 private long id; 031 protected String server; 032 protected String database; 033 protected String schema; 034 protected String name; 035 protected String displayName; 036 protected String fullName; 037 protected Set<String> alias = new LinkedHashSet<String>(); 038 protected Set<Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>>> positions = new LinkedHashSet<>(); 039 protected String parent; 040 protected Pair3<Long, Long, String> startPosition; 041 protected Pair3<Long, Long, String> endPosition; 042 private List<TableColumn> columns = new ArrayList<TableColumn>(); 043 private boolean subquery = false; 044 045 /** 046 * Set when an unqualified one-part name matches the same base table in two or 047 * more schemas of the (otherwise unambiguous) default catalog and the default 048 * schema holds no such object. In that case GSP must NOT fabricate a 049 * non-existent default-schema object; it leaves the reference unqualified and 050 * exposes the candidate set instead. See 051 * docs/tmp/unqualified-name-default-schema-masks-cross-schema-ambiguity.md. 052 */ 053 private boolean ambiguousUnqualifiedTable = false; 054 private List<String> candidateTables; 055 056 private TTable tableObject; 057 private TObjectName viewName; 058 private boolean isCreateTable; 059 private boolean isView; 060 private boolean isPseudo; 061 private boolean isExternal; 062 private boolean isStage; 063 private boolean isSequence; 064 private boolean isPath; 065 private boolean isStream; 066 private boolean isVariable; 067 private boolean isConstant; 068 private boolean isDatabase; 069 private boolean isSchema; 070 private boolean isDataSource; 071 private String location; 072 private String fileType; 073 private String fileFormat; 074 private List<Process> processes; 075 private SubType subType; 076 private String starStmt; 077 private boolean hasSQLEnv = false; 078 private String currentAlias; 079 private boolean isDetermined; 080 081 private boolean fromDDL; 082 083 /** 084 * How this endpoint was introduced in the analyzed SQL. The create-effect values 085 * (CREATE_TABLE_DDL / SELECT_INTO / CTAS / CLONE_TABLE) are set as parse facts at 086 * build time, independent of {@link #isDetermined()} — unlike {@link #fromDDL}. 087 * Strictly additive; never back-fills {@link #subType}. See 088 * docs/tmp/dlineage-authoritative-endpoint-classification.md. 089 */ 090 private EndpointIntroduction endpointIntroduction = EndpointIntroduction.UNKNOWN; 091 092 /** 093 * True when this endpoint is created by the analyzed SQL (CREATE TABLE / SELECT 094 * INTO / CTAS / clone target), set regardless of schema resolution. 095 */ 096 private boolean createdInSql; 097 098 private TableRelationRows relationRows = new TableRelationRows(this); 099 100 private TCustomSqlStatement stmt; 101 102 public Table(TCustomSqlStatement stmt, TObjectName viewName) { 103 this(viewName); 104 this.stmt = stmt; 105 if (stmt != null) { 106 setPosition(stmt); 107 } 108 } 109 110 public TCustomSqlStatement getSqlStatement() { 111 return stmt; 112 } 113 114 public Table(TTable table, String tableName) { 115 if (table == null) { 116 throw new IllegalArgumentException("Table arguments can't be null."); 117 } 118 119 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 120 121 this.tableObject = table; 122 123 setPosition(table); 124 125 this.name = tableName; 126 this.fullName = tableName; 127 setAlias(table.getAliasName()); 128 setCurrentAlias(table.getAliasName()); 129 130 if (name.startsWith("`") && name.endsWith("`") && name.indexOf(".") != -1 131 && ModelBindingManager.getGlobalOption().getVendor() == EDbVendor.dbvbigquery) { 132 this.name = SQLUtil.trimColumnStringQuote(name); 133 this.fullName = SQLUtil.trimColumnStringQuote(fullName); 134 } 135 136 if (table.getSubquery() != null) { 137 subquery = true; 138 } 139 140 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 141 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 142 boolean supportSchema = TSQLEnv.supportSchema(vendor); 143 144 if (table.getTableName().getDblink() == null) { 145 this.server = DlineageUtil.getTableServer(table.getTableName().toString()); 146 this.database = DlineageUtil.getTableDatabase(table.getTableName().toString()); 147 this.schema = DlineageUtil.getTableSchema(table.getTableName().toString()); 148 } 149 else { 150 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 151 this.server = getDefaultServer(); 152 } 153 this.database = SQLUtil.normalizeIdentifier(vendor, ESQLDataObjectType.dotDblink, table.getTableName().getDblink().toString()); 154 if (!SQLUtil.isEmpty(table.getTableName().getSchemaString())) { 155 this.schema = table.getTableName().getSchemaString(); 156 } 157 this.subType = SubType.dblink; 158 this.name = this.name.replace(table.getTableName().getDblink().toString(), this.database); 159 ModelBindingManager.get().appendDblinkTable(this.name); 160 return; 161 } 162 163 fillSchemaInfo(); 164 165 if (SQLUtil.isEmpty(this.database)) { 166 this.database = DlineageUtil.getTableDatabase(table.getTableName().toString()); 167 if (SQLUtil.isEmpty(this.database) && ModelBindingManager.getGlobalOption().isShowImplicitSchema() 168 && !SQLUtil.isEmpty(table.getTableName().getImplictDatabaseString())) { 169 this.database = table.getTableName().getImplictDatabaseString(); 170 } 171 } 172 173 if (SQLUtil.isEmpty(this.schema)) { 174 this.schema = DlineageUtil.getTableSchema(table.getTableName().toString()); 175 if (SQLUtil.isEmpty(this.schema) && ModelBindingManager.getGlobalOption().isShowImplicitSchema() 176 && !SQLUtil.isEmpty(table.getTableName().getImplictSchemaString())) { 177 this.schema = table.getTableName().getImplictSchemaString(); 178 } 179 } 180 181 if (!SQLUtil.isEmpty(this.database) && SQLUtil.isEmpty(this.schema)) { 182 if (!SQLUtil.isEmpty(table.getTableName().getImplictSchemaString())) { 183 this.schema = table.getTableName().getImplictSchemaString(); 184 } 185 } 186 187 if (table.getTableType() == ETableSource.function) { 188 subType = SubType.function; 189 } 190 191 if (!supportCatalog) { 192 this.database = null; 193 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 194 this.database = getDefaultDatabase(); 195 } 196 197 if (!supportSchema) { 198 this.schema = null; 199 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 200 this.schema = getDefaultSchema(); 201 } 202 203 updateTableName(supportCatalog, supportSchema); 204 205 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 206 this.server = getDefaultServer(); 207 } 208 } 209 210 public Table(TTable table) { 211 if (table == null) { 212 throw new IllegalArgumentException("Table arguments can't be null."); 213 } 214 215 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 216 217 this.tableObject = table; 218 219 setPosition(table); 220 221 if (table.getLinkTable() != null) { 222 this.fullName = table.getLinkTable().getFullName(); 223 this.name = table.getLinkTable().getName(); 224 setAlias(table.getName()); 225 setCurrentAlias(table.getName()); 226 } else if (table.getTableType() == ETableSource.function && table.getFuncCall() != null) { 227 TFunctionCall function = table.getFuncCall(); 228 this.fullName = function.getFunctionName().toString(); 229 this.name = function.getFunctionName().toString(); 230 setAlias(table.getAliasName()); 231 setCurrentAlias(table.getAliasName()); 232 } else { 233 if (table.getSubquery() != null && table.getAliasClause() != null) { 234 this.name = table.getAliasClause().getAliasName().toString(); 235 this.subType = SubType.alias_table; 236 } else if (table.getTableName() != null) { 237 this.name = table.getTableName().toString(); 238 } else { 239 this.name = table.getName(); 240 } 241 242 this.fullName = table.getFullName(); 243 if (this.fullName == null) { 244 this.fullName = this.name; 245 } 246 247 setAlias(table.getAliasName()); 248 setCurrentAlias(table.getAliasName()); 249 } 250 251 if (name.startsWith("`") && name.endsWith("`") && name.indexOf(".") != -1 252 && ModelBindingManager.getGlobalOption().getVendor() == EDbVendor.dbvbigquery) { 253 this.name = SQLUtil.trimColumnStringQuote(name); 254 this.fullName = SQLUtil.trimColumnStringQuote(fullName); 255 } 256 257 if (table.getSubquery() != null) { 258 subquery = true; 259 } 260 261 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 262 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 263 boolean supportSchema = TSQLEnv.supportSchema(vendor); 264 265 if (table.getTableName().getDblink() == null) { 266 this.server = DlineageUtil.getTableServer(table.getTableName().toString()); 267 this.database = DlineageUtil.getTableDatabase(table.getTableName().toString()); 268 this.schema = DlineageUtil.getTableSchema(table.getTableName().toString()); 269 } 270 else { 271 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 272 this.server = getDefaultServer(); 273 } 274 this.database = SQLUtil.normalizeIdentifier(vendor, ESQLDataObjectType.dotDblink, table.getTableName().getDblink().toString()); 275 if (!SQLUtil.isEmpty(table.getTableName().getSchemaString())) { 276 this.schema = table.getTableName().getSchemaString(); 277 } 278 this.subType = SubType.dblink; 279 this.name = this.name.replace(table.getTableName().getDblink().toString(), this.database); 280 ModelBindingManager.get().appendDblinkTable(this.name); 281 return; 282 } 283 284 fillSchemaInfo(); 285 286 if (SQLUtil.isEmpty(this.database)) { 287 this.database = DlineageUtil.getTableDatabase(table.getTableName().toString()); 288 if (SQLUtil.isEmpty(this.database) && ModelBindingManager.getGlobalOption().isShowImplicitSchema() 289 && !SQLUtil.isEmpty(table.getTableName().getImplictDatabaseString())) { 290 this.database = table.getTableName().getImplictDatabaseString(); 291 } 292 } 293 294 if (SQLUtil.isEmpty(this.schema)) { 295 this.schema = DlineageUtil.getTableSchema(table.getTableName().toString()); 296 if (SQLUtil.isEmpty(this.schema) && ModelBindingManager.getGlobalOption().isShowImplicitSchema() 297 && !SQLUtil.isEmpty(table.getTableName().getImplictSchemaString())) { 298 this.schema = table.getTableName().getImplictSchemaString(); 299 } 300 } 301 302 if (!SQLUtil.isEmpty(this.database) && SQLUtil.isEmpty(this.schema)) { 303 if (!SQLUtil.isEmpty(table.getTableName().getImplictSchemaString())) { 304 this.schema = table.getTableName().getImplictSchemaString(); 305 } 306 } 307 308 if (table.getTableType() == ETableSource.function) { 309 subType = SubType.function; 310 } 311 312 if (!supportCatalog) { 313 this.database = null; 314 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 315 this.database = getDefaultDatabase(); 316 } 317 318 if (!supportSchema) { 319 this.schema = null; 320 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 321 this.schema = getDefaultSchema(); 322 } 323 324 updateTableName(supportCatalog, supportSchema); 325 326 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 327 this.server = getDefaultServer(); 328 } 329 } 330 331 public void addColumnsFromSQLEnv(){ 332 TSQLEnv sqlEnv = ModelBindingManager.getGlobalSQLEnv(); 333 if (sqlEnv == null) { 334 return; 335 } 336 TSQLTable tsqlTable = sqlEnv.searchTable(ModelFactory.getQualifiedTableName(this)); 337 if (tsqlTable != null && tsqlTable.getColumnList()!=null && !tsqlTable.getColumnList().isEmpty()) { 338 for(TSQLColumn tsqlColumn: tsqlTable.getColumnList()){ 339 TObjectName columnName = new TObjectName(); 340 columnName.setString(tsqlColumn.getName()); 341 new TableColumn(this, columnName); 342 } 343 this.setCreateTable(true); 344 this.setHasSQLEnv(true); 345 } 346 } 347 348 private void fillSchemaInfo() { 349 Stack<TCustomSqlStatement> stmtStack = ModelBindingManager.getGlobalStmtStack(); 350 if (!stmtStack.isEmpty()) { 351 TCustomSqlStatement currentStmt = stmtStack.peek(); 352 TCustomSqlStatement stmt = DlineageUtil.getTopStmt(stmtStack.peek()); 353 354 String sqlComment = null; 355 try { 356 sqlComment = stmt.getCommentBeforeNode(); 357 } catch (Exception e) { 358 } 359 if (!SQLUtil.isEmpty(sqlComment) && (sqlComment.indexOf("db") != -1 || sqlComment.indexOf("schema") != -1)) { 360 Properties properties = new Properties(); 361 try { 362 properties.load( 363 new ByteArrayInputStream(sqlComment.replace("--", "").trim().replace(",", "\n").getBytes())); 364 if (SQLUtil.isEmpty(this.server) && properties.containsKey("db-instance")) { 365 this.server = properties.getProperty("db-instance"); 366 } 367 if (SQLUtil.isEmpty(this.database) && properties.containsKey("db")) { 368 this.database = properties.getProperty("db"); 369 if(this.database.indexOf(".")!=-1) { 370 String delimitedChar = TSQLEnv.delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 371 this.database = delimitedChar + SQLUtil.trimColumnStringQuote(this.database) + delimitedChar; 372 } 373 } 374 if (SQLUtil.isEmpty(this.schema) && properties.containsKey("schema")) { 375 this.schema = properties.getProperty("schema"); 376 if(this.schema.indexOf(".")!=-1) { 377 String delimitedChar = TSQLEnv.delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 378 this.schema = delimitedChar + SQLUtil.trimColumnStringQuote(this.schema) + delimitedChar; 379 } 380 } 381 } catch (IOException e) { 382 logger.error("load sql comment properties failed.", e); 383 } 384 } 385 386 if(stmt instanceof TCreateTableSqlStatement) { 387 TCreateTableSqlStatement createTableStmt = (TCreateTableSqlStatement)stmt; 388 if (createTableStmt.getTableKinds() != null 389 && (createTableStmt.getTableKinds().contains(ETableKind.etkTemporary) 390 || createTableStmt.getTableKinds().contains(ETableKind.etkTemp) 391 || createTableStmt.getTableKinds().contains(ETableKind.etkLocalTemporary) 392 || createTableStmt.getTableKinds().contains(ETableKind.etkLocalTemp) 393 || createTableStmt.getTableKinds().contains(ETableKind.etkGlobalTemporary) 394 || createTableStmt.getTableKinds().contains(ETableKind.etkGlobalTemp) 395 || createTableStmt.getTableKinds().contains(ETableKind.etkScopedTemporary))) { 396 if (this.tableObject == createTableStmt.getTargetTable()) { 397 setSubType(SubType.temp_table); 398 return; 399 } 400 } 401 } 402 403 if(currentStmt instanceof TCreateTableSqlStatement) { 404 TCreateTableSqlStatement createTableStmt = (TCreateTableSqlStatement)currentStmt; 405 if (createTableStmt.getTableKinds() != null 406 && (createTableStmt.getTableKinds().contains(ETableKind.etkTemporary) 407 || createTableStmt.getTableKinds().contains(ETableKind.etkTemp) 408 || createTableStmt.getTableKinds().contains(ETableKind.etkLocalTemporary) 409 || createTableStmt.getTableKinds().contains(ETableKind.etkLocalTemp) 410 || createTableStmt.getTableKinds().contains(ETableKind.etkGlobalTemporary) 411 || createTableStmt.getTableKinds().contains(ETableKind.etkGlobalTemp) 412 || createTableStmt.getTableKinds().contains(ETableKind.etkScopedTemporary))) { 413 if (this.tableObject == createTableStmt.getTargetTable()) { 414 setSubType(SubType.temp_table); 415 return; 416 } 417 } 418 } 419 420 if(stmt instanceof TCreateViewSqlStatement) { 421 TCreateViewSqlStatement createViewStmt = (TCreateViewSqlStatement)stmt; 422 if (createViewStmt.getTableKind() != null 423 && (createViewStmt.getTableKind() == (ETableKind.etkTemporary) 424 || createViewStmt.getTableKind() == (ETableKind.etkTemp) 425 || createViewStmt.getTableKind() == (ETableKind.etkLocalTemporary) 426 || createViewStmt.getTableKind() == (ETableKind.etkLocalTemp) 427 || createViewStmt.getTableKind() == (ETableKind.etkGlobalTemporary) 428 || createViewStmt.getTableKind() == (ETableKind.etkGlobalTemp))) { 429 if (this.viewName == createViewStmt.getViewName()) { 430 setSubType(SubType.temp_table); 431 return; 432 } 433 } 434 } 435 436 if(currentStmt instanceof TCreateViewSqlStatement) { 437 TCreateViewSqlStatement createViewStmt = (TCreateViewSqlStatement)currentStmt; 438 if (createViewStmt.getTableKind() != null 439 && (createViewStmt.getTableKind() == (ETableKind.etkTemporary) 440 || createViewStmt.getTableKind() == (ETableKind.etkTemp) 441 || createViewStmt.getTableKind() == (ETableKind.etkLocalTemporary) 442 || createViewStmt.getTableKind() == (ETableKind.etkLocalTemp) 443 || createViewStmt.getTableKind() == (ETableKind.etkGlobalTemporary) 444 || createViewStmt.getTableKind() == (ETableKind.etkGlobalTemp))) { 445 if (this.viewName == createViewStmt.getViewName()) { 446 setSubType(SubType.temp_table); 447 return; 448 } 449 } 450 } 451 } 452 453 454 if (SQLUtil.isEmpty(this.database) || this.database.equals(ModelBindingManager.getGlobalDatabase())) { 455 TSQLEnv sqlEnv = ModelBindingManager.getGlobalSQLEnv(); 456 if (sqlEnv == null) { 457 return; 458 } 459 int occurrence = 0; 460 int maxPriority = -1; 461 String tableDatabase = null; 462 String tableSchema = null; 463 Map<Integer, List<String>> databaseSchemasMap = new HashMap<Integer, List<String>>(); 464 // Every cross-schema hit for the bare name, grouped by priority, so a name 465 // that is ambiguous across schemas (and which the rules below cannot resolve 466 // to a single winner) can be surfaced as a candidate set instead of being 467 // stamped with a non-existent default schema. 468 Map<Integer, List<String[]>> candidatesByPriority = new TreeMap<Integer, List<String[]>>(); 469 for (TSQLCatalog catalog : sqlEnv.getCatalogList()) { 470 if (SQLUtil.isEmpty(schema) || this.schema.equals(ModelBindingManager.getGlobalSchema())) { 471 for (TSQLSchema schema : catalog.getSchemaList()) { 472 TSQLSchemaObject tsqlTable = schema.findTable(DlineageUtil.getSimpleTableName(this.name)); 473 if(tsqlTable == null){ 474 tsqlTable = schema.findSynonyms(DlineageUtil.getSimpleTableName(this.name)); 475 } 476 if (tsqlTable != null) { 477 occurrence += 1; 478 String candDb = quoteDottedSegment(catalog.getName()); 479 String candSchema = quoteDottedSegment(schema.getName()); 480 String candName = candDb + "." + candSchema + "." 481 + DlineageUtil.getSimpleTableName(this.name); 482 int candPriority = tsqlTable.getPriority(); 483 if (!candidatesByPriority.containsKey(candPriority)) { 484 candidatesByPriority.put(candPriority, new ArrayList<String[]>()); 485 } 486 candidatesByPriority.get(candPriority).add(new String[] { candDb, candSchema, candName }); 487 if (tsqlTable.getPriority() > maxPriority 488 || (maxPriority > 0 && tsqlTable.getPriority() == maxPriority)) { 489 tableDatabase = catalog.getName(); 490 if(tableDatabase.indexOf(".")!=-1) { 491 String delimitedChar = TSQLEnv.delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 492 tableDatabase = delimitedChar + SQLUtil.trimColumnStringQuote(tableDatabase) + delimitedChar; 493 } 494 tableSchema = schema.getName(); 495 if(tableSchema.indexOf(".")!=-1) { 496 String delimitedChar = TSQLEnv.delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 497 tableSchema = delimitedChar + SQLUtil.trimColumnStringQuote(tableSchema) + delimitedChar; 498 } 499 maxPriority = tsqlTable.getPriority(); 500 if(!databaseSchemasMap.containsKey(maxPriority)) { 501 databaseSchemasMap.put(maxPriority, new ArrayList<String>()); 502 } 503 databaseSchemasMap.get(maxPriority).add(tableDatabase + "___" + tableSchema); 504 } 505 } 506 } 507// if (occurrence > 1) { 508// break; 509// } 510 } else { 511 TSQLSchema schema = catalog.getSchema(this.schema, false); 512 if (schema != null) { 513 TSQLSchemaObject tsqlTable = schema.findTable(DlineageUtil.getSimpleTableName(this.name)); 514 if(tsqlTable == null){ 515 tsqlTable = schema.findSynonyms(DlineageUtil.getSimpleTableName(this.name)); 516 } 517 if (tsqlTable != null) { 518 occurrence += 1; 519 if (tsqlTable.getPriority() > maxPriority 520 || (maxPriority > 0 && tsqlTable.getPriority() == maxPriority)) { 521 tableDatabase = catalog.getName(); 522 if(tableDatabase.indexOf(".")!=-1) { 523 String delimitedChar = TSQLEnv.delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 524 tableDatabase = delimitedChar + SQLUtil.trimColumnStringQuote(tableDatabase) + delimitedChar; 525 } 526 tableSchema = schema.getName(); 527 if(tableSchema.indexOf(".")!=-1) { 528 String delimitedChar = TSQLEnv.delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 529 tableSchema = delimitedChar + SQLUtil.trimColumnStringQuote(tableSchema) + delimitedChar; 530 } 531 maxPriority = tsqlTable.getPriority(); 532 if(!databaseSchemasMap.containsKey(maxPriority)) { 533 databaseSchemasMap.put(maxPriority, new ArrayList<String>()); 534 } 535 databaseSchemasMap.get(maxPriority).add(tableDatabase + "___" + tableSchema); 536 } 537 } 538 } 539 } 540 } 541 if (occurrence == 1 || (maxPriority > 0 && databaseSchemasMap.get(maxPriority).size() == 1)) { 542 if (this.database == null && tableDatabase != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(tableDatabase)) { 543 this.database = tableDatabase; 544 } 545 if (this.schema == null && tableSchema != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(tableSchema)) { 546 this.schema = tableSchema; 547 } 548 } else if ((this.schema == null || this.schema.equals(ModelBindingManager.getGlobalSchema())) 549 && !candidatesByPriority.isEmpty()) { 550 // The existing rules could not pick a single winner and the schema is 551 // still only a default (null or the global default). Look at the 552 // top-priority group: when it holds two or more schemas AND none of 553 // them is the default schema, the unqualified name is genuinely 554 // ambiguous. Flag it and record the candidate set so the output layer 555 // can surface the reference unqualified instead of fabricating a 556 // non-existent default-schema object. A match in the default schema 557 // (e.g. an existing dbo.orders) is left to win through the normal 558 // stamping path. 559 int maxCandPriority = Collections.max(candidatesByPriority.keySet()); 560 List<String[]> topCandidates = candidatesByPriority.get(maxCandPriority); 561 String defaultSchema = getDefaultSchema(); 562 boolean defaultSchemaHasObject = false; 563 for (String[] cand : topCandidates) { 564 if (cand[1] != null && cand[1].equalsIgnoreCase(defaultSchema)) { 565 defaultSchemaHasObject = true; 566 break; 567 } 568 } 569 if (!defaultSchemaHasObject && topCandidates.size() >= 2) { 570 List<String> candidates = new ArrayList<String>(); 571 for (String[] cand : topCandidates) { 572 candidates.add(cand[2]); 573 } 574 this.candidateTables = candidates; 575 this.ambiguousUnqualifiedTable = true; 576 } 577 } 578 } else if (SQLUtil.isEmpty(this.schema) || this.schema.equals(ModelBindingManager.getGlobalSchema())) { 579 TSQLEnv sqlEnv = ModelBindingManager.getGlobalSQLEnv(); 580 if (sqlEnv == null) { 581 return; 582 } 583 int occurrence = 0; 584 int maxPriority = -1; 585 String tableDatabase = null; 586 String tableSchema = null; 587 Map<Integer, List<String>> databaseSchemasMap = new HashMap<Integer, List<String>>(); 588 TSQLCatalog catalog = sqlEnv.searchCatalog(this.database); 589 if (catalog != null) { 590 for (TSQLSchema schema : catalog.getSchemaList()) { 591 TSQLSchemaObject tsqlTable = schema.findTable(DlineageUtil.getSimpleTableName(this.name)); 592 if(tsqlTable == null){ 593 tsqlTable = schema.findSynonyms(DlineageUtil.getSimpleTableName(this.name)); 594 } 595 if (tsqlTable != null) { 596 occurrence += 1; 597 if (tsqlTable.getPriority() > maxPriority 598 || (maxPriority > 0 && tsqlTable.getPriority() == maxPriority)) { 599 tableDatabase = catalog.getName(); 600 if (tableDatabase.indexOf(".") != -1) { 601 String delimitedChar = TSQLEnv 602 .delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 603 tableDatabase = delimitedChar + SQLUtil.trimColumnStringQuote(tableDatabase) 604 + delimitedChar; 605 } 606 tableSchema = schema.getName(); 607 if (tableSchema.indexOf(".") != -1) { 608 String delimitedChar = TSQLEnv 609 .delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 610 tableSchema = delimitedChar + SQLUtil.trimColumnStringQuote(tableSchema) 611 + delimitedChar; 612 } 613 maxPriority = tsqlTable.getPriority(); 614 if(!databaseSchemasMap.containsKey(maxPriority)) { 615 databaseSchemasMap.put(maxPriority, new ArrayList<String>()); 616 } 617 databaseSchemasMap.get(maxPriority).add(tableDatabase + "___" + tableSchema); 618 } 619 } 620 } 621 } 622 623 if (occurrence == 1 || (maxPriority > 0 && databaseSchemasMap.get(maxPriority).size() == 1)) { 624 if (this.database == null && tableDatabase != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(tableDatabase)) { 625 this.database = tableDatabase; 626 } 627 if (this.schema == null && tableSchema != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(tableSchema)) { 628 this.schema = tableSchema; 629 } 630 } 631 632 if (SQLUtil.isEmpty(this.schema) && !SQLUtil.isEmpty(this.database)) { 633 this.schema = getDefaultSchema(); 634 } 635 } 636 } 637 638 public Table(TObjectName tableName) { 639 if (tableName == null) { 640 throw new IllegalArgumentException("Table arguments can't be null."); 641 } 642 643 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 644 645 this.viewName = tableName; 646 647 setPosition(tableName); 648 649 this.fullName = tableName.toString(); 650 this.name = tableName.toString(); 651 if (SQLUtil.isEmpty(name)) { 652 name = tableName.toString(); 653 } 654 655 if (name.startsWith("`") && name.endsWith("`") && name.indexOf(".") != -1 656 && ModelBindingManager.getGlobalOption().getVendor() == EDbVendor.dbvbigquery) { 657 this.name = SQLUtil.trimColumnStringQuote(name); 658 this.fullName = SQLUtil.trimColumnStringQuote(fullName); 659 } 660 661 this.server = DlineageUtil.getTableServer(tableName.toString()); 662 this.database = DlineageUtil.getTableDatabase(tableName.toString()); 663 this.schema = DlineageUtil.getTableSchema(tableName.toString()); 664 665 fillSchemaInfo(); 666 667 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 668 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 669 boolean supportSchema = TSQLEnv.supportSchema(vendor); 670 671 if (supportCatalog && SQLUtil.isEmpty(this.database)) { 672 this.database = DlineageUtil.getTableDatabase(tableName.toString()); 673 if (SQLUtil.isEmpty(this.database) && ModelBindingManager.getGlobalOption().isShowImplicitSchema() 674 && !SQLUtil.isEmpty(tableName.getImplictDatabaseString())) { 675 this.database = tableName.getImplictDatabaseString(); 676 } 677 } 678 679 if (supportSchema && SQLUtil.isEmpty(this.schema)) { 680 this.schema = DlineageUtil.getTableSchema(tableName.toString()); 681 if (SQLUtil.isEmpty(this.schema) && ModelBindingManager.getGlobalOption().isShowImplicitSchema() 682 && !SQLUtil.isEmpty(tableName.getImplictSchemaString())) { 683 this.schema = tableName.getImplictSchemaString(); 684 } 685 } 686 687 if (supportSchema && !SQLUtil.isEmpty(this.database) && SQLUtil.isEmpty(this.schema)) { 688 if (!SQLUtil.isEmpty(tableName.getImplictSchemaString())) { 689 this.schema = tableName.getImplictSchemaString(); 690 } else { 691 this.schema = getDefaultSchema(); 692 } 693 } 694 695 if (!supportCatalog) { 696 this.database = null; 697 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 698 this.database = getDefaultDatabase(); 699 } 700 701 if (!supportSchema) { 702 this.schema = null; 703 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 704 this.schema = getDefaultSchema(); 705 } 706 707 updateTableName(supportCatalog, supportSchema); 708 709 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 710 this.server = getDefaultServer(); 711 } 712 } 713 714 protected String getDefaultServer() { 715 String defaultServer = null; 716 if (ModelBindingManager.getGlobalSQLEnv() != null) { 717 defaultServer = ModelBindingManager.getGlobalSQLEnv().getDefaultServerName(); 718 } 719 if (!SQLUtil.isEmpty(defaultServer)) 720 return defaultServer; 721 return TSQLEnv.DEFAULT_SERVER_NAME; 722 } 723 724 protected String getDefaultSchema() { 725 String defaultSchema = null; 726 if (ModelBindingManager.getGlobalSQLEnv() != null) { 727 defaultSchema = ModelBindingManager.getGlobalSQLEnv().getDefaultSchemaName(); 728 } 729 if (!SQLUtil.isEmpty(defaultSchema)) 730 return defaultSchema; 731 return TSQLEnv.DEFAULT_SCHEMA_NAME; 732 } 733 734 protected String getDefaultDatabase() { 735 return ModelBindingManager.getEffectiveDefaultDatabase(); 736 } 737 738 /** 739 * @return true when this one-part reference is ambiguous across two or more 740 * schemas and was intentionally left unqualified (no default schema 741 * stamped). The candidates are available via {@link #getCandidateTables()}. 742 */ 743 public boolean isAmbiguousUnqualifiedTable() { 744 return ambiguousUnqualifiedTable; 745 } 746 747 /** 748 * @return the qualified candidate table names (e.g. {@code testdb.s1.orders}, 749 * {@code testdb.s2.orders}) when this reference is ambiguous across 750 * schemas, or null when the reference resolved normally. 751 */ 752 public List<String> getCandidateTables() { 753 return candidateTables; 754 } 755 756 /** 757 * Quote a catalog/schema segment the same way the surrounding resolution code 758 * does (lines around the occurrence loop), so a name containing a dot is kept 759 * as a single delimited segment. 760 */ 761 private static String quoteDottedSegment(String segment) { 762 if (segment != null && segment.indexOf('.') != -1) { 763 String delimitedChar = TSQLEnv.delimitedChar(ModelBindingManager.getGlobalOption().getVendor()); 764 return delimitedChar + SQLUtil.trimColumnStringQuote(segment) + delimitedChar; 765 } 766 return segment; 767 } 768 769 public Table(String tableName) { 770 if (tableName == null) { 771 throw new IllegalArgumentException("Table arguments can't be null."); 772 } 773 774 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 775 776 this.startPosition = new Pair3<Long, Long, String>(-1L, -1L, ModelBindingManager.getGlobalHash()); 777 this.endPosition = new Pair3<Long, Long, String>(-1L, -1L, ModelBindingManager.getGlobalHash()); 778 779 this.fullName = tableName; 780 this.name = tableName; 781 if (SQLUtil.isEmpty(name)) { 782 name = tableName; 783 } 784 785 if (name.startsWith("`") && name.endsWith("`") && name.indexOf(".") != -1 786 && ModelBindingManager.getGlobalOption().getVendor() == EDbVendor.dbvbigquery) { 787 this.name = SQLUtil.trimColumnStringQuote(name); 788 this.fullName = SQLUtil.trimColumnStringQuote(fullName); 789 } 790 791 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 792 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 793 boolean supportSchema = TSQLEnv.supportSchema(vendor); 794 795 fillSchemaInfo(); 796 797 this.server = DlineageUtil.getTableServer(tableName); 798 799 if (supportCatalog && SQLUtil.isEmpty(this.database)) { 800 this.database = DlineageUtil.getTableDatabase(tableName); 801 } 802 if (supportSchema && SQLUtil.isEmpty(this.schema)) { 803 this.schema = DlineageUtil.getTableSchema(tableName); 804 } 805 if (supportSchema && !SQLUtil.isEmpty(this.database) && SQLUtil.isEmpty(this.schema)) { 806 this.schema = getDefaultSchema(); 807 } 808 809 if (!supportCatalog) { 810 this.database = null; 811 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 812 this.database = getDefaultDatabase(); 813 } 814 815 if (!supportSchema) { 816 this.schema = null; 817 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 818 this.schema = getDefaultSchema(); 819 } 820 821 updateTableName(supportCatalog, supportSchema); 822 823 if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) { 824 this.server = getDefaultServer(); 825 } 826 } 827 828 protected void updateTableName(boolean supportCatalog, boolean supportSchema) { 829 List<String> segments = SQLUtil.parseNames(this.name); 830 this.name = segments.get(segments.size() - 1); 831 if (supportCatalog && supportSchema) { 832 StringBuilder builder = new StringBuilder(); 833 if (segments.size() > 2) { 834 builder.append(segments.get(segments.size() - 3)).append("."); 835 } else { 836 if (!SQLUtil.isEmpty(this.database) && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(this.database)) { 837 builder.append(this.database).append("."); 838 } 839 } 840 if (segments.size() > 1) { 841 builder.append(segments.get(segments.size() - 2)).append("."); 842 } else { 843 if (builder.length() > 0) { 844 if (this.schema == null) { 845 if (ModelBindingManager.getGlobalVendor() == EDbVendor.dbvmssql) { 846 this.schema = "dbo"; 847 } else { 848 this.schema = getDefaultSchema(); 849 } 850 } 851 else if (TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(this.schema) 852 && ModelBindingManager.getGlobalVendor() == EDbVendor.dbvmssql) { 853 this.schema = "dbo"; 854 } 855 builder.append(this.schema).append("."); 856 } else { 857 if (!SQLUtil.isEmpty(this.schema) && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(this.schema)) { 858 builder.append(this.schema).append("."); 859 } 860 } 861 } 862 builder.append(this.name); 863 this.name = builder.toString(); 864 } else if (supportCatalog) { 865 if (segments.size() > 1) { 866 this.name = segments.get(segments.size() - 2) + "." + this.name; 867 } 868 else { 869 if (!SQLUtil.isEmpty(this.database) && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(this.database)) { 870 this.name = this.database + "." + this.name; 871 } 872 } 873 } else if (supportSchema) { 874 if (segments.size() > 1) { 875 this.name = segments.get(segments.size() - 2) + "." + this.name; 876 } else { 877 if (!SQLUtil.isEmpty(this.schema) && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(this.schema)) { 878 this.name = this.schema + "." + this.name; 879 } 880 } 881 } 882 883 if (this.server != null && !this.server.equals(getDefaultServer())) { 884 this.name = this.server + "." + this.name; 885 } 886 } 887 888 public long getId() { 889 return id; 890 } 891 892 public String getName() { 893 return name; 894 } 895 896 public void setName(String name) { 897 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 898 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 899 boolean supportSchema = TSQLEnv.supportSchema(vendor); 900 if (!supportCatalog) { 901 this.database = null; 902 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 903 this.database = getDefaultDatabase(); 904 } 905 906 if (!supportSchema) { 907 this.schema = null; 908 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 909 this.schema = getDefaultSchema(); 910 } 911 912 this.name = name; 913 914 updateTableName(supportCatalog, supportSchema); 915 } 916 917 public Pair3<Long, Long, String> getStartPosition() { 918 return startPosition; 919 } 920 921 public Pair3<Long, Long, String> getEndPosition() { 922 return endPosition; 923 } 924 925 public List<TableColumn> getColumns() { 926 return columns; 927 } 928 929 public void addColumn(TableColumn column) { 930 if (column != null && !this.columns.contains(column)) { 931 this.columns.add(column); 932 } 933 } 934 935 public TTable getTableObject() { 936 return tableObject; 937 } 938 939 public String getAlias() { 940 String aliasString = Arrays.toString(this.alias.toArray(new String[0])); 941 return aliasString.substring(1, aliasString.length() - 1); 942 } 943 944 public void setAlias(String alias) { 945 if (!SQLUtil.isEmpty(alias)) { 946 this.alias.add(alias); 947 } 948 } 949 950 public String getFullName() { 951 return fullName; 952 } 953 954 public boolean hasSubquery() { 955 return subquery; 956 } 957 958 public String getParent() { 959 return parent; 960 } 961 962 public void setParent(String parent) { 963 this.parent = parent; 964 } 965 966// public TObjectName getTableName() { 967// return tableName; 968// } 969 970 public String getDatabase() { 971 return database; 972 } 973 974 public String getSchema() { 975 return schema; 976 } 977 978 public TableRelationRows getRelationRows() { 979 return relationRows; 980 } 981 982 public boolean isCreateTable() { 983 return isCreateTable; 984 } 985 986 public void setCreateTable(boolean isCreateTable, boolean fromDDL) { 987 Stack<TCustomSqlStatement> stmtStack = ModelBindingManager.getGlobalStmtStack(); 988 TCustomSqlStatement stmt = null; 989 if (stmtStack != null && !stmtStack.isEmpty()) { 990 stmt = stmtStack.peek(); 991 } 992 if (stmt != null && stmt.getClass().getSimpleName().startsWith("TCreate")) { 993 Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>> position = setPosition(stmt); 994 if (position != null) { 995 this.startPosition = position.first; 996 this.endPosition = position.second; 997 } 998 } 999 this.isCreateTable = isCreateTable; 1000 if (isCreateTable) { 1001 for (TableColumn column : columns) { 1002 column.setShowStar(false); 1003 } 1004 setDetermined(true); 1005 if (fromDDL) { 1006 setFromDDL(true); 1007 } 1008 } 1009 } 1010 1011 public void setCreateTable(boolean isCreateTable) { 1012 setCreateTable(isCreateTable, false); 1013 } 1014 1015 public boolean isView() { 1016 return isView; 1017 } 1018 1019 public void setView(boolean isView) { 1020 this.isView = isView; 1021 } 1022 1023 public boolean isPseudo() { 1024 return isPseudo; 1025 } 1026 1027 public void setPseudo(boolean isPseudo) { 1028 this.isPseudo = isPseudo; 1029 } 1030 1031 public boolean isExternal() { 1032 return isExternal; 1033 } 1034 1035 public void setExternal(boolean isExternal) { 1036 this.isExternal = isExternal; 1037 } 1038 1039 public boolean isStage() { 1040 return isStage; 1041 } 1042 1043 public void setStage(boolean isStage) { 1044 this.isStage = isStage; 1045 } 1046 1047 public boolean isSequence() { 1048 return isSequence; 1049 } 1050 1051 public void setSequence(boolean sequence) { 1052 isSequence = sequence; 1053 } 1054 1055 public boolean isVariable() { 1056 return isVariable; 1057 } 1058 1059 public void setVariable(boolean isVariable) { 1060 this.isVariable = isVariable; 1061 } 1062 1063 public boolean isConstant() { 1064 return isConstant; 1065 } 1066 1067 public void setConstant(boolean isConstant) { 1068 this.isConstant = isConstant; 1069 } 1070 1071 public boolean isPath() { 1072 return isPath; 1073 } 1074 1075 public void setPath(boolean isPath) { 1076 this.isPath = isPath; 1077 if(isPath) { 1078 String filePathDatabase = ModelBindingManager.getGlobalOption().getFilePathDatabase(); 1079 String filePathSchema = ModelBindingManager.getGlobalOption().getFilePathSchema(); 1080 if(SQLUtil.isEmpty(filePathDatabase)) { 1081 this.database = null; 1082 } 1083 else { 1084 this.database = filePathDatabase; 1085 } 1086 if(SQLUtil.isEmpty(filePathSchema)) { 1087 this.schema = null; 1088 } 1089 else { 1090 this.schema = filePathSchema; 1091 } 1092 1093 EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor(); 1094 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 1095 boolean supportSchema = TSQLEnv.supportSchema(vendor); 1096 if (!supportCatalog) { 1097 this.database = null; 1098 } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) { 1099 this.database = getDefaultDatabase(); 1100 } 1101 1102 if (!supportSchema) { 1103 this.schema = null; 1104 } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) { 1105 this.schema = getDefaultSchema(); 1106 } 1107 1108 updateTableName(supportCatalog, supportSchema); 1109 } 1110 } 1111 1112 public boolean isCursor() { 1113 return isVariable && getSubType() == SubType.cursor; 1114 } 1115 1116 public String getLocation() { 1117 return location; 1118 } 1119 1120 public void setLocation(String location) { 1121 this.location = location; 1122 } 1123 1124 public String getFileType() { 1125 return fileType; 1126 } 1127 1128 public void setFileType(String fileType) { 1129 this.fileType = fileType; 1130 } 1131 1132 public String getDisplayName() { 1133 return displayName; 1134 } 1135 1136 public void setDisplayName(String displayName) { 1137 this.displayName = displayName; 1138 } 1139 1140 public String getFileFormat() { 1141 return fileFormat; 1142 } 1143 1144 public void setFileFormat(String fileFormat) { 1145 this.fileFormat = fileFormat; 1146 } 1147 1148 public List<Process> getProcesses() { 1149 return processes; 1150 } 1151 1152 public void addProcess(Process process) { 1153 if (processes == null) { 1154 processes = new ArrayList<Process>(); 1155 } 1156 if (process != null && !processes.contains(process)) { 1157 processes.add(process); 1158 } 1159 } 1160 1161 public void removeProcess(Process process) { 1162 if (processes == null) { 1163 processes = new ArrayList<Process>(); 1164 } 1165 if (process != null && processes.contains(process)) { 1166 processes.remove(process); 1167 } 1168 } 1169 1170 public void setSubType(SubType subType) { 1171 if (this.subType == null || subType == SubType.record_type) { 1172 this.subType = subType; 1173 } 1174 } 1175 1176 public SubType getSubType() { 1177 return subType; 1178 } 1179 1180 public boolean isStream() { 1181 return isStream; 1182 } 1183 1184 public void setStream(boolean isStream) { 1185 this.isStream = isStream; 1186 } 1187 1188 public boolean isSchema() { 1189 return isSchema; 1190 } 1191 1192 public void setSchema(boolean isSchema) { 1193 this.isSchema = isSchema; 1194 } 1195 1196 public void setDatabase(String database) { 1197 this.database = database; 1198 } 1199 1200 public boolean isDatabase() { 1201 return isDatabase; 1202 } 1203 1204 public void setDatabase(boolean isDatabase) { 1205 this.isDatabase = isDatabase; 1206 } 1207 1208 public boolean isDataSource() { 1209 return isDataSource; 1210 } 1211 1212 public void setDataSource(boolean dataSource) { 1213 isDataSource = dataSource; 1214 } 1215 1216 public String getStarStmt() { 1217 return starStmt; 1218 } 1219 1220 public void setStarStmt(String starStmt) { 1221 this.starStmt = starStmt; 1222 } 1223 1224 public void setHasSQLEnv(boolean hasSQLEnv) { 1225 this.hasSQLEnv = hasSQLEnv; 1226 } 1227 1228 public boolean hasSQLEnv() { 1229 return hasSQLEnv; 1230 } 1231 1232 public String getCurrentAlias() { 1233 return currentAlias; 1234 } 1235 1236 public void setCurrentAlias(String currentAlias) { 1237 if (SQLUtil.isEmpty(currentAlias)) { 1238 this.currentAlias = null; 1239 } else 1240 this.currentAlias = currentAlias; 1241 } 1242 1243 public Set<String> getAliasList() { 1244 return alias; 1245 } 1246 1247 public String getServer() { 1248 return server; 1249 } 1250 1251 public boolean isDetermined() { 1252 return isDetermined; 1253 } 1254 1255 public void setDetermined(boolean isDetermined) { 1256 this.isDetermined = isDetermined; 1257 } 1258 1259 public boolean isFromDDL() { 1260 return fromDDL; 1261 } 1262 1263 public void setFromDDL(boolean fromDDL) { 1264 this.fromDDL = fromDDL; 1265 } 1266 1267 /** 1268 * Whether this endpoint is created by the analyzed SQL. A parse fact, NOT gated on 1269 * {@link #isDetermined()}; true for CREATE TABLE / SELECT INTO / CTAS / clone 1270 * targets even when the defining query's schema could not be resolved. 1271 */ 1272 public boolean isCreatedInSql() { 1273 return createdInSql; 1274 } 1275 1276 public void setCreatedInSql(boolean createdInSql) { 1277 this.createdInSql = createdInSql; 1278 } 1279 1280 public EndpointIntroduction getEndpointIntroduction() { 1281 return endpointIntroduction; 1282 } 1283 1284 /** 1285 * Records how the endpoint was introduced. First non-UNKNOWN writer wins, so the 1286 * structural creation site (set early at build time) is not later clobbered. 1287 */ 1288 public void setEndpointIntroduction(EndpointIntroduction endpointIntroduction) { 1289 if (endpointIntroduction == null) { 1290 return; 1291 } 1292 if (this.endpointIntroduction == null || this.endpointIntroduction == EndpointIntroduction.UNKNOWN) { 1293 this.endpointIntroduction = endpointIntroduction; 1294 } 1295 } 1296 1297 /** 1298 * Existing schema/model resolution state, exposed under the endpoint-classification 1299 * name. Mirrors {@link #isDetermined()} exactly; introduces no new resolution logic. 1300 */ 1301 public boolean isResolved() { 1302 return isDetermined; 1303 } 1304 1305 /** 1306 * Delimiter-normalized, dialect-aware form of {@link #getName()} (brackets / quotes 1307 * stripped per identifier segment). Computed on access from the current name; does 1308 * NOT mutate {@link #getName()}, which stays raw. Returns the raw name when no 1309 * normalization is possible (e.g. no global vendor context). 1310 */ 1311 public String getNormalizedName() { 1312 if (name == null) { 1313 return null; 1314 } 1315 try { 1316 String normalized = DlineageUtil.getIdentifierNormalTableName(name); 1317 return normalized == null ? name : normalized; 1318 } catch (Exception e) { 1319 return name; 1320 } 1321 } 1322 1323 /** 1324 * Authoritative {@link EndpointKind}, computed on access from the current (raw) 1325 * name and the active dialect. Quote-robust: each identifier segment is delimiter- 1326 * stripped before the {@code #}/{@code ##}/{@code @}/{@code tempdb} tests. This 1327 * reads only the new classification; it never inspects or mutates {@link #subType}. 1328 * 1329 * <p>§5.6 minimum scope: distinguishes T-SQL temp / global-temp / table-variable / 1330 * tempdb objects from plain catalog objects. CTE / derived / TVF / constant kinds 1331 * are deferred and reported as {@link EndpointKind#UNKNOWN} here. 1332 */ 1333 public EndpointKind getEndpointKind() { 1334 if (SQLUtil.isEmpty(name)) { 1335 return EndpointKind.UNKNOWN; 1336 } 1337 1338 EDbVendor vendor = null; 1339 if (ModelBindingManager.getGlobalOption() != null) { 1340 vendor = ModelBindingManager.getGlobalOption().getVendor(); 1341 } 1342 1343 if (vendor == EDbVendor.dbvmssql || vendor == EDbVendor.dbvazuresql || vendor == EDbVendor.dbvsybase) { 1344 List<String> segments = SQLUtil.parseNames(name); 1345 if (!segments.isEmpty()) { 1346 String last = SQLUtil.trimColumnStringQuote(segments.get(segments.size() - 1).trim()); 1347 if (last.startsWith("##")) { 1348 return EndpointKind.GLOBAL_TEMP_TABLE; 1349 } 1350 if (last.startsWith("#")) { 1351 return EndpointKind.LOCAL_TEMP_TABLE; 1352 } 1353 if (last.startsWith("@")) { 1354 return EndpointKind.TABLE_VARIABLE; 1355 } 1356 String first = SQLUtil.trimColumnStringQuote(segments.get(0).trim()); 1357 if (segments.size() > 1 && "tempdb".equalsIgnoreCase(first)) { 1358 return EndpointKind.TEMPDB_OBJECT; 1359 } 1360 } 1361 } 1362 1363 // Only a plain real table is a catalog object here. Resultsets / derived / 1364 // views / variables / stages etc. are deferred kinds — leave them UNKNOWN 1365 // rather than mislabel them CATALOG_OBJECT. 1366 if (!subquery && !isView && !isPseudo && !isStage && !isSequence && !isStream 1367 && !isVariable && !isConstant && !isDatabase && !isSchema && !isDataSource 1368 && subType != SubType.alias_table && subType != SubType.values_table 1369 && subType != SubType.synonym && subType != SubType.function 1370 && subType != SubType.unnest) { 1371 return EndpointKind.CATALOG_OBJECT; 1372 } 1373 1374 return EndpointKind.UNKNOWN; 1375 } 1376 1377 private Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>> setPositionInternal(TSourceToken startToken, TSourceToken endToken) { 1378 if (startToken == null || endToken == null) { 1379 return null; 1380 } 1381 1382 Pair3<Long, Long, String> startPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 1383 ModelBindingManager.getGlobalHash()); 1384 Pair3<Long, Long, String> endPosition = new Pair3<Long, Long, String>(endToken.lineNo, endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), 1385 ModelBindingManager.getGlobalHash()); 1386 1387 if(this.startPosition == null && this.endPosition == null) { 1388 this.startPosition = startPosition; 1389 this.endPosition = endPosition; 1390 } 1391 1392 boolean traceTablePosition = ModelBindingManager.getGlobalOption().isTraceTablePosition(); 1393 1394 1395 Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>> position = Pair.create(startPosition, endPosition); 1396 1397 if (traceTablePosition) { 1398 positions.add(position); 1399 } 1400 1401 return position; 1402 } 1403 1404 public Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>> setPosition(TTable table) { 1405 if (table == null) { 1406 return null; 1407 } 1408 1409 TSourceToken startToken = table.getStartToken(); 1410 TSourceToken endToken = table.getEndToken(); 1411 if (startToken == null && table.getSubquery() != null) { 1412 startToken = table.getSubquery().getStartToken(); 1413 endToken = table.getSubquery().getEndToken(); 1414 } 1415 1416 return setPositionInternal(startToken, endToken); 1417 } 1418 1419 public Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>> setPosition(TObjectName tableName) { 1420 if (tableName == null) { 1421 return null; 1422 } 1423 1424 TSourceToken startToken = tableName.getStartToken(); 1425 TSourceToken endToken = tableName.getEndToken(); 1426 1427 return setPositionInternal(startToken, endToken); 1428 } 1429 1430 public Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>> setPosition(TCustomSqlStatement stmt) { 1431 if (stmt == null) { 1432 return null; 1433 } 1434 1435 TSourceToken startToken = stmt.getStartToken(); 1436 TSourceToken endToken = stmt.getEndToken(); 1437 1438 return setPositionInternal(startToken, endToken); 1439 } 1440 1441 public Set<Pair<Pair3<Long, Long, String>, Pair3<Long, Long, String>>> getPositions() { 1442 return positions; 1443 } 1444 1445 public TableColumn findColumn(String columnName) { 1446 if (columns == null) { 1447 return null; 1448 } 1449 for (TableColumn column : columns) { 1450 if (DlineageUtil.compareColumnIdentifier(column.getName(), columnName)) { 1451 return column; 1452 } 1453 } 1454 return null; 1455 } 1456 1457 public void removeColumn(TableColumn column) { 1458 if (columns != null && columns.contains(column)) { 1459 columns.remove(column); 1460 } 1461 } 1462 1463 public void removeColumn(String columnName) { 1464 TableColumn column = findColumn(columnName); 1465 if (column != null) { 1466 removeColumn(column); 1467 } 1468 } 1469}