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