001package gudusoft.gsqlparser.dlineage.dataflow.metadata.sqlenv; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.dlineage.dataflow.metadata.MetadataAnalyzer; 005import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager; 006import gudusoft.gsqlparser.dlineage.dataflow.model.xml.*; 007import gudusoft.gsqlparser.dlineage.util.DlineageUtil; 008import gudusoft.gsqlparser.dlineage.util.Pair3; 009import gudusoft.gsqlparser.sqlenv.*; 010import gudusoft.gsqlparser.util.SQLUtil; 011 012import java.util.*; 013 014public class SQLEnvMetadataAnalyzer implements MetadataAnalyzer<TSQLEnv> { 015 016 private Map<String, procedure> procedureMap = new LinkedHashMap<String, procedure>(); 017 private Map<String, table> tableMap = new LinkedHashMap<String, table>(); 018 private Map<String, oraclePackage> oraclePackageMap = new LinkedHashMap<String, oraclePackage>(); 019 private Map<String, column> columnMap = new LinkedHashMap<String, column>(); 020 private EDbVendor vendor; 021 private boolean lower = false; 022 023 public SQLEnvMetadataAnalyzer(boolean lower) { 024 this.lower = lower; 025 } 026 027 @Override 028 public synchronized dataflow analyzeMetadata(EDbVendor metadataVendor, TSQLEnv sqlenv) { 029 init(vendor); 030 dataflow dataflow = new dataflow(); 031 032 String serverName = sqlenv.getDefaultServerName(); 033 EDbVendor vendor = metadataVendor; 034 if (sqlenv.getDBVendor() != null) { 035 vendor = sqlenv.getDBVendor(); 036 } 037 038 boolean supportsCatalogs = TSQLEnv.supportCatalog(vendor); 039 boolean supportsSchemas = TSQLEnv.supportSchema(vendor); 040 041 List<TSQLCatalog> databases = sqlenv.getCatalogList(); 042 if (databases != null) { 043 for (int i = 0; i < databases.size(); i++) { 044 TSQLCatalog catalog = databases.get(i); 045 String databaseName = catalog.getName(); 046 if (SQLUtil.parseNames(databaseName).size() > 1) { 047 databaseName = "\"" + databaseName + "\""; 048 } 049 List<TSQLSchema> schemas = catalog.getSchemaList(); 050 if (schemas == null) { 051 continue; 052 } 053 for (int j = 0; j < schemas.size(); j++) { 054 TSQLSchema jsonSchema = schemas.get(j); 055 String schemaName = (String) jsonSchema.getName(); 056 if (SQLUtil.parseNames(schemaName).size() > 1) { 057 schemaName = "\"" + schemaName + "\""; 058 } 059 List<TSQLSchemaObject> dbObjs = jsonSchema.getSchemaObjectList(); 060 if (dbObjs == null) { 061 continue; 062 } 063 for (int k = 0; k < dbObjs.size(); k++) { 064 TSQLSchemaObject jsonTable = dbObjs.get(k); 065 if (jsonTable instanceof TSQLTable) { 066 TSQLTable table = (TSQLTable) jsonTable; 067 String tableName = table.getName(); 068 boolean isView = table.isView(); 069 List<TSQLColumn> columns = table.getColumnList(); 070 if (columns == null) { 071 continue; 072 } 073 for (int l = 0; l < columns.size(); l++) { 074 TSQLColumn jsonColumn = columns.get(l); 075 String columnName = jsonColumn.getName(); 076 if (SQLUtil.isEmpty(columnName)) { 077 continue; 078 } 079 appendTable(vendor, supportsCatalogs, supportsSchemas, dataflow, serverName, databaseName, schemaName, tableName, isView, columnName, 080 storedTableIdentity(sqlenv, vendor, supportsCatalogs, supportsSchemas, catalog.getName(), jsonSchema.getName(), tableName)); 081 if ("*".equals(columnName) && !jsonColumn.isUnresolvedStar()) { 082 // a catalog column named *, not the star lineage writes back for an unresolved width 083 column published = columnMap.get(getFullName(serverName, databaseName, schemaName, tableName, columnName)); 084 if (published != null) { 085 published.setStar(Boolean.FALSE); 086 } 087 } 088 } 089 } 090 if (jsonTable instanceof TSQLOraclePackage) { 091 TSQLOraclePackage sqlOraclePackage = (TSQLOraclePackage) jsonTable; 092 oraclePackage oraclePackage = appendOraclePackage(vendor, supportsCatalogs, supportsSchemas, dataflow, serverName, databaseName, schemaName, sqlOraclePackage); 093 if (sqlOraclePackage.getSchemaObjectList() != null) { 094 for (TSQLSchemaObject sqlProcedure : sqlOraclePackage.getSchemaObjectList()) { 095 appendProcedure(vendor, supportsCatalogs, supportsSchemas, dataflow, serverName, databaseName, schemaName, (TSQLSchemaObject) sqlProcedure, oraclePackage); 096 } 097 } 098 } 099 if(jsonTable instanceof TSQLProcedure){ 100 appendProcedure(vendor, supportsCatalogs, supportsSchemas, dataflow, serverName, databaseName, schemaName, (TSQLSchemaObject)jsonTable, null); 101 } 102 } 103 } 104 } 105 } 106 sortTableColumns(dataflow); 107 return dataflow; 108 } 109 110 private void appendProcedure(EDbVendor vendor, boolean supportsCatalogs, boolean supportsSchemas, dataflow dataflow, String serverName, String databaseName, String schemaName, TSQLSchemaObject sqlProcedure, oraclePackage oraclePackage) { 111 String qualifiedName = sqlProcedure.getQualifiedName(); 112 if (DlineageUtil.isProcedureExcluded(qualifiedName)) { 113 return; 114 } 115 String procedureName = sqlProcedure.getName(); 116 String procedureKey = getFullName(serverName, databaseName, schemaName, procedureName); 117 if (!procedureMap.containsKey(procedureKey)) { 118 procedure procedure = new procedure(); 119 if (serverName != null && !TSQLEnv.DEFAULT_SERVER_NAME.equalsIgnoreCase(serverName)) { 120 procedure.setServer(serverName); 121 } 122 if(supportsCatalogs && supportsSchemas){ 123 if(databaseName != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(databaseName)) { 124 if (lower) { 125 procedure.setDatabase(SQLUtil.trimColumnStringQuote(databaseName).toLowerCase(Locale.ROOT)); 126 } else { 127 procedure.setDatabase(databaseName); 128 } 129 } 130 if(schemaName != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(schemaName)) { 131 if (lower) { 132 procedure.setSchema(SQLUtil.trimColumnStringQuote(schemaName).toLowerCase(Locale.ROOT)); 133 } else { 134 procedure.setSchema(schemaName); 135 } 136 } 137 } 138 else if(supportsCatalogs){ 139 if(databaseName != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(databaseName)) { 140 if (lower) { 141 procedure.setDatabase(SQLUtil.trimColumnStringQuote(databaseName).toLowerCase(Locale.ROOT)); 142 } else { 143 procedure.setDatabase(databaseName); 144 } 145 } 146 } 147 else if(supportsSchemas){ 148 if(schemaName != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(schemaName)) { 149 if (lower) { 150 procedure.setSchema(SQLUtil.trimColumnStringQuote(schemaName).toLowerCase(Locale.ROOT)); 151 } else { 152 procedure.setSchema(schemaName); 153 } 154 } 155 } 156 157 if (lower) { 158 procedure.setName(SQLUtil.trimColumnStringQuote(procedureName).toLowerCase(Locale.ROOT)); 159 } else { 160 procedure.setName(procedureName); 161 } 162 if (supportsSchemas && !SQLUtil.isEmpty(procedure.getSchema()) && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(procedure.getSchema())) { 163 procedure.setName(procedure.getSchema() + "." + procedure.getName()); 164 } 165 if (supportsCatalogs && !SQLUtil.isEmpty(procedure.getDatabase()) && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(procedure.getDatabase())) { 166 procedure.setName(procedure.getDatabase() + "." + procedure.getName()); 167 } 168 procedure.setId(String.valueOf(++ModelBindingManager.get().TABLE_COLUMN_ID)); 169 if (ModelBindingManager.getGlobalSqlInfo() != null) { 170 procedure.setCoordinate(new Pair3<Long, Long, Integer>(-1L, -1L, 171 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash())) + "," 172 + new Pair3<Long, Long, Integer>(-1L, -1L, 173 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash()))); 174 } 175 procedure.setType(sqlProcedure.getDataObjectType().name().replace("dot","").toLowerCase(Locale.ROOT)); 176 if(oraclePackage!=null) { 177 oraclePackage.getProcedures().add(procedure); 178 } 179 else{ 180 dataflow.getProcedures().add(procedure); 181 } 182 procedureMap.put(procedureKey, procedure); 183 } 184 } 185 186 private oraclePackage appendOraclePackage(EDbVendor vendor, boolean supportsCatalogs, boolean supportsSchemas, dataflow dataflow, String serverName, String databaseName, String schemaName, TSQLOraclePackage sqlOraclePackage) { 187 String oraclePackageName = sqlOraclePackage.getName(); 188 String oraclePackageKey = getFullName(serverName, databaseName, schemaName, oraclePackageName); 189 if (!oraclePackageMap.containsKey(oraclePackageKey)) { 190 oraclePackage oraclePackage = new oraclePackage(); 191 if (serverName != null && !TSQLEnv.DEFAULT_SERVER_NAME.equalsIgnoreCase(serverName)) { 192 oraclePackage.setServer(serverName); 193 } 194 if(supportsCatalogs && supportsSchemas){ 195 if(databaseName != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(databaseName)) { 196 if (lower) { 197 oraclePackage.setDatabase(SQLUtil.trimColumnStringQuote(databaseName).toLowerCase(Locale.ROOT)); 198 } else { 199 oraclePackage.setDatabase(databaseName); 200 } 201 } 202 if(schemaName != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(schemaName)) { 203 if (lower) { 204 oraclePackage.setSchema(SQLUtil.trimColumnStringQuote(schemaName).toLowerCase(Locale.ROOT)); 205 } else { 206 oraclePackage.setSchema(schemaName); 207 } 208 } 209 } 210 else if(supportsCatalogs){ 211 if(databaseName != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(databaseName)) { 212 if (lower) { 213 oraclePackage.setDatabase(SQLUtil.trimColumnStringQuote(databaseName).toLowerCase(Locale.ROOT)); 214 } else { 215 oraclePackage.setDatabase(databaseName); 216 } 217 } 218 } 219 else if(supportsSchemas){ 220 if(schemaName != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(schemaName)) { 221 if (lower) { 222 oraclePackage.setSchema(SQLUtil.trimColumnStringQuote(schemaName).toLowerCase(Locale.ROOT)); 223 } else { 224 oraclePackage.setSchema(schemaName); 225 } 226 } 227 } 228 229 if (lower) { 230 oraclePackage.setName(SQLUtil.trimColumnStringQuote(oraclePackageName).toLowerCase(Locale.ROOT)); 231 } else { 232 oraclePackage.setName(oraclePackageName); 233 } 234 if (supportsSchemas && !SQLUtil.isEmpty(oraclePackage.getSchema()) && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(oraclePackage.getSchema())) { 235 oraclePackage.setName(oraclePackage.getSchema() + "." + oraclePackage.getName()); 236 } 237 if (supportsCatalogs && !SQLUtil.isEmpty(oraclePackage.getDatabase()) && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(oraclePackage.getDatabase())) { 238 oraclePackage.setName(oraclePackage.getDatabase() + "." + oraclePackage.getName()); 239 } 240 oraclePackage.setId(String.valueOf(++ModelBindingManager.get().TABLE_COLUMN_ID)); 241 if (ModelBindingManager.getGlobalSqlInfo() != null) { 242 oraclePackage.setCoordinate(new Pair3<Long, Long, Integer>(-1L, -1L, 243 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash())) + "," 244 + new Pair3<Long, Long, Integer>(-1L, -1L, 245 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash()))); 246 } 247 dataflow.getPackages().add(oraclePackage); 248 oraclePackageMap.put(oraclePackageKey, oraclePackage); 249 } 250 return oraclePackageMap.get(oraclePackageKey); 251 } 252 253 private void init(EDbVendor vendor) { 254 this.vendor = vendor; 255 procedureMap.clear(); 256 tableMap.clear(); 257 columnMap.clear(); 258 if (ModelBindingManager.get() == null) { 259 ModelBindingManager.set(new ModelBindingManager()); 260 } 261 } 262 263 private void sortTableColumns(dataflow dataflow) { 264 if (dataflow.getTables() != null) { 265 for (table table : dataflow.getTables()) { 266 Collections.sort(table.getColumns(), new Comparator<column>() { 267 public int compare(column t1, column t2) { 268 if (t1.getName().equalsIgnoreCase("RelationRows")) 269 return 1; 270 if (t2.getName().equalsIgnoreCase("RelationRows")) 271 return -1; 272 return 0; 273 } 274 }); 275 } 276 } 277 278 if (dataflow.getResultsets() != null) { 279 for (table table : dataflow.getResultsets()) { 280 Collections.sort(table.getColumns(), new Comparator<column>() { 281 public int compare(column t1, column t2) { 282 if (t1.getName().equalsIgnoreCase("RelationRows")) 283 return 1; 284 if (t2.getName().equalsIgnoreCase("RelationRows")) 285 return -1; 286 return 0; 287 } 288 }); 289 } 290 } 291 } 292 293 /** 294 * The identity of a catalog table under the analysis's identifier rules, from the stored names the 295 * catalog holds, laid out as its published name is: the names this analyzer publishes are the bare 296 * stored names, which read as SQL would name another table ({@code "Orders"} published as 297 * {@code Orders}). Null when the rules give the name no identity. 298 */ 299 private static QualifiedCanonKey storedTableIdentity(TSQLEnv sqlenv, EDbVendor vendor, boolean supportsCatalogs, 300 boolean supportsSchemas, String database, String schema, String table) { 301 List<String> segments = new ArrayList<String>(); 302 if (supportsCatalogs && database != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(database)) { // non-identifier-compare: DEFAULT is the placeholder for a missing tier 303 segments.add(database); 304 } 305 if (supportsSchemas && schema != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(schema)) { // non-identifier-compare: DEFAULT is the placeholder for a missing tier 306 segments.add(schema); 307 } 308 segments.add(table); 309 // The catalog's own rules: the names are what it stored under them. 310 IdentifierService service = sqlenv.getIdentifierService(); 311 if (service == null) { 312 service = IdentifierService.defaultServiceFor(vendor); 313 } 314 try { 315 return service.qualifiedCanonKeyFromStored(segments, ESQLDataObjectType.dotTable); 316 } catch (RuntimeException noIdentity) { 317 return null; 318 } 319 } 320 321 private void appendTable(EDbVendor vendor, boolean supportsCatalogs, boolean supportsSchemas, dataflow dataflow, String serverName, String databaseName, String schemaName, String tableName, 322 boolean isView, String columnName, QualifiedCanonKey identity) { 323 String tableKey = getFullName(serverName, databaseName, schemaName, tableName); 324 if (!tableMap.containsKey(tableKey)) { 325 table table = new table(); 326 table.setMergeIdentity(identity); 327 if (serverName != null && !TSQLEnv.DEFAULT_SERVER_NAME.equalsIgnoreCase(serverName)) { 328 table.setServer(serverName); 329 } 330 if(supportsCatalogs && supportsSchemas){ 331 if(databaseName != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(databaseName)) { 332 if (lower) { 333 table.setDatabase(SQLUtil.trimColumnStringQuote(databaseName).toLowerCase(Locale.ROOT)); 334 } else { 335 table.setDatabase(databaseName); 336 } 337 } 338 if(schemaName != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(schemaName)) { 339 if (lower) { 340 table.setSchema(SQLUtil.trimColumnStringQuote(schemaName).toLowerCase(Locale.ROOT)); 341 } else { 342 table.setSchema(schemaName); 343 } 344 } 345 } 346 else if(supportsCatalogs){ 347 if(databaseName != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(databaseName)) { 348 if (lower) { 349 table.setDatabase(SQLUtil.trimColumnStringQuote(databaseName).toLowerCase(Locale.ROOT)); 350 } else { 351 table.setDatabase(databaseName); 352 } 353 } 354 } 355 else if(supportsSchemas){ 356 if(schemaName != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(schemaName)) { 357 if (lower) { 358 table.setSchema(SQLUtil.trimColumnStringQuote(schemaName).toLowerCase(Locale.ROOT)); 359 } else { 360 table.setSchema(schemaName); 361 } 362 } 363 } 364 365 if (lower) { 366 table.setName(SQLUtil.trimColumnStringQuote(tableName).toLowerCase(Locale.ROOT)); 367 } else { 368 table.setName(tableName); 369 } 370 if (supportsSchemas && !SQLUtil.isEmpty(table.getSchema()) && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(table.getSchema())) { 371 table.setName(table.getSchema() + "." + table.getName()); 372 } 373 if (supportsCatalogs && !SQLUtil.isEmpty(table.getDatabase()) && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(table.getDatabase())) { 374 table.setName(table.getDatabase() + "." + table.getName()); 375 } 376 table.setId(String.valueOf(++ModelBindingManager.get().TABLE_COLUMN_ID)); 377 if (ModelBindingManager.getGlobalSqlInfo() != null) { 378 table.setCoordinate(new Pair3<Long, Long, Integer>(-1L, -1L, 379 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash())) + "," 380 + new Pair3<Long, Long, Integer>(-1L, -1L, 381 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash()))); 382 } 383 if (isView) { 384 table.setType("view"); 385 dataflow.getViews().add(table); 386 } else { 387 table.setType("table"); 388 dataflow.getTables().add(table); 389 } 390 tableMap.put(tableKey, table); 391 } 392 393 table table = tableMap.get(tableKey); 394 String sourceColumnKey = getFullName(serverName, databaseName, schemaName, tableName, columnName); 395 if (!columnMap.containsKey(sourceColumnKey)) { 396 column column = new column(); 397 column.setId(String.valueOf(++ModelBindingManager.get().TABLE_COLUMN_ID)); 398 column.setName(columnName); 399 if ("RelationRows".equalsIgnoreCase(columnName)) { 400 column.setSource("system"); 401 } 402 if (ModelBindingManager.getGlobalSqlInfo() != null) { 403 column.setCoordinate(new Pair3<Long, Long, Integer>(-1L, -1L, 404 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash())) + "," 405 + new Pair3<Long, Long, Integer>(-1L, -1L, 406 ModelBindingManager.getGlobalSqlInfo().getIndexOf(ModelBindingManager.getGlobalHash()))); 407 } 408 table.getColumns().add(column); 409 columnMap.put(sourceColumnKey, column); 410 } 411 } 412 413 private String getFullName(String... segments) { 414 StringBuilder builder = new StringBuilder(); 415 for (int i = 0; i < segments.length; i++) { 416 builder.append(segments[i]); 417 if (i < segments.length - 1) { 418 builder.append("."); 419 } 420 } 421 return builder.toString(); 422 } 423}