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}