1 /* Copyright (c) 2001-2016, The HSQL Development Group 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of the HSQL Development Group nor the names of its 15 * contributors may be used to endorse or promote products derived from this 16 * software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, 22 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 32 package org.hsqldb; 33 34 import org.hsqldb.error.Error; 35 import org.hsqldb.error.ErrorCode; 36 import org.hsqldb.lib.StringConverter; 37 import org.hsqldb.rights.Grantee; 38 39 import java.util.concurrent.atomic.AtomicInteger; 40 41 /** 42 * Provides Name Management for SQL objects. <p> 43 * 44 * This class now includes the HsqlName class introduced in 1.7.1 and improves 45 * auto-naming with multiple databases in the engine.<p> 46 * 47 * Methods check user defined names and issue system generated names 48 * for SQL objects.<p> 49 * 50 * This class does not deal with the type of the SQL object for which it 51 * is used.<p> 52 * 53 * Some names beginning with SYS_ are reserved for system generated names. 54 * These are defined in isReserveName(String name) and created by the 55 * makeAutoName(String type) factory method<p> 56 * 57 * sysNumber is used to generate system-generated names. It is 58 * set to the largest integer encountered in names that use the 59 * SYS_xxxxxxx_INTEGER format. As the DDL is processed before any ALTER 60 * command, any new system generated name will have a larger integer suffix 61 * than all the existing names. 62 * 63 * @author Fred Toussi (fredt@users dot sourceforge.net) 64 * @version 2.3.2 65 * @since 1.7.2 66 */ 67 public final class HsqlNameManager { 68 69 private static final HsqlNameManager staticManager = new HsqlNameManager(); 70 71 static { 72 staticManager.serialNumber.set(Integer.MIN_VALUE); 73 } 74 75 private static final HsqlName[] autoColumnNames = new HsqlName[32]; 76 private static final String[] autoNoNameColumnNames = new String[32]; 77 78 static { 79 for (int i = 0; i < autoColumnNames.length; i++) { 80 autoColumnNames[i] = new HsqlName(staticManager, "C" + (i + 1), 0, 81 false); 82 autoNoNameColumnNames[i] = String.valueOf(i); 83 } 84 } 85 86 private AtomicInteger serialNumber = new AtomicInteger(1); // 0 is reserved in lookups 87 private int sysNumber = 10000; // avoid name clash in older scripts 88 private HsqlName catalogName; 89 private boolean sqlRegularNames; 90 HsqlName subqueryTableName; 91 HsqlNameManager()92 public HsqlNameManager() { 93 sqlRegularNames = true; 94 } 95 HsqlNameManager(Database database)96 public HsqlNameManager(Database database) { 97 98 catalogName = new HsqlName(this, SqlInvariants.DEFAULT_CATALOG_NAME, 99 SchemaObject.CATALOG, false); 100 sqlRegularNames = database.sqlRegularNames; 101 subqueryTableName = new HsqlName(this, SqlInvariants.SYSTEM_SUBQUERY, 102 false, SchemaObject.TABLE); 103 subqueryTableName.schema = SqlInvariants.SYSTEM_SCHEMA_HSQLNAME; 104 } 105 getCatalogName()106 public HsqlName getCatalogName() { 107 return catalogName; 108 } 109 setSqlRegularNames(boolean value)110 public void setSqlRegularNames(boolean value) { 111 sqlRegularNames = value; 112 } 113 newSystemObjectName(String name, int type)114 public static HsqlName newSystemObjectName(String name, int type) { 115 return new HsqlName(staticManager, name, type, false); 116 } 117 newInfoSchemaColumnName(String name, HsqlName table)118 public static HsqlName newInfoSchemaColumnName(String name, 119 HsqlName table) { 120 121 HsqlName hsqlName = new HsqlName(staticManager, name, false, 122 SchemaObject.COLUMN); 123 124 hsqlName.schema = SqlInvariants.INFORMATION_SCHEMA_HSQLNAME; 125 hsqlName.parent = table; 126 127 return hsqlName; 128 } 129 newInfoSchemaTableName(String name)130 public static HsqlName newInfoSchemaTableName(String name) { 131 132 HsqlName hsqlName = new HsqlName(staticManager, name, 133 SchemaObject.TABLE, false); 134 135 hsqlName.schema = SqlInvariants.INFORMATION_SCHEMA_HSQLNAME; 136 137 return hsqlName; 138 } 139 newInfoSchemaObjectName(String name, boolean isQuoted, int type)140 public static HsqlName newInfoSchemaObjectName(String name, 141 boolean isQuoted, int type) { 142 143 HsqlName hsqlName = new HsqlName(staticManager, name, type, isQuoted); 144 145 hsqlName.schema = SqlInvariants.INFORMATION_SCHEMA_HSQLNAME; 146 147 return hsqlName; 148 } 149 newHsqlName(HsqlName schema, String name, int type)150 public HsqlName newHsqlName(HsqlName schema, String name, int type) { 151 152 HsqlName hsqlName = new HsqlName(this, name, type, false); 153 154 hsqlName.schema = schema; 155 156 return hsqlName; 157 } 158 159 // newHsqlName(String name, boolean isquoted, int type)160 public HsqlName newHsqlName(String name, boolean isquoted, int type) { 161 return new HsqlName(this, name, isquoted, type); 162 } 163 newHsqlName(HsqlName schema, String name, boolean isquoted, int type)164 public HsqlName newHsqlName(HsqlName schema, String name, 165 boolean isquoted, int type) { 166 167 HsqlName hsqlName = new HsqlName(this, name, isquoted, type); 168 169 hsqlName.schema = schema; 170 171 return hsqlName; 172 } 173 newHsqlName(HsqlName schema, String name, boolean isquoted, int type, HsqlName parent)174 public HsqlName newHsqlName(HsqlName schema, String name, 175 boolean isquoted, int type, HsqlName parent) { 176 177 HsqlName hsqlName = new HsqlName(this, name, isquoted, type); 178 179 hsqlName.schema = schema; 180 hsqlName.parent = parent; 181 182 return hsqlName; 183 } 184 newColumnSchemaHsqlName(HsqlName table, SimpleName name)185 public HsqlName newColumnSchemaHsqlName(HsqlName table, SimpleName name) { 186 return newColumnHsqlName(table, name.name, name.isNameQuoted); 187 } 188 newColumnHsqlName(HsqlName table, String name, boolean isquoted)189 public HsqlName newColumnHsqlName(HsqlName table, String name, 190 boolean isquoted) { 191 192 HsqlName hsqlName = new HsqlName(this, name, isquoted, 193 SchemaObject.COLUMN); 194 195 hsqlName.schema = table.schema; 196 hsqlName.parent = table; 197 198 return hsqlName; 199 } 200 201 /** 202 * Same name string but different objects and serial number 203 */ getSubqueryTableName()204 public HsqlName getSubqueryTableName() { 205 return subqueryTableName; 206 } 207 208 /** 209 * Auto names are used for autogenerated indexes or anonymous constraints. 210 */ newAutoName(String prefix, HsqlName schema, HsqlName parent, int type)211 public HsqlName newAutoName(String prefix, HsqlName schema, 212 HsqlName parent, int type) { 213 214 HsqlName name = newAutoName(prefix, (String) null, schema, parent, 215 type); 216 217 return name; 218 } 219 newSpecificRoutineName(HsqlName name)220 public HsqlName newSpecificRoutineName(HsqlName name) { 221 222 StringBuffer sb = new StringBuffer(); 223 224 sb.append(name.name).append('_').append(++sysNumber); 225 226 HsqlName hsqlName = new HsqlName(this, sb.toString(), 227 SchemaObject.SPECIFIC_ROUTINE, 228 name.isNameQuoted); 229 230 hsqlName.parent = name; 231 hsqlName.schema = name.schema; 232 233 return hsqlName; 234 } 235 236 /** 237 * Column index i is 0 based, returns 1 based numbered column. 238 */ getAutoColumnName(int i)239 public static HsqlName getAutoColumnName(int i) { 240 241 if (i < autoColumnNames.length) { 242 return autoColumnNames[i]; 243 } 244 245 return new HsqlName(staticManager, "C_" + (i + 1), 246 SchemaObject.COLUMN, false); 247 } 248 getAutoNoNameColumnString(int i)249 public static String getAutoNoNameColumnString(int i) { 250 251 if (i < autoColumnNames.length) { 252 return autoNoNameColumnNames[i]; 253 } 254 255 return String.valueOf(i); 256 } 257 getAutoSavepointNameString(long i, int j)258 public static String getAutoSavepointNameString(long i, int j) { 259 260 StringBuffer sb = new StringBuffer("S"); 261 262 sb.append(i).append('_').append(j); 263 264 return sb.toString(); 265 } 266 267 /** 268 * Auto names are used for autogenerated indexes or anonymous constraints. 269 */ newAutoName(String prefix, String namepart, HsqlName schema, HsqlName parent, int type)270 public HsqlName newAutoName(String prefix, String namepart, 271 HsqlName schema, HsqlName parent, int type) { 272 273 StringBuffer sb = new StringBuffer(); 274 275 if (prefix != null) { 276 if (prefix.length() != 0) { 277 sb.append("SYS_"); 278 sb.append(prefix); 279 sb.append('_'); 280 281 if (namepart != null) { 282 sb.append(namepart); 283 sb.append('_'); 284 } 285 286 sb.append(++sysNumber); 287 } 288 } else { 289 sb.append(namepart); 290 } 291 292 HsqlName name = new HsqlName(this, sb.toString(), type, false); 293 294 name.schema = schema; 295 name.parent = parent; 296 297 return name; 298 } 299 getSimpleName(String name, boolean isNameQuoted)300 public static SimpleName getSimpleName(String name, boolean isNameQuoted) { 301 return new SimpleName(name, isNameQuoted); 302 } 303 304 public static class SimpleName { 305 306 public String name; 307 public boolean isNameQuoted; 308 SimpleName()309 private SimpleName() {} 310 SimpleName(String name, boolean isNameQuoted)311 private SimpleName(String name, boolean isNameQuoted) { 312 this.name = name; 313 this.isNameQuoted = isNameQuoted; 314 } 315 hashCode()316 public int hashCode() { 317 return name.hashCode(); 318 } 319 equals(Object other)320 public boolean equals(Object other) { 321 322 if (other instanceof SimpleName) { 323 return ((SimpleName) other).isNameQuoted == isNameQuoted 324 && ((SimpleName) other).name.equals(name); 325 } 326 327 return false; 328 } 329 getStatementName()330 public String getStatementName() { 331 332 return isNameQuoted 333 ? StringConverter.toQuotedString(name, '"', true) 334 : name; 335 } 336 getNameString()337 public String getNameString() { 338 return name; 339 } 340 } 341 342 public static final class HsqlName extends SimpleName { 343 344 static HsqlName[] emptyArray = new HsqlName[]{}; 345 346 // 347 HsqlNameManager manager; 348 public String statementName; 349 public String comment; 350 public HsqlName schema; 351 public HsqlName parent; 352 public Grantee owner; 353 public final int type; 354 355 // 356 private final int hashCode; 357 HsqlName(HsqlNameManager man, int type)358 private HsqlName(HsqlNameManager man, int type) { 359 360 manager = man; 361 this.type = type; 362 hashCode = manager.serialNumber.getAndIncrement(); 363 } 364 HsqlName(HsqlNameManager man, String name, boolean isquoted, int type)365 private HsqlName(HsqlNameManager man, String name, boolean isquoted, 366 int type) { 367 368 this(man, type); 369 370 rename(name, isquoted); 371 } 372 373 /** for auto names and system-defined names */ HsqlName(HsqlNameManager man, String name, int type, boolean isQuoted)374 private HsqlName(HsqlNameManager man, String name, int type, 375 boolean isQuoted) { 376 377 this(man, type); 378 379 this.name = name; 380 this.statementName = name; 381 this.isNameQuoted = isQuoted; 382 383 if (isNameQuoted) { 384 statementName = StringConverter.toQuotedString(name, '"', 385 true); 386 } 387 } 388 getStatementName()389 public String getStatementName() { 390 return statementName; 391 } 392 getSchemaQualifiedStatementName()393 public String getSchemaQualifiedStatementName() { 394 395 switch (type) { 396 397 case SchemaObject.PARAMETER : 398 case SchemaObject.VARIABLE : { 399 return statementName; 400 } 401 case SchemaObject.COLUMN : { 402 if (parent == null 403 || SqlInvariants.SYSTEM_SUBQUERY.equals( 404 parent.name)) { 405 return statementName; 406 } 407 408 StringBuffer sb = new StringBuffer(); 409 410 if (schema != null) { 411 sb.append(schema.getStatementName()); 412 sb.append('.'); 413 } 414 415 sb.append(parent.getStatementName()); 416 sb.append('.'); 417 sb.append(statementName); 418 419 return sb.toString(); 420 } 421 } 422 423 if (schema == null 424 || SqlInvariants.SYSTEM_SCHEMA.equals(schema.name)) { 425 return statementName; 426 } 427 428 StringBuffer sb = new StringBuffer(); 429 430 sb.append(schema.getStatementName()); 431 sb.append('.'); 432 sb.append(statementName); 433 434 return sb.toString(); 435 } 436 rename(HsqlName name)437 public void rename(HsqlName name) { 438 rename(name.name, name.isNameQuoted); 439 } 440 rename(String name, boolean isquoted)441 public void rename(String name, boolean isquoted) { 442 443 if (manager.sqlRegularNames && name.length() > 128) { 444 throw Error.error(ErrorCode.X_42501, name); 445 } 446 447 // get rid of the excess 448 this.name = name; 449 this.statementName = this.name; 450 this.isNameQuoted = isquoted; 451 452 if (isNameQuoted) { 453 statementName = StringConverter.toQuotedString(name, '"', 454 true); 455 } 456 457 if (name.startsWith("SYS_")) { 458 int length = name.lastIndexOf('_') + 1; 459 460 try { 461 int temp = Integer.parseInt(name.substring(length)); 462 463 if (temp > manager.sysNumber) { 464 manager.sysNumber = temp; 465 } 466 } catch (NumberFormatException e) {} 467 } 468 } 469 rename(String prefix, String name, boolean isquoted)470 void rename(String prefix, String name, boolean isquoted) { 471 472 StringBuffer sbname = new StringBuffer(prefix); 473 474 sbname.append('_'); 475 sbname.append(name); 476 rename(sbname.toString(), isquoted); 477 } 478 setSchemaIfNull(HsqlName schema)479 public void setSchemaIfNull(HsqlName schema) { 480 481 if (this.schema == null) { 482 this.schema = schema; 483 } 484 } 485 equals(Object other)486 public boolean equals(Object other) { 487 488 if (other instanceof HsqlName) { 489 return hashCode == ((HsqlName) other).hashCode; 490 } 491 492 return false; 493 } 494 495 /** 496 * hash code for this object is its unique serial number. 497 */ hashCode()498 public int hashCode() { 499 return hashCode; 500 } 501 502 /** 503 * "SYS_IDX_" is used for auto-indexes on referring FK columns or 504 * unique constraints. 505 * "SYS_PK_" is for the primary key constraints 506 * "SYS_CT_" is for unique and check constraints 507 * "SYS_REF_" is for FK constraints in referenced tables 508 * "SYS_FK_" is for FK constraints in referencing tables 509 * 510 */ 511 static final String[] sysPrefixes = new String[] { 512 "SYS_IDX_", "SYS_PK_", "SYS_REF_", "SYS_CT_", "SYS_FK_", 513 }; 514 sysPrefixLength(String name)515 static int sysPrefixLength(String name) { 516 517 for (int i = 0; i < sysPrefixes.length; i++) { 518 if (name.startsWith(sysPrefixes[i])) { 519 return sysPrefixes[i].length(); 520 } 521 } 522 523 return 0; 524 } 525 isReservedName(String name)526 static boolean isReservedName(String name) { 527 return sysPrefixLength(name) > 0; 528 } 529 isReservedName()530 boolean isReservedName() { 531 return isReservedName(name); 532 } 533 toString()534 public String toString() { 535 536 return getClass().getName() + super.hashCode() 537 + "[this.hashCode()=" + this.hashCode + ", name=" + name 538 + ", name.hashCode()=" + name.hashCode() 539 + ", isNameQuoted=" + isNameQuoted + "]"; 540 } 541 compareTo(Object o)542 public int compareTo(Object o) { 543 return hashCode - o.hashCode(); 544 } 545 546 /** 547 * Returns true if the identifier consists of all uppercase letters 548 * digits and underscore, beginning with a letter and is not in the 549 * keyword list. 550 */ isRegularIdentifier(String name)551 static boolean isRegularIdentifier(String name) { 552 553 for (int i = 0, length = name.length(); i < length; i++) { 554 int c = name.charAt(i); 555 556 if (c >= 'A' && c <= 'Z') { 557 continue; 558 } else if (c == '_' && i > 0) { 559 continue; 560 } else if (c >= '0' && c <= '9') { 561 continue; 562 } 563 564 return false; 565 } 566 567 return !Tokens.isKeyword(name); 568 } 569 } 570 } 571