001package gudusoft.gsqlparser.sqlenv.parser.sqlflow; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 005import gudusoft.gsqlparser.sqlenv.IdentifierInputForm; 006import gudusoft.gsqlparser.sqlenv.*; 007import gudusoft.gsqlparser.sqlenv.parser.sqldep.MultipleSQLDepSQLEnv; 008import gudusoft.gsqlparser.util.SQLUtil; 009 010import java.util.ArrayList; 011import java.util.List; 012import java.util.Map; 013import java.util.logging.Level; 014import java.util.logging.Logger; 015 016@SuppressWarnings("rawtypes") 017public class SqlflowSQLEnv extends TSQLEnv { 018 019 private Map server; 020 021 private Boolean init = false; 022 023 /** 024 * How the names in this metadata arrived: SQL spellings (the default, and what the 025 * sqlflow-ingester exporter emits, every name delimited) or names read back from a database 026 * catalog. Declared once per file as the top-level {@code nameForm}; never guessed from how a 027 * name looks. Must be set before {@link #initSQLEnv()} runs. 028 */ 029 private IdentifierInputForm nameForm = IdentifierInputForm.SQL_LEXICAL; 030 031 public SqlflowSQLEnv(String defaultServer, String defaultDatabase, String defaultSchema, EDbVendor dbVendor, Map server) { 032 this(defaultServer, defaultDatabase, defaultSchema, dbVendor, server, IdentifierInputForm.SQL_LEXICAL); 033 } 034 035 public SqlflowSQLEnv(String defaultServer, String defaultDatabase, String defaultSchema, EDbVendor dbVendor, Map server, 036 IdentifierInputForm nameForm) { 037 this(defaultServer, defaultDatabase, defaultSchema, dbVendor, server, nameForm, null); 038 } 039 040 /** 041 * Same input, analyzed under an explicit identifier configuration. 042 * 043 * <p>The metadata this environment registers is indexed with {@code identifierProfile}, so the 044 * analysis that reads it back asks the catalog the same question it was filled with. A profile 045 * belonging to another vendor is not applied ({@link IdentifierProfile#forVendorOr}); null means 046 * the vendor default. 047 */ 048 public SqlflowSQLEnv(String defaultServer, String defaultDatabase, String defaultSchema, EDbVendor dbVendor, Map server, 049 IdentifierInputForm nameForm, IdentifierProfile identifierProfile) { 050 super(dbVendor, IdentifierProfile.forVendorOr(identifierProfile, dbVendor)); 051 this.nameForm = nameForm != null ? nameForm : IdentifierInputForm.SQL_LEXICAL; 052 if(!SQLUtil.isEmpty(defaultServer) && !defaultServer.equals(TSQLEnv.DEFAULT_SERVER_NAME)) { 053 setDefaultServerName(defaultServer); 054 } 055 if(!SQLUtil.isEmpty(defaultDatabase) && !defaultDatabase.equals(TSQLEnv.DEFAULT_DB_NAME)) { 056 setDefaultCatalogName(defaultDatabase); 057 } 058 if(!SQLUtil.isEmpty(defaultSchema) && !defaultSchema.equals(TSQLEnv.DEFAULT_SCHEMA_NAME)) { 059 setDefaultSchemaName(defaultSchema); 060 } 061 this.server = server; 062 initSQLEnv(); 063 } 064 065 @Override 066 public void initSQLEnv() { 067 synchronized (init) { 068 if (init) 069 return; 070 String dbVendor = (String) server.get("dbVendor"); 071 if (!SQLUtil.isEmpty((String) server.get("name"))) { 072 this.setDefaultServerName((String) server.get("name")); 073 } 074 EDbVendor vendor = getDBVendor(); 075 if (dbVendor != null) { 076 vendor = EDbVendor.valueOf(dbVendor); 077 } 078 079 boolean supportsCatalogs = TSQLEnv.supportCatalog(vendor); 080 boolean supportsSchemas = TSQLEnv.supportSchema(vendor); 081 082 // Synonyms are resolved in a final pass so that their source tables, 083 // which may live in another schema/catalog, are already loaded. 084 List<Object[]> pendingSynonyms = new ArrayList<Object[]>(); 085 086 try { 087 if (supportsCatalogs && supportsSchemas) { 088 List databases = (List) server.get("databases"); 089 if (databases != null) { 090 for (int i = 0; i < databases.size(); i++) { 091 Map jsonDatabase = (Map) databases.get(i); 092 String databaseName = lex(ESQLDataObjectType.dotCatalog, (String) jsonDatabase.get("name")); 093 List schemas = (List) jsonDatabase.get("schemas"); 094 if (schemas == null) { 095 continue; 096 } 097 TSQLCatalog sqlCatalog = getSQLCatalog(databaseName, true); 098 for (int j = 0; j < schemas.size(); j++) { 099 Map jsonSchema = (Map) schemas.get(j); 100 String schemaName = lex(ESQLDataObjectType.dotSchema, (String) jsonSchema.get("name")); 101 TSQLSchema sqlSchema = sqlCatalog.getSchema(schemaName, true); 102 appendTables(jsonSchema, sqlSchema); 103 appendPackages(jsonSchema, sqlSchema); 104 appendProcedures(jsonSchema, sqlSchema, null); 105 collectSynonyms(jsonSchema, sqlSchema, pendingSynonyms); 106 } 107 } 108 } 109 } else if (supportsCatalogs) { 110 List databases = (List) server.get("databases"); 111 if (databases != null) { 112 for (int i = 0; i < databases.size(); i++) { 113 Map jsonDatabase = (Map) databases.get(i); 114 String databaseName = lex(ESQLDataObjectType.dotCatalog, (String) jsonDatabase.get("name")); 115 TSQLCatalog sqlCatalog = getSQLCatalog(databaseName, true); 116 TSQLSchema sqlSchema = sqlCatalog.getSchema(TSQLEnv.DEFAULT_SCHEMA_NAME, true); 117 appendTables(jsonDatabase, sqlSchema); 118 appendPackages(jsonDatabase, sqlSchema); 119 appendProcedures(jsonDatabase, sqlSchema, null); 120 collectSynonyms(jsonDatabase, sqlSchema, pendingSynonyms); 121 } 122 } 123 } else if (supportsSchemas) { 124 List schemas = (List) server.get("schemas"); 125 if (schemas != null) { 126 for (int i = 0; i < schemas.size(); i++) { 127 Map jsonSchema = (Map) schemas.get(i); 128 String schemaName = lex(ESQLDataObjectType.dotSchema, (String) jsonSchema.get("name")); 129 TSQLCatalog sqlCatalog = getSQLCatalog(TSQLEnv.DEFAULT_DB_NAME, true); 130 TSQLSchema sqlSchema = sqlCatalog.getSchema(schemaName, true); 131 appendTables(jsonSchema, sqlSchema); 132 appendPackages(jsonSchema, sqlSchema); 133 appendProcedures(jsonSchema, sqlSchema, null); 134 collectSynonyms(jsonSchema, sqlSchema, pendingSynonyms); 135 } 136 } 137 } 138 for (int i = 0; i < pendingSynonyms.size(); i++) { 139 Object[] pending = pendingSynonyms.get(i); 140 appendSynonym((TSQLSchema) pending[0], (Map) pending[1]); 141 } 142 } catch (Exception e) { 143 Logger.getLogger(MultipleSQLDepSQLEnv.class.getName()).log(Level.WARNING, "Parse json failed.", e); 144 } 145 init = true; 146 } 147 } 148 149 /** The spelling to register for a name from this file, per its declared {@link #nameForm}. */ 150 private String lex(ESQLDataObjectType type, String name) { 151 return TSQLEnv.toLexical(getDBVendor(), type, name, nameForm); 152 } 153 154 private void appendTables(Map jsonSchema, TSQLSchema sqlSchema) { 155 List tables = (List) jsonSchema.get("tables"); 156 List views = (List) jsonSchema.get("views"); 157 List dbObjs = new ArrayList(); 158 if (tables != null) { 159 dbObjs.addAll(tables); 160 } 161 if (views != null) { 162 dbObjs.addAll(views); 163 } 164 for (int k = 0; k < dbObjs.size(); k++) { 165 Map jsonTable = (Map) dbObjs.get(k); 166 String tableName = lex(ESQLDataObjectType.dotTable, (String) jsonTable.get("name")); 167 TSQLTable sqlTable = sqlSchema.createTable(tableName, 3); 168 String type = (String) jsonTable.get("type"); 169 if (type != null && type.toLowerCase(java.util.Locale.ROOT).indexOf("view") != -1) { 170 sqlTable.setView(true); 171 } 172 List columns = (List) jsonTable.get("columns"); 173 if(columns!=null) { 174 for (int l = 0; l < columns.size(); l++) { 175 Map jsonColumn = (Map) columns.get(l); 176 sqlTable.addColumn(lex(ESQLDataObjectType.dotColumn, (String) jsonColumn.get("name"))); 177 } 178 } 179 } 180 } 181 182 private void collectSynonyms(Map jsonSchema, TSQLSchema sqlSchema, List<Object[]> pendingSynonyms) { 183 List synonyms = (List) jsonSchema.get("synonyms"); 184 if (synonyms == null) { 185 return; 186 } 187 for (int k = 0; k < synonyms.size(); k++) { 188 Map jsonSynonym = (Map) synonyms.get(k); 189 if (jsonSynonym == null) { 190 continue; 191 } 192 pendingSynonyms.add(new Object[] { sqlSchema, jsonSynonym }); 193 } 194 } 195 196 private void appendSynonym(TSQLSchema sqlSchema, Map jsonSynonym) { 197 String synonymName = lex(ESQLDataObjectType.dotSynonyms, (String) jsonSynonym.get("name")); 198 if (SQLUtil.isEmpty(synonymName)) { 199 return; 200 } 201 String sourceName = lex(ESQLDataObjectType.dotTable, (String) jsonSynonym.get("sourceName")); 202 if (SQLUtil.isEmpty(sourceName)) { 203 return; 204 } 205 String sourceDatabase = lex(ESQLDataObjectType.dotCatalog, (String) jsonSynonym.get("sourceDatabase")); 206 String sourceSchema = lex(ESQLDataObjectType.dotSchema, (String) jsonSynonym.get("sourceSchema")); 207 // Register the synonym as a TSQLSynonyms carrying its base target. The 208 // owning TSQLEnv dereferences the synonym to its base table/view at 209 // lookup time via TSQLSynonyms.resolveBaseTable(), so the synonym 210 // transparently exposes the base object's columns without us copying 211 // them up-front (which previously also broke cross-database / forward 212 // references). 213 sqlSchema.createSynonyms(synonymName, sourceDatabase, sourceSchema, sourceName); 214 } 215 216 private void appendPackages(Map jsonSchema, TSQLSchema sqlSchema) { 217 List packages = (List) jsonSchema.get("packages"); 218 List dbObjs = new ArrayList(); 219 if (packages != null) { 220 dbObjs.addAll(packages); 221 } 222 for (int k = 0; k < dbObjs.size(); k++) { 223 Map jsonOraclePackage = (Map) dbObjs.get(k); 224 String packageName = lex(ESQLDataObjectType.dotOraclePackage, (String) jsonOraclePackage.get("name")); 225 TSQLOraclePackage sqlPackage = sqlSchema.createOraclePackage(packageName); 226 appendProcedures(jsonOraclePackage, sqlSchema, sqlPackage); 227 } 228 } 229 230 private void appendProcedures(Map jsonProcedureParent, TSQLSchema sqlSchema, TSQLOraclePackage sqlPackage) { 231 if (jsonProcedureParent.containsKey("procedures")) { 232 List<Map> procedures = (List<Map>) jsonProcedureParent.get("procedures"); 233 if(procedures!=null) { 234 for (Map procedure : procedures) { 235 String procedureName = lex(ESQLDataObjectType.dotProcedure, (String) procedure.get("name")); 236 TSQLRoutine routine = sqlPackage != null ? sqlPackage.createProcedure(procedureName) 237 : sqlSchema.createProcedure(procedureName); 238 // values preset for its parameters, for the dynamic SQL evaluation (Mantis 4962) 239 if (routine != null && procedure.get("parameterBindings") instanceof Map) { 240 routine.applyParameterBindings((Map) procedure.get("parameterBindings"), "SQLFlow metadata"); 241 } 242 } 243 } 244 } 245 if (jsonProcedureParent.containsKey("functions")) { 246 List<Map> functions = (List<Map>) jsonProcedureParent.get("functions"); 247 if (functions != null) { 248 for (Map function : functions) { 249 String functionName = lex(ESQLDataObjectType.dotFunction, (String) function.get("name")); 250 if (sqlPackage != null) { 251 sqlPackage.createFunction(functionName); 252 } else { 253 sqlSchema.createFunction(functionName); 254 } 255 } 256 } 257 } 258 if (jsonProcedureParent.containsKey("triggers")) { 259 List<Map> triggers = (List<Map>) jsonProcedureParent.get("triggers"); 260 if (triggers != null) { 261 for (Map trigger : triggers) { 262 String triggerName = lex(ESQLDataObjectType.dotTrigger, (String) trigger.get("name")); 263 if (sqlPackage != null) { 264 sqlPackage.createTrigger(triggerName); 265 } else { 266 sqlSchema.createTrigger(triggerName); 267 } 268 } 269 } 270 } 271 } 272}