001package gudusoft.gsqlparser.sqlenv; 002 003import gudusoft.gsqlparser.EDataType; 004import gudusoft.gsqlparser.EDbVendor; 005import gudusoft.gsqlparser.TBaseType; 006import gudusoft.gsqlparser.TGSqlParser; 007import gudusoft.gsqlparser.nodes.TObjectName; 008import gudusoft.gsqlparser.nodes.TTypeName; 009import gudusoft.gsqlparser.sqlenv.catalog.CatalogStoreProvider; 010import gudusoft.gsqlparser.sqlenv.catalog.ICatalogProvider; 011import gudusoft.gsqlparser.util.Logger; 012import gudusoft.gsqlparser.util.LoggerFactory; 013import gudusoft.gsqlparser.util.SQLUtil; 014 015import java.util.ArrayList; 016import java.util.EnumMap; 017import java.util.List; 018import java.util.Map; 019import java.util.concurrent.ConcurrentHashMap; 020import java.util.concurrent.CopyOnWriteArrayList; 021 022import static gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.*; 023 024/** 025 * SQL environment includes the metadata of a list of databases. The typical scenario is there is one database includes 026 * some schemas, and each schema includes some tables, views, procedures and etc. 027 * <br> 028 * <br>Database known as catalog in ANSI SQL. 029 * <br>Each catalog including a list of schemas. 030 * <br>Each schema including a list of schema objects such as table, procedure, function, trigger and more. 031 * <br> 032 * Implement your own concrete class derived from this class to get the metadata from a real database. 033 * Usually, this is done by querying the INFORMATION_SCHEMA in the {@link #initSQLEnv()} method which should be override 034 * in your own class. 035 * <br><br> 036 * 037 */ 038public abstract class TSQLEnv { 039 040 private static final Logger logger = LoggerFactory.getLogger(TSQLEnv.class); 041 042 public static final Map<EDbVendor, Boolean> columnCollationCaseSensitive; 043 044 static { 045 columnCollationCaseSensitive = new EnumMap<>(EDbVendor.class); 046 columnCollationCaseSensitive.put(EDbVendor.dbvaccess, false); 047 columnCollationCaseSensitive.put(EDbVendor.dbvansi, false); 048 columnCollationCaseSensitive.put(EDbVendor.dbvathena, false); 049 columnCollationCaseSensitive.put(EDbVendor.dbvazuresql, false); 050 columnCollationCaseSensitive.put(EDbVendor.dbvbigquery, false); 051 columnCollationCaseSensitive.put(EDbVendor.dbvclickhouse, false); 052 columnCollationCaseSensitive.put(EDbVendor.dbvcouchbase, false); 053 columnCollationCaseSensitive.put(EDbVendor.dbvdax, false); 054 columnCollationCaseSensitive.put(EDbVendor.dbvdb2, false); 055 columnCollationCaseSensitive.put(EDbVendor.dbvexasol, false); 056 columnCollationCaseSensitive.put(EDbVendor.dbvfirebird, false); 057 columnCollationCaseSensitive.put(EDbVendor.dbvgeneric, false); 058 columnCollationCaseSensitive.put(EDbVendor.dbvgreenplum, false); 059 columnCollationCaseSensitive.put(EDbVendor.dbvhana, false); 060 columnCollationCaseSensitive.put(EDbVendor.dbvhive, false); 061 columnCollationCaseSensitive.put(EDbVendor.dbvimpala, false); 062 columnCollationCaseSensitive.put(EDbVendor.dbvinformix, false); 063 columnCollationCaseSensitive.put(EDbVendor.dbvmdx, false); 064 columnCollationCaseSensitive.put(EDbVendor.dbvmysql, false); 065 columnCollationCaseSensitive.put(EDbVendor.dbvmssql, false); 066 columnCollationCaseSensitive.put(EDbVendor.dbvnetezza, false); 067 columnCollationCaseSensitive.put(EDbVendor.dbvodbc, false); 068 columnCollationCaseSensitive.put(EDbVendor.dbvopenedge, false); 069 columnCollationCaseSensitive.put(EDbVendor.dbvoracle, true); 070 columnCollationCaseSensitive.put(EDbVendor.dbvdameng, true); 071 columnCollationCaseSensitive.put(EDbVendor.dbvoceanbase, false); // mirrors MySQL — default tenant mode is MYSQL 072 columnCollationCaseSensitive.put(EDbVendor.dbvpostgresql, false); 073 columnCollationCaseSensitive.put(EDbVendor.dbvduckdb, false); 074 columnCollationCaseSensitive.put(EDbVendor.dbvpresto, false); 075 columnCollationCaseSensitive.put(EDbVendor.dbvflink, false); 076 columnCollationCaseSensitive.put(EDbVendor.dbvredshift, false); 077 columnCollationCaseSensitive.put(EDbVendor.dbvsnowflake, false); 078 columnCollationCaseSensitive.put(EDbVendor.dbvsoql, false); 079 columnCollationCaseSensitive.put(EDbVendor.dbvsparksql, false); 080 columnCollationCaseSensitive.put(EDbVendor.dbvsybase, false); 081 columnCollationCaseSensitive.put(EDbVendor.dbvteradata, false); 082 columnCollationCaseSensitive.put(EDbVendor.dbvtrino, false); 083 columnCollationCaseSensitive.put(EDbVendor.dbvvertica, false); 084 columnCollationCaseSensitive.put(EDbVendor.dbvdatabricks, false); 085 columnCollationCaseSensitive.put(EDbVendor.dbvgaussdb, false); 086 columnCollationCaseSensitive.put(EDbVendor.dbvedb, false); 087 columnCollationCaseSensitive.put(EDbVendor.dbvdoris, false); // Doris is MySQL-compatible, case-insensitive 088 columnCollationCaseSensitive.put(EDbVendor.dbvstarrocks, false); // StarRocks is MySQL-compatible, case-insensitive 089 columnCollationCaseSensitive.put(EDbVendor.dbvsqlite, false); // SQLite is case-insensitive for identifiers 090 091 // Compile-time check to ensure all enum values are covered 092 if (columnCollationCaseSensitive.size() != EDbVendor.values().length) { 093 throw new IllegalStateException("columnCollationCaseSensitive map must contain all EDbVendor values"); 094 } 095 } 096 097 public static final Map<EDbVendor, Boolean> functionCollationCaseSensitive; 098 099 static { 100 functionCollationCaseSensitive = new EnumMap<>(EDbVendor.class); 101 functionCollationCaseSensitive.put(EDbVendor.dbvaccess, false); 102 functionCollationCaseSensitive.put(EDbVendor.dbvansi, false); 103 functionCollationCaseSensitive.put(EDbVendor.dbvathena, false); 104 functionCollationCaseSensitive.put(EDbVendor.dbvazuresql, false); 105 functionCollationCaseSensitive.put(EDbVendor.dbvbigquery, false); 106 functionCollationCaseSensitive.put(EDbVendor.dbvclickhouse, false); 107 functionCollationCaseSensitive.put(EDbVendor.dbvcouchbase, false); 108 functionCollationCaseSensitive.put(EDbVendor.dbvdax, false); 109 functionCollationCaseSensitive.put(EDbVendor.dbvdb2, false); 110 functionCollationCaseSensitive.put(EDbVendor.dbvexasol, false); 111 functionCollationCaseSensitive.put(EDbVendor.dbvfirebird, false); 112 functionCollationCaseSensitive.put(EDbVendor.dbvgeneric, false); 113 functionCollationCaseSensitive.put(EDbVendor.dbvgreenplum, false); 114 functionCollationCaseSensitive.put(EDbVendor.dbvhana, false); 115 functionCollationCaseSensitive.put(EDbVendor.dbvhive, false); 116 functionCollationCaseSensitive.put(EDbVendor.dbvimpala, false); 117 functionCollationCaseSensitive.put(EDbVendor.dbvinformix, false); 118 functionCollationCaseSensitive.put(EDbVendor.dbvmdx, false); 119 functionCollationCaseSensitive.put(EDbVendor.dbvmysql, false); 120 functionCollationCaseSensitive.put(EDbVendor.dbvmssql, false); 121 functionCollationCaseSensitive.put(EDbVendor.dbvnetezza, false); 122 functionCollationCaseSensitive.put(EDbVendor.dbvodbc, false); 123 functionCollationCaseSensitive.put(EDbVendor.dbvopenedge, false); 124 functionCollationCaseSensitive.put(EDbVendor.dbvoracle, true); 125 functionCollationCaseSensitive.put(EDbVendor.dbvdameng, true); 126 functionCollationCaseSensitive.put(EDbVendor.dbvoceanbase, false); // mirrors MySQL — default tenant mode is MYSQL 127 functionCollationCaseSensitive.put(EDbVendor.dbvpostgresql, true); 128 functionCollationCaseSensitive.put(EDbVendor.dbvduckdb, false); 129 functionCollationCaseSensitive.put(EDbVendor.dbvpresto, false); 130 functionCollationCaseSensitive.put(EDbVendor.dbvflink, false); 131 functionCollationCaseSensitive.put(EDbVendor.dbvredshift, false); 132 functionCollationCaseSensitive.put(EDbVendor.dbvsnowflake, false); 133 functionCollationCaseSensitive.put(EDbVendor.dbvsoql, false); 134 functionCollationCaseSensitive.put(EDbVendor.dbvsparksql, false); 135 functionCollationCaseSensitive.put(EDbVendor.dbvsybase, false); 136 functionCollationCaseSensitive.put(EDbVendor.dbvteradata, false); 137 functionCollationCaseSensitive.put(EDbVendor.dbvtrino, false); 138 functionCollationCaseSensitive.put(EDbVendor.dbvvertica, false); 139 functionCollationCaseSensitive.put(EDbVendor.dbvdatabricks, false); 140 functionCollationCaseSensitive.put(EDbVendor.dbvgaussdb, false); 141 functionCollationCaseSensitive.put(EDbVendor.dbvedb, false); 142 functionCollationCaseSensitive.put(EDbVendor.dbvdoris, false); // Doris is MySQL-compatible, case-insensitive 143 functionCollationCaseSensitive.put(EDbVendor.dbvstarrocks, false); // StarRocks is MySQL-compatible, case-insensitive 144 functionCollationCaseSensitive.put(EDbVendor.dbvsqlite, false); // SQLite is case-insensitive for identifiers 145 146 // Compile-time check to ensure all enum values are covered 147 if (functionCollationCaseSensitive.size() != EDbVendor.values().length) { 148 throw new IllegalStateException("functionCollationCaseSensitive map must contain all EDbVendor values"); 149 } 150 } 151 public static final Map<EDbVendor, Boolean> tableCollationCaseSensitive; 152 153 static { 154 tableCollationCaseSensitive = new EnumMap<>(EDbVendor.class); 155 tableCollationCaseSensitive.put(EDbVendor.dbvaccess, false); 156 tableCollationCaseSensitive.put(EDbVendor.dbvansi, false); 157 tableCollationCaseSensitive.put(EDbVendor.dbvathena, false); 158 tableCollationCaseSensitive.put(EDbVendor.dbvazuresql, false); 159 tableCollationCaseSensitive.put(EDbVendor.dbvbigquery, false); 160 tableCollationCaseSensitive.put(EDbVendor.dbvclickhouse, false); 161 tableCollationCaseSensitive.put(EDbVendor.dbvcouchbase, false); 162 tableCollationCaseSensitive.put(EDbVendor.dbvdax, false); 163 tableCollationCaseSensitive.put(EDbVendor.dbvdb2, true); 164 tableCollationCaseSensitive.put(EDbVendor.dbvexasol, false); 165 tableCollationCaseSensitive.put(EDbVendor.dbvfirebird, false); 166 tableCollationCaseSensitive.put(EDbVendor.dbvgeneric, false); 167 tableCollationCaseSensitive.put(EDbVendor.dbvgreenplum, false); 168 tableCollationCaseSensitive.put(EDbVendor.dbvhana, false); 169 tableCollationCaseSensitive.put(EDbVendor.dbvhive, false); 170 tableCollationCaseSensitive.put(EDbVendor.dbvimpala, false); 171 tableCollationCaseSensitive.put(EDbVendor.dbvinformix, false); 172 tableCollationCaseSensitive.put(EDbVendor.dbvmdx, false); 173 tableCollationCaseSensitive.put(EDbVendor.dbvmysql, false); 174 tableCollationCaseSensitive.put(EDbVendor.dbvmssql, false); 175 tableCollationCaseSensitive.put(EDbVendor.dbvnetezza, false); 176 tableCollationCaseSensitive.put(EDbVendor.dbvodbc, false); 177 tableCollationCaseSensitive.put(EDbVendor.dbvopenedge, false); 178 tableCollationCaseSensitive.put(EDbVendor.dbvoracle, true); 179 tableCollationCaseSensitive.put(EDbVendor.dbvdameng, true); 180 tableCollationCaseSensitive.put(EDbVendor.dbvoceanbase, false); // mirrors MySQL — default tenant mode is MYSQL 181 tableCollationCaseSensitive.put(EDbVendor.dbvpostgresql, true); 182 tableCollationCaseSensitive.put(EDbVendor.dbvduckdb, false); 183 tableCollationCaseSensitive.put(EDbVendor.dbvpresto, false); 184 tableCollationCaseSensitive.put(EDbVendor.dbvflink, false); 185 tableCollationCaseSensitive.put(EDbVendor.dbvredshift, false); 186 tableCollationCaseSensitive.put(EDbVendor.dbvsnowflake, false); 187 tableCollationCaseSensitive.put(EDbVendor.dbvsoql, false); 188 tableCollationCaseSensitive.put(EDbVendor.dbvsparksql, false); 189 tableCollationCaseSensitive.put(EDbVendor.dbvsybase, false); 190 tableCollationCaseSensitive.put(EDbVendor.dbvteradata, false); 191 tableCollationCaseSensitive.put(EDbVendor.dbvtrino, false); 192 tableCollationCaseSensitive.put(EDbVendor.dbvvertica, false); 193 tableCollationCaseSensitive.put(EDbVendor.dbvdatabricks, false); 194 tableCollationCaseSensitive.put(EDbVendor.dbvgaussdb, false); 195 tableCollationCaseSensitive.put(EDbVendor.dbvedb, false); 196 tableCollationCaseSensitive.put(EDbVendor.dbvdoris, false); // Doris is MySQL-compatible, case-insensitive 197 tableCollationCaseSensitive.put(EDbVendor.dbvstarrocks, false); // StarRocks is MySQL-compatible, case-insensitive 198 tableCollationCaseSensitive.put(EDbVendor.dbvsqlite, false); // SQLite is case-insensitive for identifiers 199 200 // Compile-time check to ensure all enum values are covered 201 if (tableCollationCaseSensitive.size() != EDbVendor.values().length) { 202 throw new IllegalStateException("tableCollationCaseSensitive map must contain all EDbVendor values"); 203 } 204 } 205 206 public static final Map<EDbVendor, Boolean> catalogCollationCaseSensitive; 207 208 static { 209 catalogCollationCaseSensitive = new EnumMap<>(EDbVendor.class); 210 catalogCollationCaseSensitive.put(EDbVendor.dbvaccess, false); 211 catalogCollationCaseSensitive.put(EDbVendor.dbvansi, false); 212 catalogCollationCaseSensitive.put(EDbVendor.dbvathena, false); 213 catalogCollationCaseSensitive.put(EDbVendor.dbvazuresql, false); 214 catalogCollationCaseSensitive.put(EDbVendor.dbvbigquery, false); 215 catalogCollationCaseSensitive.put(EDbVendor.dbvclickhouse, false); 216 catalogCollationCaseSensitive.put(EDbVendor.dbvcouchbase, false); 217 catalogCollationCaseSensitive.put(EDbVendor.dbvdax, false); 218 catalogCollationCaseSensitive.put(EDbVendor.dbvdb2, true); 219 catalogCollationCaseSensitive.put(EDbVendor.dbvexasol, false); 220 catalogCollationCaseSensitive.put(EDbVendor.dbvfirebird, false); 221 catalogCollationCaseSensitive.put(EDbVendor.dbvgeneric, false); 222 catalogCollationCaseSensitive.put(EDbVendor.dbvgreenplum, false); 223 catalogCollationCaseSensitive.put(EDbVendor.dbvhana, false); 224 catalogCollationCaseSensitive.put(EDbVendor.dbvhive, false); 225 catalogCollationCaseSensitive.put(EDbVendor.dbvimpala, false); 226 catalogCollationCaseSensitive.put(EDbVendor.dbvinformix, false); 227 catalogCollationCaseSensitive.put(EDbVendor.dbvmdx, false); 228 catalogCollationCaseSensitive.put(EDbVendor.dbvmysql, true); 229 catalogCollationCaseSensitive.put(EDbVendor.dbvmssql, false); 230 catalogCollationCaseSensitive.put(EDbVendor.dbvnetezza, false); 231 catalogCollationCaseSensitive.put(EDbVendor.dbvodbc, false); 232 catalogCollationCaseSensitive.put(EDbVendor.dbvopenedge, false); 233 catalogCollationCaseSensitive.put(EDbVendor.dbvoracle, false); 234 catalogCollationCaseSensitive.put(EDbVendor.dbvdameng, false); 235 catalogCollationCaseSensitive.put(EDbVendor.dbvoceanbase, true); // mirrors MySQL — catalog (database) is case-sensitive 236 catalogCollationCaseSensitive.put(EDbVendor.dbvpostgresql, true); 237 catalogCollationCaseSensitive.put(EDbVendor.dbvduckdb, false); 238 catalogCollationCaseSensitive.put(EDbVendor.dbvpresto, false); 239 catalogCollationCaseSensitive.put(EDbVendor.dbvflink, false); 240 catalogCollationCaseSensitive.put(EDbVendor.dbvredshift, false); 241 catalogCollationCaseSensitive.put(EDbVendor.dbvsnowflake, false); 242 catalogCollationCaseSensitive.put(EDbVendor.dbvsoql, false); 243 catalogCollationCaseSensitive.put(EDbVendor.dbvsparksql, false); 244 catalogCollationCaseSensitive.put(EDbVendor.dbvsybase, false); 245 catalogCollationCaseSensitive.put(EDbVendor.dbvteradata, false); 246 catalogCollationCaseSensitive.put(EDbVendor.dbvtrino, false); 247 catalogCollationCaseSensitive.put(EDbVendor.dbvvertica, false); 248 catalogCollationCaseSensitive.put(EDbVendor.dbvdatabricks, false); 249 catalogCollationCaseSensitive.put(EDbVendor.dbvgaussdb, false); 250 catalogCollationCaseSensitive.put(EDbVendor.dbvedb, false); 251 catalogCollationCaseSensitive.put(EDbVendor.dbvdoris, true); // Doris is MySQL-compatible, catalog (database) is case-sensitive 252 catalogCollationCaseSensitive.put(EDbVendor.dbvstarrocks, true); // StarRocks is MySQL-compatible, catalog (database) is case-sensitive 253 catalogCollationCaseSensitive.put(EDbVendor.dbvsqlite, true); // SQLite database names (file paths) are case-sensitive on most OS 254 255 // Compile-time check to ensure all enum values are covered 256 if (catalogCollationCaseSensitive.size() != EDbVendor.values().length) { 257 throw new IllegalStateException("catalogCollationCaseSensitive map must contain all EDbVendor values"); 258 } 259 } 260 261 public static final Map<EDbVendor, Boolean> defaultCollationCaseSensitive; 262 263 static { 264 defaultCollationCaseSensitive = new EnumMap<>(EDbVendor.class); 265 defaultCollationCaseSensitive.put(EDbVendor.dbvaccess, false); 266 defaultCollationCaseSensitive.put(EDbVendor.dbvansi, false); 267 defaultCollationCaseSensitive.put(EDbVendor.dbvathena, false); 268 defaultCollationCaseSensitive.put(EDbVendor.dbvazuresql, false); 269 defaultCollationCaseSensitive.put(EDbVendor.dbvbigquery, false); 270 defaultCollationCaseSensitive.put(EDbVendor.dbvclickhouse, false); 271 defaultCollationCaseSensitive.put(EDbVendor.dbvcouchbase, false); 272 defaultCollationCaseSensitive.put(EDbVendor.dbvdax, false); 273 defaultCollationCaseSensitive.put(EDbVendor.dbvdb2, false); 274 defaultCollationCaseSensitive.put(EDbVendor.dbvexasol, false); 275 defaultCollationCaseSensitive.put(EDbVendor.dbvfirebird, false); 276 defaultCollationCaseSensitive.put(EDbVendor.dbvgeneric, false); 277 defaultCollationCaseSensitive.put(EDbVendor.dbvgreenplum, false); 278 defaultCollationCaseSensitive.put(EDbVendor.dbvhana, false); 279 defaultCollationCaseSensitive.put(EDbVendor.dbvhive, false); 280 defaultCollationCaseSensitive.put(EDbVendor.dbvimpala, false); 281 defaultCollationCaseSensitive.put(EDbVendor.dbvinformix, false); 282 defaultCollationCaseSensitive.put(EDbVendor.dbvmdx, false); 283 defaultCollationCaseSensitive.put(EDbVendor.dbvmysql, false); 284 defaultCollationCaseSensitive.put(EDbVendor.dbvmssql, false); 285 defaultCollationCaseSensitive.put(EDbVendor.dbvnetezza, false); 286 defaultCollationCaseSensitive.put(EDbVendor.dbvodbc, false); 287 defaultCollationCaseSensitive.put(EDbVendor.dbvopenedge, false); 288 defaultCollationCaseSensitive.put(EDbVendor.dbvoracle, false); 289 defaultCollationCaseSensitive.put(EDbVendor.dbvdameng, false); 290 defaultCollationCaseSensitive.put(EDbVendor.dbvoceanbase, false); // mirrors MySQL — default tenant mode is MYSQL 291 defaultCollationCaseSensitive.put(EDbVendor.dbvpostgresql, false); 292 defaultCollationCaseSensitive.put(EDbVendor.dbvduckdb, false); 293 defaultCollationCaseSensitive.put(EDbVendor.dbvpresto, false); 294 defaultCollationCaseSensitive.put(EDbVendor.dbvflink, false); 295 defaultCollationCaseSensitive.put(EDbVendor.dbvredshift, false); 296 defaultCollationCaseSensitive.put(EDbVendor.dbvsnowflake, false); 297 defaultCollationCaseSensitive.put(EDbVendor.dbvsoql, false); 298 defaultCollationCaseSensitive.put(EDbVendor.dbvsparksql, false); 299 defaultCollationCaseSensitive.put(EDbVendor.dbvsybase, false); 300 defaultCollationCaseSensitive.put(EDbVendor.dbvteradata, false); 301 defaultCollationCaseSensitive.put(EDbVendor.dbvtrino, false); 302 defaultCollationCaseSensitive.put(EDbVendor.dbvvertica, false); 303 defaultCollationCaseSensitive.put(EDbVendor.dbvdatabricks, false); 304 defaultCollationCaseSensitive.put(EDbVendor.dbvgaussdb, false); 305 defaultCollationCaseSensitive.put(EDbVendor.dbvedb, false); 306 defaultCollationCaseSensitive.put(EDbVendor.dbvdoris, false); // Doris is MySQL-compatible, case-insensitive 307 defaultCollationCaseSensitive.put(EDbVendor.dbvstarrocks, false); // StarRocks is MySQL-compatible, case-insensitive 308 defaultCollationCaseSensitive.put(EDbVendor.dbvsqlite, false); // SQLite is case-insensitive by default 309 310 // Compile-time check to ensure all enum values are covered 311 if (defaultCollationCaseSensitive.size() != EDbVendor.values().length) { 312 throw new IllegalStateException("defaultCollationCaseSensitive map must contain all EDbVendor values"); 313 } 314 } 315 316 317 318 public static final Map<EDbVendor, Boolean> isAliasReferenceForbidden; 319 320 static { 321 isAliasReferenceForbidden = new EnumMap<>(EDbVendor.class); 322 isAliasReferenceForbidden.put(EDbVendor.dbvaccess, false); 323 isAliasReferenceForbidden.put(EDbVendor.dbvansi, false); 324 isAliasReferenceForbidden.put(EDbVendor.dbvathena, false); 325 isAliasReferenceForbidden.put(EDbVendor.dbvazuresql, true); 326 isAliasReferenceForbidden.put(EDbVendor.dbvbigquery, true); 327 isAliasReferenceForbidden.put(EDbVendor.dbvclickhouse, false); 328 isAliasReferenceForbidden.put(EDbVendor.dbvcouchbase, false); 329 isAliasReferenceForbidden.put(EDbVendor.dbvdax, false); 330 isAliasReferenceForbidden.put(EDbVendor.dbvdb2, true); 331 isAliasReferenceForbidden.put(EDbVendor.dbvexasol, false); // Exasol disallows forward references 332 isAliasReferenceForbidden.put(EDbVendor.dbvfirebird, false); // Firebird follows standard SQL behavior 333 isAliasReferenceForbidden.put(EDbVendor.dbvgeneric, false); 334 isAliasReferenceForbidden.put(EDbVendor.dbvgreenplum, true); 335 isAliasReferenceForbidden.put(EDbVendor.dbvhana, false); // SAP HANA follows standard SQL behavior 336 isAliasReferenceForbidden.put(EDbVendor.dbvhive, true); 337 isAliasReferenceForbidden.put(EDbVendor.dbvimpala, false); // Impala follows Hive's behavior 338 isAliasReferenceForbidden.put(EDbVendor.dbvinformix, true); 339 isAliasReferenceForbidden.put(EDbVendor.dbvmdx, false); 340 isAliasReferenceForbidden.put(EDbVendor.dbvmysql, true); 341 isAliasReferenceForbidden.put(EDbVendor.dbvmssql, true); 342 isAliasReferenceForbidden.put(EDbVendor.dbvnetezza, true); 343 isAliasReferenceForbidden.put(EDbVendor.dbvodbc, false); 344 isAliasReferenceForbidden.put(EDbVendor.dbvopenedge, false); 345 isAliasReferenceForbidden.put(EDbVendor.dbvoracle, true); 346 isAliasReferenceForbidden.put(EDbVendor.dbvdameng, true); 347 isAliasReferenceForbidden.put(EDbVendor.dbvoceanbase, true); // mirrors MySQL — alias forward reference forbidden 348 isAliasReferenceForbidden.put(EDbVendor.dbvpostgresql, true); 349 isAliasReferenceForbidden.put(EDbVendor.dbvduckdb, true); 350 isAliasReferenceForbidden.put(EDbVendor.dbvpresto, true); 351 isAliasReferenceForbidden.put(EDbVendor.dbvflink, true); // Flink follows standard SQL behavior like SparkSQL 352 isAliasReferenceForbidden.put(EDbVendor.dbvredshift, true); 353 isAliasReferenceForbidden.put(EDbVendor.dbvsnowflake, false); // Snowflake disallows column alias references 354 isAliasReferenceForbidden.put(EDbVendor.dbvsoql, false); 355 isAliasReferenceForbidden.put(EDbVendor.dbvsparksql, true); 356 isAliasReferenceForbidden.put(EDbVendor.dbvsybase, true); 357 isAliasReferenceForbidden.put(EDbVendor.dbvteradata, false); // Teradata follows standard SQL behavior 358 isAliasReferenceForbidden.put(EDbVendor.dbvtrino, true); 359 isAliasReferenceForbidden.put(EDbVendor.dbvvertica, false); // Vertica disallows forward references 360 isAliasReferenceForbidden.put(EDbVendor.dbvdatabricks, true); 361 isAliasReferenceForbidden.put(EDbVendor.dbvgaussdb, true); 362 isAliasReferenceForbidden.put(EDbVendor.dbvedb, true); 363 isAliasReferenceForbidden.put(EDbVendor.dbvdoris, true); // Doris is MySQL-compatible, alias reference forbidden 364 isAliasReferenceForbidden.put(EDbVendor.dbvstarrocks, true); // StarRocks is MySQL-compatible, alias reference forbidden 365 isAliasReferenceForbidden.put(EDbVendor.dbvsqlite, true); // SQLite follows PostgreSQL behavior, alias reference forbidden in same clause 366 367 // Compile-time check to ensure all enum values are covered 368 if (isAliasReferenceForbidden.size() != EDbVendor.values().length) { 369 throw new IllegalStateException("isAliasReferenceForbidden map must contain all EDbVendor values"); 370 } 371 } 372 373 public static String getStmtSeparatorChar(EDbVendor dbVendor){ 374 String ret = ";"; 375 switch (dbVendor){ 376 case dbvoracle: 377 case dbvteradata: 378 case dbvpostgresql: 379 case dbvduckdb: 380 case dbvredshift: 381 case dbvgreenplum: 382 ret = "/"; 383 break; 384 case dbvdameng: 385 ret = "/"; 386 break; 387 case dbvdb2: 388 ret = "@"; 389 break; 390 case dbvmysql: 391 ret = "$"; 392 break; 393 default: 394 break; 395 } 396 return ret; 397 } 398 399 400 /** 401 * Whether this database support schema or not? 402 * 403 * @param dbVendor 404 * @return 405 */ 406 public static boolean supportSchema(EDbVendor dbVendor){ 407 return (!((dbVendor == EDbVendor.dbvmysql) 408 ||(dbVendor == EDbVendor.dbvteradata)||(dbVendor == EDbVendor.dbvhive)||(dbVendor == EDbVendor.dbvimpala) 409 )); 410 } 411 412 /** 413 * Whether this database support catalog or not? 414 * 415 * @param dbVendor 416 * @return 417 */ 418 public static boolean supportCatalog(EDbVendor dbVendor) { 419// return (!((dbVendor == EDbVendor.dbvoracle) 420// )); 421 return true; 422 } 423 424 /** 425 * used to delimit a database identifier, such as [ used in SQL Server, ` used in MySQL 426 * 427 * @param dbVendor 428 * @return 429 */ 430 public static String delimitedChar(EDbVendor dbVendor){ 431 String ret = "\""; 432 switch (dbVendor){ 433 case dbvmssql: 434 case dbvazuresql: 435 ret = "["; 436 break; 437 case dbvathena: 438 case dbvmysql: 439 case dbvbigquery: 440 case dbvcouchbase: 441 case dbvhive: 442 case dbvimpala: 443 case dbvdatabricks: 444 ret = "`"; 445 break; 446 case dbvdax: 447 ret = "'"; 448 break; 449 default: 450 break; 451 } 452 return ret; 453 } 454 455 public static boolean isDelimitedIdentifier(EDbVendor dbVendor, String identifier){ 456 boolean ret = false; 457 switch (dbVendor){ 458 case dbvmssql: 459 case dbvazuresql: 460 ret = identifier.startsWith("[")||identifier.startsWith("\"")||identifier.startsWith("'"); 461 break; 462 case dbvmysql: 463 case dbvbigquery: 464 case dbvcouchbase: 465 case dbvhive: 466 case dbvimpala: 467 ret = identifier.startsWith("`"); 468 break; 469 case dbvdax: 470 ret = identifier.startsWith("'"); 471 break; 472 default: 473 ret = identifier.startsWith("\""); 474 break; 475 } 476 return ret; 477 } 478 479 public static boolean endsWithDelimitedIdentifier(EDbVendor dbVendor, String identifier){ 480 boolean ret = false; 481 switch (dbVendor){ 482 case dbvmssql: 483 case dbvazuresql: 484 ret = identifier.endsWith("]")||identifier.endsWith("\""); 485 break; 486 case dbvmysql: 487 case dbvbigquery: 488 case dbvcouchbase: 489 case dbvhive: 490 case dbvimpala: 491 ret = identifier.endsWith("`"); 492 break; 493 case dbvdax: 494 ret = identifier.endsWith("'"); 495 break; 496 default: 497 ret = identifier.endsWith("\""); 498 break; 499 } 500 return ret; 501 } 502 503 public boolean isDelimitedIdentifier(String identifier){ 504 return TSQLEnv.isDelimitedIdentifier(this.getDBVendor(),identifier); 505 } 506 507 508 public boolean compareColumn(String ident1, String ident2){ 509 // Route through NameService when flag is enabled 510 if (TBaseType.USE_EXT_NAME_MATCHER) { 511 return nameService.equals(ESQLDataObjectType.dotColumn, ident1, ident2); 512 } 513 return compareIdentifier(this.getDBVendor(),ESQLDataObjectType.dotColumn,ident1,ident2); 514 } 515 516 public boolean compareTable(String ident1, String ident2){ 517 // Route through NameService when flag is enabled 518 if (TBaseType.USE_EXT_NAME_MATCHER) { 519 return nameService.equals(ESQLDataObjectType.dotTable, ident1, ident2); 520 } 521 return compareIdentifier(this.getDBVendor(),ESQLDataObjectType.dotTable,ident1,ident2); 522 } 523 524 public boolean compareIdentifier(ESQLDataObjectType objectType, String ident1, String ident2){ 525 // Route through NameService when flag is enabled 526 if (TBaseType.USE_EXT_NAME_MATCHER) { 527 return nameService.equals(objectType, ident1, ident2); 528 } 529 530 // Legacy path: use existing logic 531 switch (objectType){ 532 case dotTable: 533 return compareTable(ident1,ident2); 534 case dotColumn: 535 return compareColumn(ident1,ident2); 536 default: 537 return compareIdentifier(this.getDBVendor(),objectType,ident1,ident2); 538 } 539 } 540 541 public static boolean compareColumn( EDbVendor dbVendor, TObjectName sourceColumn, TObjectName targetColumn){ 542 return compareQualifiedColumn(dbVendor,sourceColumn.getColumnNameOnly(),targetColumn.getColumnNameOnly(), 543 sourceColumn.getTableString(),targetColumn.getTableString(), 544 sourceColumn.getSchemaString(),targetColumn.getSchemaString(), 545 sourceColumn.getDatabaseString(),targetColumn.getDatabaseString()); 546 } 547 548 public static boolean compareTable( EDbVendor dbVendor, TObjectName sourceTable, TObjectName targetTable){ 549 return compareQualifiedTable(dbVendor, 550 sourceTable.getTableString(),targetTable.getTableString(), 551 sourceTable.getSchemaString(),targetTable.getSchemaString(), 552 sourceTable.getDatabaseString(),targetTable.getDatabaseString() 553 ); 554 } 555 556 public static boolean compareQualifiedTable( EDbVendor dbVendor, String sourceTable, String targetTable, String sourceSchema, String targetSchema, String sourceDatabase, String targetDatabase){ 557 boolean ret = compareIdentifier(dbVendor,dotTable,sourceTable,targetTable); 558 if (!ret) return ret; 559 560 // compare schema 561 ret = compareIdentifier(dbVendor,dotSchema,sourceSchema,targetSchema); 562 if (!ret) return ret; 563 564 // compare database 565 return compareIdentifier(dbVendor,dotCatalog,sourceDatabase,targetDatabase); 566 } 567 568 public static boolean compareQualifiedColumn( EDbVendor dbVendor, String sourceColumn, String targetColumn, String sourceTable, String targetTable, String sourceSchema, String targetSchema, String sourceDatabase, String targetDatabase){ 569 570 boolean ret = compareIdentifier(dbVendor,dotColumn,sourceColumn,targetColumn); 571 if (!ret) return ret; 572 573 return compareQualifiedTable(dbVendor,sourceTable,targetTable,sourceSchema,targetSchema,sourceDatabase,targetDatabase); 574 } 575 576 public static boolean compareIdentifier( EDbVendor dbVendor, ESQLDataObjectType objectType, TObjectName source, TObjectName target){ 577 return compareIdentifier(dbVendor,objectType,source.toString(),target.toString()); 578 } 579 580 public static boolean compareIdentifier( EDbVendor dbVendor, ESQLDataObjectType objectType, String ident1, String ident2){ 581 582// ident1 = IdentifierService.normalizeStatic(dbVendor,objectType,ident1); 583// ident2 = IdentifierService.normalizeStatic(dbVendor,objectType,ident1); 584// 585// return IdentifierService.areEqualStatic(dbVendor,objectType,ident1,ident2); 586 587 ident1 = normalizeIdentifier(dbVendor,objectType,ident1); // IdentifierService.normalizeStatic(dbVendor,objectType,ident1); 588 ident2 = normalizeIdentifier(dbVendor,objectType,ident2); // IdentifierService.normalizeStatic(dbVendor,objectType,ident1); 589 590 boolean collationSensitive = false; 591 switch (objectType){ 592 case dotCatalog: 593 case dotSchema: 594 collationSensitive = catalogCollationCaseSensitive.get(dbVendor); 595 break; 596 case dotOraclePackage: 597 case dotProcedure: 598 case dotTrigger: 599 case dotTable: 600 collationSensitive = tableCollationCaseSensitive.get(dbVendor); 601 break; 602 case dotFunction: 603 collationSensitive = functionCollationCaseSensitive.get(dbVendor); 604 break; 605 case dotColumn: 606 collationSensitive = columnCollationCaseSensitive.get(dbVendor); 607 break; 608 case dotDblink: // oracle dblink 609 collationSensitive = false; 610 break; 611 default: 612 collationSensitive = defaultCollationCaseSensitive.get(dbVendor); 613 break; 614 } 615 616 if (collationSensitive) { 617 return ident1.equals(ident2); 618 } else { 619 return ident1.equalsIgnoreCase(ident2); 620 } 621 622 } 623 624 public String normalizeIdentifier(ESQLDataObjectType sqlDataObjectType, String identifier) { 625 return this.getIdentifierService().normalize(identifier,sqlDataObjectType); 626 } 627 628 /** 629 * 1. remove delimited char if it's delimited/quoted identifier 630 * 2. change the case of the name in the same way as it saved to the information_schema 631 * 632 * @param dbVendor 633 * @param sqlDataObjectType 634 * @param identifier 635 * @return 636 */ 637 public static String normalizeIdentifier(EDbVendor dbVendor, ESQLDataObjectType sqlDataObjectType, String identifier){ 638 String ret; 639 640 if (identifier == null) return identifier; 641 if (identifier.length() == 0) return identifier; 642 643 if (TSQLEnv.isDelimitedIdentifier(dbVendor,identifier)){ 644 ret = TBaseType.getTextWithoutQuoted(identifier); 645 }else { 646 switch (dbVendor){ 647 case dbvdb2: 648 case dbvoracle: 649 ret = identifier.toUpperCase(); 650 break; 651 case dbvdameng: 652 ret = identifier.toUpperCase(); 653 break; 654 case dbvpostgresql: 655 case dbvduckdb: 656 case dbvgreenplum: 657 case dbvredshift: 658 ret = identifier.toLowerCase(); 659 break; 660 default: 661 ret = identifier; 662 break; 663 } 664 } 665 return ret; 666 } 667 668 /** 669 * 比较 一个数据库对象名是否等于或者属于另一个对象 670 * 等于 就是完全相等(根据不同数据库的比较规则) 671 * 属于 表示如下情况: 672 * 1. column1 -> 属于 -> table1.column1 673 * 2. table1 -> 属于 -> db1.schema1.table1 674 * 3. `schema1.table1` -> 属于 -> `db1`.`schema1`.`table1` 675 * 4. `schema1.table1` -> 不属于 -> `db1`.`schema2`.`table1` 676 * 677 * @param sub 678 * @param whole 679 * @return 680 */ 681 public static boolean matchSubObjectNameToWhole(EDbVendor dbVendor, ESQLDataObjectType sqlDataObjectType,String sub, String whole){ 682 List<String> subParts = SQLUtil.parseNames(sub); 683 // `data.RETAIL_PROD_EXCEPTIONS_SOURCE` 没有被分开,需要去掉 `` 后再次拆分 684 if ((subParts.size() == 1)&&(sub.indexOf(".") != -1)){ 685 subParts = SQLUtil.parseNames(IdentifierService.normalizeStatic(dbVendor,sqlDataObjectType,sub)); 686 } 687 688 List<String> wholeParts = SQLUtil.parseNames(whole); 689 if ((wholeParts.size() == 1)&&(whole.indexOf(".") != -1)){ 690 wholeParts = SQLUtil.parseNames(IdentifierService.normalizeStatic(dbVendor,sqlDataObjectType,whole)); 691 } 692 693 694 if(subParts.size() >wholeParts.size()) return false; 695 int k=0; 696 for(String s:subParts){ 697 subParts.set(k,TBaseType.removePrefixOrSuffixQuoteChar(normalizeIdentifier(dbVendor,sqlDataObjectType,s))); 698 k++; 699 } 700 k=0; 701 for(String s:wholeParts){ 702 wholeParts.set(k, TBaseType.removePrefixOrSuffixQuoteChar(normalizeIdentifier(dbVendor,sqlDataObjectType,s))); 703 k++; 704 } 705 706 boolean ret = false; 707 int i= subParts.size() -1; 708 int j = wholeParts.size() -1; 709 while (i >= 0){ 710 if ( !subParts.get(i).toUpperCase().equals(wholeParts.get(j).toUpperCase())) break; 711 if (i == 0) ret = true; 712 i--; 713 j--; 714 } 715 716 return ret; 717 } 718 719 private boolean enableGetMetadataFromDDL = true; 720 721 public void setEnableGetMetadataFromDDL(boolean enableGetMetadataFromDDL) { 722 this.enableGetMetadataFromDDL = enableGetMetadataFromDDL; 723 } 724 725 /** 726 * If this option is enabled, SQLEnv will collect table/view/function/procedure metadata 727 * from the create table/create view/create function/create procedure statement during the parse of the SQL script. 728 * <br>A TSQLEnv object instance must be passed to {@link TGSqlParser} before parsing the SQL script. And this TSQLEnv 729 * instance can be passed to another {@link TGSqlParser} object with the collected database metadata. 730 * <br>Default value is true. 731 * 732 * @return 733 */ 734 public boolean isEnableGetMetadataFromDDL() { 735 return enableGetMetadataFromDDL; 736 } 737 738 /** 739 * create a SQL environment. 740 * 741 * @param dbVendor the database vendor 742 */ 743 public TSQLEnv(EDbVendor dbVendor){ 744 this.dbVendor = dbVendor; 745 this.nameService = new NameService(dbVendor); 746 747 // ===== Phase 0: Initialize IdentifierService first (required by CatalogStore) ===== 748 // Create IdentifierProfile and IdentifierService 749 this.identifierProfile = IdentifierProfile.forVendor( 750 dbVendor, 751 IdentifierProfile.VendorFlags.defaults() // Default configuration 752 ); 753 this.collatorProvider = createCollatorProvider(dbVendor); // SQL Server needs this 754 this.identifierService = new IdentifierService(identifierProfile, collatorProvider); 755 756 // Phase 3.5: Create CatalogStoreProvider (which internally creates CatalogStore) 757 this.catalogProvider = new CatalogStoreProvider(dbVendor, IdentifierProfile.VendorFlags.defaults()); 758 } 759 760 private EDbVendor dbVendor; 761 762 // Name matching service (Phase 2 integration) 763 private final NameService nameService; 764 765 // ===== Phase 3.5: Final catalog implementation ===== 766 private IdentifierProfile identifierProfile; 767 private IdentifierService identifierService; 768 private CollatorProvider collatorProvider; // SQL Server specific 769 private ICatalogProvider catalogProvider; // Uses CatalogStoreProvider 770 771 /** 772 * the database vendor where this SQL environment is generated from. 773 * 774 * @return the database vendor 775 */ 776 public EDbVendor getDBVendor() { 777 return dbVendor; 778 } 779 780 /** 781 * Returns the catalog store (Phase 3.5 integration). 782 * 783 * <p>Note: This method delegates to the underlying CatalogStoreProvider. 784 * Direct access to CatalogStore is provided for backward compatibility 785 * with existing test code.</p> 786 * 787 * @return the catalog store 788 */ 789 public CatalogStore getCatalogStore() { 790 if (catalogProvider instanceof CatalogStoreProvider) { 791 return ((CatalogStoreProvider) catalogProvider).getCatalogStore(); 792 } 793 return null; 794 } 795 796 // ===== Phase 0: Helper methods and getters ===== 797 798 /** 799 * Create CollatorProvider for SQL Server 800 * 801 * @param vendor database vendor 802 * @return CollatorProvider instance, null for non-SQL Server databases 803 */ 804 private CollatorProvider createCollatorProvider(EDbVendor vendor) { 805 if (vendor == EDbVendor.dbvmssql || vendor == EDbVendor.dbvazuresql) { 806 return new CollatorProvider(); // SQL Server collation 807 } 808 return null; 809 } 810 811 /** 812 * Get IdentifierService (Phase 0) 813 * 814 * @return IdentifierService instance 815 */ 816 public IdentifierService getIdentifierService() { 817 return identifierService; 818 } 819 820 /** 821 * Get IdentifierProfile (Phase 0) 822 * 823 * @return IdentifierProfile instance 824 */ 825 public IdentifierProfile getIdentifierProfile() { 826 return identifierProfile; 827 } 828 829 /** 830 * Get ICatalogProvider (Phase 3) 831 * 832 * @return ICatalogProvider instance 833 */ 834 public ICatalogProvider getCatalogProvider() { 835 return catalogProvider; 836 } 837 838 /** 839 * This method must be override in the subclass to build a SQL environment with real metadata. 840 * <br>this usually done by querying the INFORMATION_SCHEMA 841 */ 842 public abstract void initSQLEnv(); 843 844 /** 845 * a list of catalog/database in this SQL environment. 846 * 847 * @return a list of catalog/database 848 */ 849 public List<TSQLCatalog> getCatalogList() { 850 return catalogList; 851 } 852 853 private String serverName = null; // SQL Server 854 855 private List<TSQLCatalog> catalogList = new CopyOnWriteArrayList<>(); 856 857 /** 858 * add a catalog to the SQL environment, called internally. 859 * 860 * @param sqlCatalog catalog 861 * @return return false if a catalog with the same name already exists. 862 */ 863 protected boolean doAddCatalog(TSQLCatalog sqlCatalog){ 864 boolean isFound = false; 865 for(TSQLCatalog c : catalogList){ 866 if ( sqlCatalog.getName().compareTo(c.getName()) == 0){ 867 isFound = true; 868 break; 869 } 870 } 871 if (!isFound){ 872 catalogList.add(sqlCatalog); 873 } 874 return !isFound; 875 } 876 877 private Map<String, TSQLSchemaObject> schemaObjectList = new ConcurrentHashMap<>(); 878 879 // Fast canonical index for fully-qualified lookups (additive, keeps existing map intact) 880 private final Map<NameKey, TSQLSchemaObject> objectIndex = new ConcurrentHashMap<NameKey, TSQLSchemaObject>(); 881 // Reverse index for tables by object name only (speeds up ..table lookups) 882 private final Map<String, List<TSQLTable>> tablesByName = new ConcurrentHashMap<String, List<TSQLTable>>(); 883 884 /** 885 * put a schema object into the hashmap which can be used to find a schema object in a more efficient way. 886 * 887 * @param schemaObjectName name of schema object 888 * @param schemaObject instance of a schema object 889 * @return always return true. 890 */ 891 protected boolean putSchemaObject(String schemaObjectName, TSQLSchemaObject schemaObject){ 892 String newSchemaName = schemaObjectName; 893 if (schemaObject.getDataObjectType() == dotTable){ 894 if (!tableCollationCaseSensitive.get(this.getDBVendor())){ 895 newSchemaName = newSchemaName.toUpperCase(); 896 } 897 }else { 898 if (!defaultCollationCaseSensitive.get(this.getDBVendor())){ 899 newSchemaName = newSchemaName.toUpperCase(); 900 } 901 } 902 903 if (schemaObject.getDataObjectType() == dotFunction){ 904 newSchemaName = newSchemaName+"$function"; 905 }else if (schemaObject.getDataObjectType() == dotProcedure){ 906 newSchemaName = newSchemaName+"$procedure"; 907 } 908 909 schemaObjectList.put(newSchemaName,schemaObject); 910 911 // ===== Phase 3.5: Write to catalog provider (no redundancy) ===== 912 try { 913 catalogProvider.addObject(schemaObject); 914 } catch (Throwable e) { 915 // Log error but maintain backward compatibility 916 logger.error("[CatalogProvider] Error adding object: " + schemaObject + ", error=" + e.getMessage(), e); 917 } 918 919 // Maintain fast index 920 try { 921 List<String> parts = SQLUtil.parseNames(schemaObjectName); 922 if (parts.size() >= 3) { 923 String catalog = this.normalizeIdentifier(ESQLDataObjectType.dotCatalog, parts.get(0)); 924 String schema = this.normalizeIdentifier(ESQLDataObjectType.dotSchema, parts.get(1)); 925 String object = this.normalizeIdentifier( 926 (schemaObject.getDataObjectType() == ESQLDataObjectType.dotColumn) ? ESQLDataObjectType.dotTable : schemaObject.getDataObjectType(), 927 SQLUtil.mergeSegments(parts, 2)); 928 String server = (getDefaultServerName() == null) ? DEFAULT_SERVER_NAME : getDefaultServerName(); 929 NameKey key = new NameKey(schemaObject.getDataObjectType(), server, catalog, schema, object); 930 objectIndex.put(key, schemaObject); 931 932 if (schemaObject instanceof TSQLTable) { 933 String objOnly = this.normalizeIdentifier(ESQLDataObjectType.dotTable, getObjectName(schemaObjectName)); 934 List<TSQLTable> list = tablesByName.get(objOnly); 935 if (list == null) { 936 list = new ArrayList<TSQLTable>(); 937 tablesByName.put(objOnly, list); 938 } 939 list.add((TSQLTable) schemaObject); 940 } 941 } 942 } catch (Throwable ignore) { 943 // keep backward compatibility even if fast index building fails 944 } 945 return true; 946 } 947 948 /** 949 * add a table 950 * 951 * @param qualifiedTableName, must be in syntax like: catalog.schema.table 952 * @param sqlTable, table instance 953 */ 954// public void addSQLTable(String qualifiedTableName, TSQLTable sqlTable){ 955// schemaObjectList.put(qualifiedTableName,sqlTable); 956// } 957// 958// public void addRoutine(String qualifiedRoutineName, TSQLRoutine sqlRoutine){ 959// schemaObjectList.put(qualifiedRoutineName,sqlRoutine); 960// } 961 962 public static final String DEFAULT_SERVER_NAME = "DEFAULT_SERVER"; 963 public static final String DEFAULT_DB_NAME = "DEFAULT"; 964 public static final String DEFAULT_SCHEMA_NAME = "DEFAULT"; 965 966 private String defaultCatalogName = null; 967 private String defaultSchemaName = null; 968 private String defaultServerName = null; 969 970 protected TSQLSchemaObject doSearchSchemaObject(String catalog, String schema, String table, ESQLDataObjectType objectType){ 971 TSQLSchemaObject result = null; 972 973 String normalizedCurrentCatalogName = this.normalizeIdentifier(ESQLDataObjectType.dotCatalog, defaultCatalogName); 974 String normalizedCurrentSchemaName = this.normalizeIdentifier(ESQLDataObjectType.dotSchema, defaultSchemaName); 975 976 if ((catalog.length()>0)&&(schema.length()>0)){ // catalog.schema.table 977 result = doSearchSchemaObject(catalog+"."+schema+"."+table,objectType); 978 }else if (schema.length()>0){ //.schema.table 979 if (defaultCatalogName != null){ 980 result = doSearchSchemaObject(normalizedCurrentCatalogName+"."+schema+"."+table,objectType); 981 }else{ 982 for(TSQLCatalog c : catalogList){ 983 result = doSearchSchemaObject(c.name+"."+schema+"."+table,objectType); 984 if (result != null) break; 985 } 986 } 987 }else if (catalog.length()>0){ // catalog..table 988 if (defaultSchemaName != null){ 989 result = doSearchSchemaObject(catalog+"."+normalizedCurrentSchemaName+"."+table,objectType); 990 }else{ 991 for(TSQLCatalog c : catalogList){ 992 if ( c.compareTo(catalog) != 0) continue; 993 for(TSQLSchema s: c.getSchemaList()){ 994 result = doSearchSchemaObject(s.getQualifiedName()+"."+table,objectType); 995 if (result != null) break; 996 } 997 if (result != null) break; 998 } 999 } 1000 }else{ // ..table, search under the current default database and schema. 1001 // The current default database and schema can be set by the user. 1002 // if current default database and schema is not set, then search in all databases and schemas 1003 if ((objectType == dotTable) && (defaultCatalogName == null) && (defaultSchemaName == null)){ 1004 String canonicalTable = this.normalizeIdentifier(ESQLDataObjectType.dotTable, table); 1005 List<TSQLTable> candidates = tablesByName.get(canonicalTable); 1006 if (candidates != null && !candidates.isEmpty()){ 1007 return candidates.get(0); 1008 } 1009 } 1010 for(TSQLCatalog c : catalogList){ 1011 if ((defaultCatalogName != null) && ( c.compareTo(defaultCatalogName) != 0)) continue; 1012 for(TSQLSchema s: c.getSchemaList()){ 1013 if ((defaultSchemaName != null)&&(s.compareTo(defaultSchemaName) != 0)) continue; 1014 result = doSearchSchemaObject(s.getQualifiedName()+"."+table,objectType); 1015 if (result != null) break; 1016 } 1017 if (result != null) break; 1018 } 1019 } 1020 1021 return result; 1022 } 1023 1024 protected TSQLSchemaObject searchSchemaObject(TObjectName qualifiedName, ESQLDataObjectType objectType){ 1025 1026 String catalog = this.normalizeIdentifier(ESQLDataObjectType.dotCatalog, qualifiedName.getDatabaseString()); 1027 String schema = this.normalizeIdentifier(ESQLDataObjectType.dotSchema, qualifiedName.getSchemaString()); 1028 String table = this.normalizeIdentifier(ESQLDataObjectType.dotTable, qualifiedName.getTableString()); 1029 1030 schema = (schema == null)?getDefaultSchemaName():schema; 1031 schema = (schema == null)?"":schema; 1032 1033 catalog = (catalog == null)?getDefaultCatalogName():catalog; 1034 catalog = (catalog == null)?"":catalog; 1035 1036 return doSearchSchemaObject(catalog,schema,table,objectType); 1037 } 1038 1039 /** 1040 * Phase 1: 使用 IdentifierService 的新方法处理多段名 1041 */ 1042 public TSQLSchemaObject searchSchemaObject(String qualifiedName, ESQLDataObjectType objectType){ 1043 // Phase 1: 使用 IdentifierService 的新方法 1044 IdentifierService svc = getIdentifierService(); 1045 1046 // 1. 解析多段名 1047 List<String> parts = svc.parseQualifiedName(qualifiedName); 1048 1049 // 2. 展开厂商特定语法(MSSQL "..") 1050 // Pass the default schema name to avoid relying on global state 1051 parts = svc.expandVendorSpecific(parts, dbVendor, getDefaultSchemaName()); 1052 1053 if (parts.size() < 3) { 1054 return null; 1055 } 1056 1057 boolean supportCatalog = TSQLEnv.supportCatalog(dbVendor); 1058 boolean supportSchema = TSQLEnv.supportSchema(dbVendor); 1059 1060 // 3. 使用 normalizeSegment 规范化各段(Phase 1: 单段处理) 1061 String catalog = svc.normalizeSegment(parts.get(0), ESQLDataObjectType.dotCatalog); 1062 String schema = svc.normalizeSegment(parts.get(1), ESQLDataObjectType.dotSchema); 1063 String table = svc.normalizeSegment(SQLUtil.mergeSegments(parts, 2), ESQLDataObjectType.dotTable); 1064 1065 TSQLSchemaObject object = doSearchSchemaObject(catalog,schema,table,objectType); 1066 1067 //如果不是同时支持database和schema,且没有找到object,交换database和schema顺序查找 1068 if (!(supportCatalog && supportSchema) && object == null) { 1069 catalog = svc.normalizeSegment(parts.get(1), ESQLDataObjectType.dotCatalog); 1070 schema = svc.normalizeSegment(parts.get(0), ESQLDataObjectType.dotSchema); 1071 object = doSearchSchemaObject(catalog, schema, table, objectType); 1072 } 1073 1074 return object; 1075 1076// String normalizedCurrentCatalogName = TSQLObject.normalizeIdentifier(this,ESQLDataObjectType.dotCatalog, defaultCatalogName); 1077// String normalizedCurrentSchemaName = TSQLObject.normalizeIdentifier(this,ESQLDataObjectType.dotSchema, defaultSchemaName); 1078// 1079// if ((catalog.length()>0)&&(schema.length()>0)){ // catalog.schema.table 1080// result = doSearchSchemaObject(catalog+"."+schema+"."+table,objectType); 1081// }else if (schema.length()>0){ //.schema.table 1082// if (defaultCatalogName != null){ 1083// result = doSearchSchemaObject(normalizedCurrentCatalogName+"."+schema+"."+table,objectType); 1084// }else{ 1085// for(TSQLCatalog c : catalogList){ 1086// result = doSearchSchemaObject(c.name+"."+schema+"."+table,objectType); 1087// if (result != null) break; 1088// } 1089// } 1090// }else if (catalog.length()>0){ // catalog..table 1091// if (defaultSchemaName != null){ 1092// result = doSearchSchemaObject(catalog+"."+normalizedCurrentSchemaName+"."+table,objectType); 1093// }else{ 1094// for(TSQLCatalog c : catalogList){ 1095// if ( c.compareTo(catalog) != 0) continue; 1096// for(TSQLSchema s: c.getSchemaList()){ 1097// result = doSearchSchemaObject(s.getQualifiedName()+"."+table,objectType); 1098// if (result != null) break; 1099// } 1100// if (result != null) break; 1101// } 1102// } 1103// }else{ // ..table 1104// for(TSQLCatalog c : catalogList){ 1105// if ((defaultCatalogName != null) && ( c.compareTo(defaultCatalogName) != 0)) continue; 1106// for(TSQLSchema s: c.getSchemaList()){ 1107// if ((defaultSchemaName != null)&&(s.compareTo(defaultSchemaName) != 0)) continue; 1108// result = doSearchSchemaObject(s.getQualifiedName()+"."+table,objectType); 1109// if (result != null) break; 1110// } 1111// if (result != null) break; 1112// } 1113// } 1114// 1115// return result; 1116 1117 } 1118 /** 1119 * find a table in the SQL environment by using a qualified table name: catalogName.schemaName.tableName 1120 * 1121 * @param qualifiedTablename, can be catalog.schema.table, 1122 * or .schema.table, use currentCatalogName or iterate all catalogs 1123 * or catalog..table, use current schema name or iterate all schema under catalog 1124 * or ..table, use currentCatalogName or iterate all catalogs, use current schema 1125 * or iterate all schema 1126 * @return a table 1127 */ 1128 public TSQLTable searchTable(String qualifiedTablename){ 1129 TSQLSchemaObject result = searchSchemaObject(qualifiedTablename,dotTable); 1130 1131 if (result instanceof TSQLTable){ 1132 return (TSQLTable)result; 1133 }else return null; 1134 1135 } 1136 1137 public TSQLTable searchTable(TObjectName tableName){ 1138 if ((tableName.getSchemaToken() == null) && (tableName.getDatabaseToken() == null)) return searchTable(".."+tableName.getTableString()); 1139 if (tableName.getDatabaseToken() == null) return searchTable("."+tableName.getSchemaString()+"."+tableName.getTableString()); 1140 if (tableName.getSchemaToken() == null) return searchTable(tableName.getDatabaseString()+".."+tableName.getTableString()); 1141 return searchTable(tableName.toString()); 1142 } 1143 /** 1144 * called by {@link #searchTable(String)} method internally. 1145 * 1146 * @param qualifiedName table name 1147 * @return return a table instance if found, otherwise, return null 1148 */ 1149// TSQLTable doSearchTable(String qualifiedTablename){ 1150// TSQLTable result = null; 1151// TSQLSchemaObject schemaObject = schemaObjectList.get(qualifiedTablename); 1152// 1153// if (schemaObject instanceof TSQLTable){ 1154// result = (TSQLTable)schemaObject; 1155// } 1156// return result; 1157// } 1158 1159 /** 1160 * Phase 1: 使用 IdentifierService 新方法 + 双写日志 1161 */ 1162 private TSQLSchemaObject doSearchSchemaObject( String qualifiedName, ESQLDataObjectType objectType){ 1163 // Phase 1: 使用 IdentifierService 1164 IdentifierService svc = getIdentifierService(); 1165 1166 // ===== Phase 3: Try catalog provider first ===== 1167 try { 1168 List<String> parts = svc.parseQualifiedName(qualifiedName); 1169 parts = svc.expandVendorSpecific(parts, dbVendor); 1170 1171 if (parts.size() >= 3) { 1172 // Phase 1: 使用 normalizeSegment 规范化单段名 1173 String catalog = svc.normalizeSegment(parts.get(0), ESQLDataObjectType.dotCatalog); 1174 String schema = svc.normalizeSegment(parts.get(1), ESQLDataObjectType.dotSchema); 1175 String object = svc.normalizeSegment(SQLUtil.mergeSegments(parts, 2), objectType); 1176 1177 TSQLSchemaObject result = catalogProvider.findObject(catalog, schema, object, objectType); 1178 if (result != null) { 1179 return result; 1180 } 1181 } 1182 } catch (Throwable e) { 1183 // Log error but continue to fallback paths 1184 // System.err.println("[CatalogProvider] Search error: " + e.getMessage()); 1185 } 1186 1187 // Fast-path using canonical index when fully qualified 1188 try { 1189 List<String> parts = svc.parseQualifiedName(qualifiedName); 1190 parts = svc.expandVendorSpecific(parts, dbVendor); 1191 1192 if (parts.size() >= 3) { 1193 String server = (getDefaultServerName() == null) ? DEFAULT_SERVER_NAME : getDefaultServerName(); 1194 1195 // Phase 1: 使用新方法规范化 1196 String catalogNew = svc.normalizeSegment(parts.get(0), ESQLDataObjectType.dotCatalog); 1197 String schemaNew = svc.normalizeSegment(parts.get(1), ESQLDataObjectType.dotSchema); 1198 String objectNew = svc.normalizeSegment(SQLUtil.mergeSegments(parts, 2), 1199 (objectType == ESQLDataObjectType.dotColumn) ? ESQLDataObjectType.dotTable : objectType); 1200 1201 // Phase 1: 双写日志 - 比较新旧键 1202 if (TBaseType.LOG_KEY_COMPARISON) { 1203 String catalogOld = this.normalizeIdentifier(ESQLDataObjectType.dotCatalog, parts.get(0)); 1204 String schemaOld = this.normalizeIdentifier(ESQLDataObjectType.dotSchema, parts.get(1)); 1205 String objectOld = this.normalizeIdentifier( 1206 (objectType == ESQLDataObjectType.dotColumn) ? ESQLDataObjectType.dotTable : objectType, 1207 SQLUtil.mergeSegments(parts, 2)); 1208 1209 if (!catalogNew.equals(catalogOld) || !schemaNew.equals(schemaOld) || !objectNew.equals(objectOld)) { 1210 System.err.println("[Phase1] Key mismatch in " + qualifiedName + " (" + objectType + ")"); 1211 System.err.println(" catalog: OLD=" + catalogOld + " NEW=" + catalogNew); 1212 System.err.println(" schema: OLD=" + schemaOld + " NEW=" + schemaNew); 1213 System.err.println(" object: OLD=" + objectOld + " NEW=" + objectNew); 1214 } 1215 } 1216 1217 NameKey key = new NameKey(objectType, server, catalogNew, schemaNew, objectNew); 1218 TSQLSchemaObject hit = objectIndex.get(key); 1219 if (hit != null) { 1220 return hit; 1221 } 1222 } 1223 } catch (Throwable ignore) { 1224 // fallback to legacy map below 1225 } 1226 String newSchemaName = qualifiedName; 1227 if (objectType == dotTable){ 1228 if (!tableCollationCaseSensitive.get(this.getDBVendor())){ 1229 newSchemaName = newSchemaName.toUpperCase(); 1230 } 1231 }else { 1232 if (!defaultCollationCaseSensitive.get(this.getDBVendor())){ 1233 newSchemaName = newSchemaName.toUpperCase(); 1234 } 1235 } 1236 1237 if (objectType == dotFunction){ 1238 newSchemaName = newSchemaName+"$function"; 1239 }else if (objectType == dotProcedure){ 1240 newSchemaName = newSchemaName+"$procedure"; 1241 } 1242 return schemaObjectList.get(newSchemaName); 1243 } 1244 1245 /** 1246 * the current active catalog/database in the SQL environment. 1247 * If search a table in syntax like this: .schemaName.tableName and this current catalog name is not null, 1248 * then, search the table in this syntax: currentCatalogName.schemaName.tableName. 1249 * If the current catalog name is null, search all catalogs in the catalog list. 1250 * 1251 * @return the name of the current active catalog/database 1252 */ 1253 public String getDefaultCatalogName() { 1254 return defaultCatalogName; 1255 } 1256 1257 /** 1258 * the current schema name in the SQL environment. 1259 * <br> 1260 * If search a table in syntax like this: catalogname..tableName and this current schema name is not null, 1261 * then, search the table in this syntax: catalogName.currentSchemaName.tableName. 1262 * If the current schema name is null, search all schemas under catalogName 1263 * 1264 * @return the default schema name 1265 */ 1266 public String getDefaultSchemaName() { 1267// if ((dbVendor == EDbVendor.dbvmssql)&&(defaultSchemaName == null)){ 1268// return "dbo"; 1269// } 1270 return defaultSchemaName; 1271 } 1272 1273 public void setDefaultCatalogName(String defaultCatalogName) { 1274 this.defaultCatalogName = defaultCatalogName; 1275 } 1276 1277 public void setDefaultSchemaName(String defaultSchemaName) { 1278 this.defaultSchemaName = defaultSchemaName; 1279 } 1280 1281 public String getDefaultServerName() { 1282 return defaultServerName; 1283 } 1284 1285 public void setDefaultServerName(String defaultServerName) { 1286 this.defaultServerName = defaultServerName; 1287 } 1288 1289 1290 1291 /** 1292 * create a new catalog/database in the SQL environment. 1293 * 1294 * @param catalogName catalog name 1295 * @return instance of the created catalog 1296 */ 1297 public TSQLCatalog createSQLCatalog(String catalogName){ 1298 return getSQLCatalog(catalogName,true); 1299 } 1300 1301 /** 1302 * get a catalog from the SQL environment if already exists, otherwise, create a new catalog. 1303 * 1304 * @param catalogName catalog name 1305 * @param createIfNotExist if this value is true, then create a new catalog if it's not exists 1306 * @return a catalog instance 1307 */ 1308 public TSQLCatalog getSQLCatalog(String catalogName, boolean createIfNotExist){ 1309 TSQLCatalog result = searchCatalog(catalogName); 1310 1311 if ((createIfNotExist)&&(result == null)){ 1312 result = new TSQLCatalog(this,catalogName); 1313 } 1314 return result; 1315 } 1316 1317 /** 1318 * search catalog in the catalog list, return null if not found. 1319 * 1320 * @param catalogName catalog name 1321 * @return null if not found. 1322 */ 1323 public TSQLCatalog searchCatalog(String catalogName){ 1324 TSQLCatalog result = null; 1325 for(TSQLCatalog c : catalogList){ 1326 if (c.compareTo(catalogName)==0){ 1327 result = c; 1328 break; 1329 } 1330 } 1331 return result; 1332 } 1333 1334 /** 1335 * create a new schema and add to the catalog 1336 * 1337 * @param qualifiedSchemaName must be a qualified name like: catalog.schema. Otherwise, a null exception will be raised. 1338 * @return a new schema instance 1339 */ 1340 public TSQLSchema createSQLSchema(String qualifiedSchemaName){ 1341 return getSQLSchema(qualifiedSchemaName,true); 1342 } 1343 1344 /** 1345 * get a schema from the specified catalog, if not exists, create a new schema in the catalog. 1346 * 1347 * @param qualifiedSchemaName must be a qualified name like: catalog.schema. Otherwise, a null exception will be raised. 1348 * @param createIfNotExist if this value is true, then create a new schema if it's not exists 1349 * @return a schema instance 1350 */ 1351 public TSQLSchema getSQLSchema(String qualifiedSchemaName, boolean createIfNotExist){ 1352 TSQLSchema result = null; 1353 String[] parts = SQLUtil.parseNames(qualifiedSchemaName).toArray(new String[0]); 1354 String catalogName = parts[0]; 1355 String schemaName = parts[1]; 1356 TSQLCatalog catalog = getSQLCatalog(catalogName,createIfNotExist); 1357 return catalog.getSchema(schemaName,createIfNotExist); 1358 } 1359 1360 1361 /** 1362 * 1363 * @param qualifiedTablename 需要完整的tablename,例如 catalog.schema.table, 如果仅传入 table name, 1364 * 需要利用 default catalog, default schema 拼接完整的名称: catalog.schema.table 1365 * @param columnNameOnly 1366 * @return 1367 */ 1368 public ArrayList<String> getColumnsInTable(String qualifiedTablename,boolean columnNameOnly){ 1369 1370 //TSQLTable tsqlTable = searchTable(getAFullQualifiedSchemaObjectName(qualifiedTablename)); 1371 TSQLTable tsqlTable = searchTable(qualifiedTablename); 1372 if (tsqlTable == null) return null; 1373 //return tsqlTable.searchColumn(columnName); 1374 return tsqlTable.getColumns(columnNameOnly); 1375 } 1376 1377 public boolean columnInTable(String qualifiedTablename, String columnName){ 1378 TSQLTable tsqlTable = searchTable(qualifiedTablename); 1379 if (tsqlTable == null) return false; 1380 return tsqlTable.searchColumn(columnName); 1381 } 1382 1383 public TSQLColumn getColumnInTable(String qualifiedTablename, String columnName){ 1384 StringBuilder builder = new StringBuilder(); 1385 String db = (getDatabaseName(qualifiedTablename) == null)?getDefaultCatalogName():getDatabaseName(qualifiedTablename); 1386 if((db == null) || (db.length() == 0)) { 1387 builder.append(DEFAULT_DB_NAME).append("."); 1388 } 1389 1390 String schema = (getSchemaName(qualifiedTablename) == null)?getDefaultSchemaName():getSchemaName(qualifiedTablename); 1391 if ((schema == null)||(schema.length() == 0)) { 1392 builder.append(DEFAULT_SCHEMA_NAME).append("."); 1393 } 1394 1395 builder.append(qualifiedTablename); 1396 1397 TSQLTable tsqlTable = searchTable(builder.toString()); 1398 if (tsqlTable == null) return null; 1399 return tsqlTable.getColumn(columnName); 1400 } 1401 1402// String[] getCatalogSchemaNameWithDefault(String qualifiedObjectName){ 1403// String[] catalogSchemaName = new String[2]; 1404// 1405// if (qualifiedObjectName.startsWith("`") && qualifiedObjectName.endsWith("`")){ 1406// qualifiedObjectName = qualifiedObjectName.substring(1,qualifiedObjectName.length()-1); 1407// } 1408// 1409// String db = (getDatabaseName(qualifiedObjectName) == null)?getDefaultCatalogName():getDatabaseName(qualifiedObjectName); 1410// if((db == null) || (db.length() == 0)) db = DEFAULT_DB_NAME; 1411// String schema = (getSchemaName(qualifiedObjectName) == null)?getDefaultSchemaName():getSchemaName(qualifiedObjectName); 1412// if ((schema == null)||(schema.length() == 0)) schema = DEFAULT_SCHEMA_NAME; 1413// 1414// catalogSchemaName[0] = db; 1415// catalogSchemaName[1] = schema; 1416// 1417// return catalogSchemaName; 1418// } 1419 public TSQLSchemaObject doAddSchemaObject(String qualifiedObjectName,ESQLDataObjectType objectType){ 1420 1421 if (qualifiedObjectName.startsWith("`") && qualifiedObjectName.endsWith("`")){ 1422 qualifiedObjectName = SQLUtil.trimColumnStringQuote(qualifiedObjectName); 1423 } 1424 1425 String db = (getDatabaseName(qualifiedObjectName) == null)?getDefaultCatalogName():getDatabaseName(qualifiedObjectName); 1426 if((db == null) || (db.length() == 0)) db = DEFAULT_DB_NAME; 1427 String schema = (getSchemaName(qualifiedObjectName) == null)?getDefaultSchemaName():getSchemaName(qualifiedObjectName); 1428 if ((schema == null)||(schema.length() == 0)) schema = DEFAULT_SCHEMA_NAME; 1429 1430 // getCatalogSchemaNameWithDefault 这个函数有问题,为导致测试用例失败 1431 // String[] catalogSchemaName = getCatalogSchemaNameWithDefault(qualifiedObjectName); 1432// String db = catalogSchemaName[0]; 1433// String schema = catalogSchemaName[1]; 1434 1435 TSQLCatalog sqlCatalog = createSQLCatalog(db); 1436 TSQLSchema sqlSchema = sqlCatalog.createSchema(schema); 1437 TSQLSchemaObject created = sqlSchema.createSchemaObject(getObjectName(qualifiedObjectName),objectType); 1438 // Ensure fast index contains the new object as well (already handled via putSchemaObject called by schema) 1439 return created; 1440 } 1441 1442 public TSQLSchemaObject doAddSchemaObject(TObjectName qualifiedObjectName,ESQLDataObjectType objectType){ 1443 String db = (getDatabaseName(qualifiedObjectName) == null)?getDefaultCatalogName():getDatabaseName(qualifiedObjectName); 1444 if((db == null) || (db.length() == 0)) db = DEFAULT_DB_NAME; 1445 String schema = (getSchemaName(qualifiedObjectName) == null)?getDefaultSchemaName():getSchemaName(qualifiedObjectName); 1446 if ((schema == null)||(schema.length() == 0)) schema = DEFAULT_SCHEMA_NAME; 1447 TSQLCatalog sqlCatalog = createSQLCatalog(db); 1448 TSQLSchema sqlSchema = sqlCatalog.createSchema(schema); 1449 return sqlSchema.createSchemaObject(getObjectName(qualifiedObjectName),objectType); 1450 } 1451 1452 /** 1453 * Add a function to SQLEnv, if a function with the same already exists, just return the existing one. 1454 * 1455 * @param qualifiedFunctionName 1456 * @return function 1457 */ 1458 public TSQLFunction addFunction(String qualifiedFunctionName, boolean fromDDL){ 1459 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1460 return (TSQLFunction)doAddSchemaObject(qualifiedFunctionName,ESQLDataObjectType.dotFunction); 1461 } 1462 1463 public TSQLFunction addFunction(TObjectName qualifiedFunctionName, boolean fromDDL){ 1464 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1465 return (TSQLFunction)doAddSchemaObject(qualifiedFunctionName,ESQLDataObjectType.dotFunction); 1466 } 1467 1468 public TSQLProcedure addOraclePackage(String qualifiedProcedureName, boolean fromDDL){ 1469 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1470 return (TSQLProcedure)doAddSchemaObject(qualifiedProcedureName, ESQLDataObjectType.dotOraclePackage); 1471 } 1472 1473 public TSQLProcedure addProcedure(String qualifiedProcedureName, boolean fromDDL){ 1474 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1475 return (TSQLProcedure)doAddSchemaObject(qualifiedProcedureName,ESQLDataObjectType.dotProcedure); 1476 } 1477 1478 public TSQLRoutine addSQLRoutine(String qualifiedProcedureName, boolean fromDDL, ESQLDataObjectType type){ 1479 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1480 return (TSQLRoutine)doAddSchemaObject(qualifiedProcedureName,type); 1481 } 1482 1483 public TSQLTrigger addTrigger(String qualifiedTriggerName, boolean fromDDL){ 1484 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1485 return (TSQLTrigger)doAddSchemaObject(qualifiedTriggerName,ESQLDataObjectType.dotTrigger); 1486 } 1487 1488 /** 1489 * Add a new table to the SQLEnv. If the table with the same name already exists in the SQLEnv, 1490 * the existing table will be returned. 1491 * <br>dbName.schemaName.tablename. If dbName is not specified, {@link #getDefaultCatalogName()} will be used to put this table in. 1492 * If schemaName is not specified, {@link #getDefaultSchemaName()} will be used to put this table in. 1493 * <br>If the specified database and schema are not already exists, a new database/schema will be generated automatically. 1494 * 1495 * @param qualifiedTableName qualified table name 1496 * @param fromDDL is this table generated from a create table statement 1497 * 1498 * @return SQL Table 1499 */ 1500 public TSQLTable addTable(String qualifiedTableName, boolean fromDDL){ 1501 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1502 return (TSQLTable)doAddSchemaObject(qualifiedTableName,ESQLDataObjectType.dotTable); 1503 } 1504 1505 public TSQLTable addView(String qualifiedViewName, boolean fromDDL){ 1506 if ((fromDDL)&&(!isEnableGetMetadataFromDDL())) return null; 1507 TSQLTable tsqlTable = (TSQLTable)doAddSchemaObject(qualifiedViewName,ESQLDataObjectType.dotTable); 1508 tsqlTable.setView(true); 1509 return tsqlTable; 1510 } 1511 1512 public static String getObjectName(TObjectName schemaObjectName){ 1513 if (schemaObjectName.getObjectString().length() == 0) return null; 1514 else 1515 return schemaObjectName.getObjectString(); 1516 } 1517 1518 public static String getObjectName(String schemaObjectName){ 1519 String[] names = SQLUtil.parseNames(schemaObjectName).toArray(new String[0]); 1520 return names[names.length-1]; 1521 } 1522 1523 public static String getDatabaseName(TObjectName schemaObjectName){ 1524 if (schemaObjectName.getDatabaseString().length() == 0) 1525 return null; 1526 else 1527 return schemaObjectName.getDatabaseString(); 1528 } 1529 1530 public static String getDatabaseName(String schemaObjectName){ 1531 String[] names = SQLUtil.parseNames(schemaObjectName).toArray(new String[0]); 1532 if (names.length == 3){ 1533 return names[0]; 1534 }else if (names.length == 4){ 1535 return names[1]; 1536 }else{ 1537 return null; 1538 } 1539 } 1540 1541 public static String getSchemaName(TObjectName schemaObjectName){ 1542 if (schemaObjectName.getSchemaString().length() == 0) return null; 1543 else 1544 return schemaObjectName.getSchemaString(); 1545 } 1546 1547 public static String getSchemaName(String schemaObjectName){ 1548 String[] names = SQLUtil.parseNames(schemaObjectName).toArray(new String[0]); 1549 if (names.length == 2){ 1550 return names[0]; 1551 }else if (names.length == 3){ 1552 return names[1]; 1553 }else if (names.length == 4){ 1554 return names[2]; 1555 }else{ 1556 return null; 1557 } 1558 } 1559 1560 public TSQLFunction searchFunction(TObjectName qualifiedTablename) { 1561 TSQLSchemaObject result = searchSchemaObject(qualifiedTablename,dotFunction); 1562 1563 if (result instanceof TSQLFunction) { 1564 return (TSQLFunction) result; 1565 } else return null; 1566 } 1567 1568 public TSQLFunction searchFunction(String qualifiedTablename) { 1569 TSQLSchemaObject result = searchSchemaObject( getAFullQualifiedSchemaObjectName(qualifiedTablename),dotFunction); 1570 1571 if (result instanceof TSQLFunction) { 1572 return (TSQLFunction) result; 1573 } else return null; 1574 } 1575 1576 /** 1577 * If the input name is fully qualified with db and schema, just return it without any modification. 1578 * If the schema name is missed, use {@link #getDefaultSchemaName()} instead 1579 * If the database name is missed, use {@link #getDefaultCatalogName()} instead 1580 * 1581 * some sample result: 1582 * <br> 1583 * db.schema.table 1584 * .schema.table, {@link #getDefaultCatalogName()} is null 1585 * ..table, both {@link #getDefaultCatalogName()} and {@link #getDefaultSchemaName()} are null. 1586 * 1587 * @param schemaObjectName 1588 * @return 1589 */ 1590 public String getAFullQualifiedSchemaObjectName(String schemaObjectName){ 1591 if (SQLUtil.parseNames(schemaObjectName).size() == 3) return schemaObjectName; 1592 String schema = (getDefaultSchemaName() == null)?"":getDefaultSchemaName(); 1593 String db = (getDefaultCatalogName() == null)?"":getDefaultCatalogName(); 1594 1595 String[] names = SQLUtil.parseNames(schemaObjectName).toArray(new String[0]); 1596 if (names.length == 1){ // no prefixed schema, db 1597 return db+"."+schema+"."+schemaObjectName; 1598 }else if (names.length == 2){ // prefix with schema, no database 1599 return db+"."+names[0]+"."+names[1]; 1600 }else { 1601 return schemaObjectName; 1602 } 1603 } 1604 1605 1606 1607 public int getNumberOfTables(){ 1608 int numOfTables = 0; 1609 for(int i=0;i<getCatalogList().size();i++){ 1610 TSQLCatalog tsqlCatalog = getCatalogList().get(i); 1611 for(TSQLSchema tsqlSchema: tsqlCatalog.getSchemaList()){ 1612 for(TSQLSchemaObject schemaObject: tsqlSchema.getSchemaObjectList()){ 1613 switch (schemaObject.getDataObjectType()){ 1614 case dotTable: 1615 numOfTables++; 1616 break; 1617 case dotProcedure: 1618 break; 1619 case dotFunction: 1620 break; 1621 case dotOraclePackage: 1622 break; 1623 } 1624 } //schema object list 1625 } // schema list 1626 } //catalog list 1627 return numOfTables; 1628 } 1629 1630 public String toString(){ 1631 String lcResult = ""; 1632 StringBuilder tables = new StringBuilder(); 1633 StringBuilder views = new StringBuilder(); 1634 StringBuilder functions = new StringBuilder(); 1635 StringBuilder oraclePackages = new StringBuilder(); 1636 StringBuilder procedures = new StringBuilder(); 1637 for(int i=0;i<getCatalogList().size();i++){ 1638 TSQLCatalog tsqlCatalog = getCatalogList().get(i); 1639 lcResult = lcResult+"\ndatabase:"+tsqlCatalog.getName(); 1640 for(TSQLSchema tsqlSchema: tsqlCatalog.getSchemaList()){ 1641 lcResult = lcResult+"\n\t"+"schema:"+tsqlSchema.getName(); 1642 for(TSQLSchemaObject schemaObject: tsqlSchema.getSchemaObjectList()){ 1643 switch (schemaObject.getDataObjectType()){ 1644 case dotTable: 1645 TSQLTable tsqlTable = (TSQLTable)schemaObject; 1646 if (tsqlTable.isView()){ 1647 views.append("\n\t\t\t"+schemaObject.getName()); 1648 for(TSQLColumn column: tsqlTable.getColumnList()){ 1649 views.append("\n\t\t\t\t"+column.getName()); 1650 1651 } 1652 }else{ 1653 tables.append("\n\t\t\t"+schemaObject.getName()); 1654 for(TSQLColumn column: tsqlTable.getColumnList()){ 1655 tables.append("\n\t\t\t\t"+column.getName()); 1656 if (column.getColumnDataType() != null){ 1657 tables.append(",\t"+column.getColumnDataType().toString()); 1658 TTypeName subType; 1659 switch (column.getColumnDataType().getDataType()){ 1660 case array_t: 1661 subType = column.getColumnDataType().getTypeOfList(); 1662 1663 if (subType.getDataType() == EDataType.struct_t){ 1664 1665 for(int k=0;k<subType.getColumnDefList().size();k++){ 1666 //System.out.println(subType.getColumnDefList().getColumn(k).getColumnName()); 1667 //System.out.println(subType.getColumnDefList().getColumn(k).getDatatype()); 1668 tables.append("\n\t\t\t\t\t\t"+ subType.getColumnDefList().getColumn(k).getColumnName().toString() 1669 +",\t"+ subType.getColumnDefList().getColumn(k).getDatatype().toString()); 1670 } 1671 } 1672 break; 1673 case struct_t: 1674 subType = column.getColumnDataType(); 1675 for(int k=0;k<subType.getColumnDefList().size();k++){ 1676 tables.append("\n\t\t\t\t\t\t"+ subType.getColumnDefList().getColumn(k).getColumnName().toString() 1677 +",\t"+ subType.getColumnDefList().getColumn(k).getDatatype().toString()); 1678 } 1679 break; 1680 } 1681 } 1682 } 1683 } 1684 1685 break; 1686 case dotProcedure: 1687 procedures.append("\n\t\t\t"+schemaObject.getName()); 1688 break; 1689 case dotFunction: 1690 functions.append("\n\t\t\t"+schemaObject.toString()); 1691 //TSQLFunction f = (TSQLFunction) schemaObject; 1692 1693 break; 1694 case dotOraclePackage: 1695 oraclePackages.append("\n\t\t\t"+schemaObject.getName()); 1696 break; 1697 } 1698 } //schema object list 1699 lcResult = lcResult+"\n\t\t"+"tables:"+tables.toString(); 1700 lcResult = lcResult+"\n\t\t"+"views:"+views.toString(); 1701 if(oraclePackages.length()>0) { 1702 lcResult = lcResult + "\n\t\t" + "oracle package:" + oraclePackages.toString(); 1703 } 1704 lcResult = lcResult+"\n\t\t"+"procedure:"+procedures.toString(); 1705 lcResult = lcResult+"\n\t\t"+"function:"+functions.toString(); 1706 tables.setLength(0); 1707 views.setLength(0); 1708 procedures.setLength(0); 1709 functions.setLength(0); 1710 } // schema list 1711 } //catalog list 1712 return lcResult; 1713 } 1714 1715 public String getProcedureParameterValue(String procedureName, int paramPos){ 1716 return ""; 1717 } 1718 public void getVariableValue(String variableName, String variableValue){} 1719 1720 private static boolean usedBySqlflow = false; 1721 1722 public static boolean isUsedBySqlflow() { 1723 return usedBySqlflow; 1724 } 1725}