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