1 24 25 package org.continuent.sequoia.controller.sql.schema; 26 27 import java.io.Serializable ; 28 import java.sql.SQLException ; 29 import java.util.ArrayList ; 30 import java.util.ConcurrentModificationException ; 31 import java.util.HashMap ; 32 import java.util.Iterator ; 33 import java.util.List ; 34 import java.util.SortedSet ; 35 36 import org.continuent.sequoia.common.i18n.Translate; 37 import org.continuent.sequoia.common.log.Trace; 38 import org.continuent.sequoia.controller.backend.DatabaseBackend; 39 import org.continuent.sequoia.controller.locks.TransactionLogicalLock; 40 import org.continuent.sequoia.controller.requestmanager.RequestManager; 41 import org.continuent.sequoia.controller.requestmanager.TransactionMetaData; 42 import org.continuent.sequoia.controller.requests.AbstractRequest; 43 import org.continuent.sequoia.controller.requests.AbstractWriteRequest; 44 import org.continuent.sequoia.controller.requests.AlterRequest; 45 import org.continuent.sequoia.controller.requests.CreateRequest; 46 import org.continuent.sequoia.controller.requests.DropRequest; 47 import org.continuent.sequoia.controller.requests.ParsingGranularities; 48 import org.continuent.sequoia.controller.semantic.SemanticBehavior; 49 50 58 public class DatabaseSchema implements Serializable 59 { 60 private static final long serialVersionUID = 1105453274994163661L; 61 62 66 private String vdbNameWithDot; 67 68 private HashMap tables; 69 70 private HashMap procedures; 71 72 73 private transient TransactionLogicalLock lock = new TransactionLogicalLock(); 74 75 80 public DatabaseSchema(String vdbName) 81 { 82 this.vdbNameWithDot = vdbName + "."; 83 tables = new HashMap (); 84 procedures = new HashMap (); 85 } 86 87 94 public DatabaseSchema(String vdbName, int nbOfTables) 95 { 96 this.vdbNameWithDot = vdbName + "."; 97 tables = new HashMap (nbOfTables); 98 procedures = new HashMap (); 99 } 100 101 107 public DatabaseSchema(DatabaseSchema schema) 108 { 109 if (schema == null) 110 throw new IllegalArgumentException ( 111 "Illegal null database schema in DatabaseSchema(DatabaseSchema) constructor"); 112 113 vdbNameWithDot = schema.getVirtualDatabaseName(); 114 tables = new HashMap (schema.getTables()); 115 procedures = new HashMap (schema.getProcedures()); 116 } 117 118 123 public synchronized void addTable(DatabaseTable table) 124 { 125 if (table == null) 126 throw new IllegalArgumentException ( 127 "Illegal null database table in addTable(DatabaseTable) method"); 128 tables.put(table.getName(), table); 129 if (table.getSchema() != null) 130 tables.put(table.getSchema() + "." + table.getName(), table); 131 } 132 133 139 public synchronized void addProcedure(DatabaseProcedure procedure) 140 { 141 if (procedure == null) 142 throw new IllegalArgumentException ( 143 "Illegal null database table in addTable(DatabaseTable) method"); 144 String key = procedure.getKey(); 145 int semicolon = key.indexOf(';'); 146 if (semicolon > 0) 147 { String keyWithoutVersionNumber = key.substring(0, semicolon) 150 + key.substring(procedure.getName().length()); 151 procedures.put(keyWithoutVersionNumber, procedure); 152 } 153 procedures.put(key, procedure); 154 } 155 156 163 public boolean allTablesAreUnlockedOrLockedByTransaction( 164 AbstractRequest request) 165 { 166 boolean retry; 170 do 171 { 172 retry = false; 173 try 174 { 175 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 176 { 177 TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock(); 178 if (l.isLocked()) 179 { 180 if (request.getTransactionId() != l.getLocker()) 182 return false; 183 } 184 } 185 } 186 catch (ConcurrentModificationException e) 187 { 188 retry = true; 189 } 190 } 191 while (retry); 192 return true; 193 } 194 195 204 public List lockAllTables(AbstractRequest request) 205 { 206 boolean retry; 210 List acquiredLocks = new ArrayList (); 211 do 212 { 213 retry = false; 214 try 215 { 216 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 217 { 218 TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock(); 219 if (!l.isLocked()) 220 { 221 l.acquire(request); 222 acquiredLocks.add(l); 223 } 224 } 225 } 226 catch (ConcurrentModificationException e) 227 { 228 retry = true; 229 } 230 } 231 while (retry); 232 return acquiredLocks; 233 } 234 235 240 public TransactionLogicalLock getLock() 241 { 242 return lock; 243 } 244 245 251 public void setLocks(DatabaseSchema oldSchema) 252 { 253 lock = oldSchema.lock; 254 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 255 { 256 DatabaseTable table = (DatabaseTable) iter.next(); 257 DatabaseTable oldTable = oldSchema.getTable(table.getName(), true); 258 if (oldTable != null) 259 table.setLock(oldTable); 260 } 261 } 262 263 270 public DatabaseProcedure getProcedure(String procedureKey) 271 { 272 DatabaseProcedure proc = null; 273 if (procedureKey == null) 274 return null; 275 276 synchronized (procedures) 277 { 278 proc = (DatabaseProcedure) procedures.get(procedureKey); 279 if (proc == null) 282 proc = (DatabaseProcedure) procedures.get(procedureKey.toLowerCase()); 283 return proc; 284 } 285 } 286 287 294 public DatabaseProcedure getProcedure(DatabaseProcedure procedure) 295 { 296 if (procedure == null) 297 return null; 298 299 boolean retry; 303 do 304 { 305 retry = false; 306 try 307 { 308 for (Iterator iter = procedures.values().iterator(); iter.hasNext();) 309 { 310 DatabaseProcedure p = (DatabaseProcedure) iter.next(); 311 if (procedure.equals(p)) 312 return p; 313 } 314 } 315 catch (ConcurrentModificationException e) 316 { 317 retry = true; 318 } 319 } 320 while (retry); 321 return null; 322 } 323 324 331 public HashMap getProcedures() 332 { 333 return procedures; 334 } 335 336 343 public DatabaseTable getTable(String tableName) 344 { 345 if ((tableName == null) || (tableName.length() == 0)) 346 return null; 347 348 synchronized (tables) 349 { 350 DatabaseTable t = (DatabaseTable) tables.get(tableName); 351 if (t == null) 352 { 353 if (tableName.startsWith(vdbNameWithDot)) 356 { t = (DatabaseTable) tables.get(tableName.substring(vdbNameWithDot 358 .length())); 359 } 360 else 364 { 365 char firstChar = tableName.charAt(0); 366 if (firstChar == '\"' || firstChar == '`' || firstChar == '\'') 367 { 368 String trimedTableName = tableName.substring(1, 369 tableName.length() - 1); 370 t = (DatabaseTable) tables.get(trimedTableName); 371 } 372 } 373 } 374 return t; 375 } 376 } 377 378 private DatabaseTable getTable(DatabaseTable other) 379 { 380 if (other.getSchema() != null) 381 return getTable(other.getSchema() + "." + other.getName()); 382 else 383 return getTable(other.getName()); 384 } 385 386 395 public DatabaseTable getTable(String tableName, boolean isCaseSensitive) 396 { 397 if ((tableName == null) || (tableName.length() == 0)) 398 return null; 399 400 DatabaseTable t = getTable(tableName); 401 if (isCaseSensitive || (t != null)) 402 return t; 403 404 407 if (tableName.startsWith(vdbNameWithDot)) 410 tableName = tableName.substring(vdbNameWithDot.length()); 411 412 boolean retry; 416 do 417 { 418 retry = false; 419 try 420 { 421 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 422 { 423 t = (DatabaseTable) iter.next(); 424 if (tableName.equalsIgnoreCase(t.getName())) 425 return t; 426 } 427 } 428 catch (ConcurrentModificationException e) 429 { 430 retry = true; 431 } 432 } 433 while (retry); 434 return null; 435 } 436 437 443 public HashMap getTables() 444 { 445 return tables; 446 } 447 448 453 public final String getVirtualDatabaseName() 454 { 455 return vdbNameWithDot; 456 } 457 458 466 public boolean hasProcedure(String procedureName, int nbOfParameters) 467 { 468 return procedures.containsKey(DatabaseProcedure.buildKey(procedureName, 469 nbOfParameters)); 470 } 471 472 480 public boolean hasATableLockedByTransaction(long transactionId) 481 { 482 boolean retry; 486 do 487 { 488 retry = false; 489 try 490 { 491 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 492 { 493 TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock(); 494 if (l.isLocked() 495 && ((l.getLocker() == transactionId) || (l 496 .isWaiting(transactionId)))) 497 return true; 498 } 499 } 500 catch (ConcurrentModificationException e) 501 { 502 retry = true; 503 } 504 } 505 while (retry); 506 return false; 507 } 508 509 516 public boolean hasTable(String tableName) 517 { 518 do 521 { 522 try 523 { 524 return tables.containsKey(tableName); 525 } 526 catch (ConcurrentModificationException e) 527 { 528 } 529 } 530 while (true); 531 } 532 533 541 public boolean isCompatibleSubset(DatabaseSchema other) 542 { 543 if (other == null) 544 return false; 545 546 DatabaseTable table, otherTable; 547 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 548 { table = (DatabaseTable) iter.next(); 550 otherTable = other.getTable(table); 551 if (otherTable == null) 552 return false; else if (!table.equalsIgnoreType(otherTable)) 554 return false; } 556 DatabaseProcedure procedure, otherProcedure; 557 for (Iterator iter = procedures.values().iterator(); iter.hasNext();) 558 { procedure = (DatabaseProcedure) iter.next(); 560 otherProcedure = other.getProcedure(procedure.getName()); 561 if (otherProcedure == null) 562 return false; else if (!procedure.equals(otherProcedure)) 564 return false; } 566 return true; } 568 569 577 public boolean isCompatibleWith(DatabaseSchema other) 578 { 579 DatabaseTable table, otherTable; 580 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 581 { table = (DatabaseTable) iter.next(); 583 otherTable = other.getTable(table); 584 if (otherTable == null) 585 continue; else if (!table.equalsIgnoreType(otherTable)) 587 return false; } 589 DatabaseProcedure procedure, otherProcedure; 590 for (Iterator iter = procedures.values().iterator(); iter.hasNext();) 591 { procedure = (DatabaseProcedure) iter.next(); 593 otherProcedure = other.getProcedure(procedure.getName()); 594 if (otherProcedure == null) 595 continue; else if (!procedure.equals(otherProcedure)) 597 return false; } 599 return true; } 601 602 610 public void mergeSchema(DatabaseSchema databaseSchema) throws SQLException 611 { 612 if (databaseSchema == null) 613 throw new IllegalArgumentException ( 614 "Illegal null database schema in mergeSchema(DatabaseSchema) method"); 615 616 HashMap otherTables = databaseSchema.getTables(); 617 if ((otherTables == null) || (otherTables.size() == 0)) 618 return; 619 620 DatabaseTable table, originalTable; 621 for (Iterator iter = otherTables.values().iterator(); iter.hasNext();) 622 { table = (DatabaseTable) iter.next(); 624 originalTable = getTable(table); 625 if (originalTable == null) 626 addTable(table); 627 else 628 originalTable.merge(table); 629 } 630 631 HashMap otherProcedures = databaseSchema.getProcedures(); 632 if ((otherProcedures == null) || (otherProcedures.size() == 0)) 633 return; 634 635 DatabaseProcedure procedure, originalProcedure; 636 for (Iterator iter = otherProcedures.values().iterator(); iter.hasNext();) 637 { procedure = (DatabaseProcedure) iter.next(); 639 originalProcedure = getProcedure(procedure); 640 if (originalProcedure == null) 641 addProcedure(procedure); 642 } 643 } 644 645 650 public void releaseLocksOnAllTables(long transactionId) 651 { 652 lock.release(transactionId); 654 655 boolean retry; 659 do 660 { 661 retry = false; 662 try 663 { 664 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 665 { 666 TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock(); 667 l.release(transactionId); 668 } 669 } 670 catch (ConcurrentModificationException e) 671 { 672 retry = true; 673 } 674 } 675 while (retry); 676 } 677 678 685 public synchronized boolean removeProcedure(DatabaseProcedure procedure) 686 { 687 if (procedure == null) 688 throw new IllegalArgumentException ( 689 "Illegal null database procedure in removeProcedure(DatabaseProcedure) method"); 690 return procedures.remove(procedure.getKey()) != null; 691 } 692 693 699 public synchronized boolean removeTable(DatabaseTable table) 700 { 701 if (table == null) 702 throw new IllegalArgumentException ( 703 "Illegal null database table in removeTable(DatabaseTable) method"); 704 if (table.getSchema() != null) 705 return ((tables.remove(table.getName()) != null) && (tables.remove(table 706 .getSchema() 707 + "." + table.getName()) != null)); 708 else 709 return (tables.remove(table.getName()) != null); 710 } 711 712 720 public void updateSchema(DatabaseSchema databaseSchema) 721 { 722 if (databaseSchema == null) 723 throw new IllegalArgumentException ( 724 "Illegal null database schema in mergeSchema(DatabaseSchema) method"); 725 726 HashMap otherTables = databaseSchema.getTables(); 727 728 for (Iterator iter = tables.values().iterator(); iter.hasNext();) 730 { 731 DatabaseTable t = (DatabaseTable) iter.next(); 732 if (!databaseSchema.hasTable(t.getSchema() + "." + t.getName())) 733 iter.remove(); 734 } 735 736 DatabaseTable table, originalTable; 738 for (Iterator iter = otherTables.values().iterator(); iter.hasNext();) 739 { 740 table = (DatabaseTable) iter.next(); 741 originalTable = getTable(table); 742 if (originalTable == null) 743 addTable(table); 744 else 745 originalTable.updateColumns(table); 746 } 747 748 this.procedures = databaseSchema.getProcedures(); 750 } 751 752 759 public boolean equals(Object other) 760 { 761 boolean equal = true; 762 if ((other == null) || !(other instanceof DatabaseSchema)) 763 return false; 764 if (tables == null) 765 equal &= ((DatabaseSchema) other).getTables() == null; 766 else 767 equal &= tables.equals(((DatabaseSchema) other).getTables()); 768 if (procedures == null) 769 equal &= ((DatabaseSchema) other).getProcedures() == null; 770 else 771 equal &= procedures.equals(((DatabaseSchema) other).getProcedures()); 772 return equal; 773 } 774 775 790 public void updateDatabaseSchema(AbstractWriteRequest request, Trace logger, 791 RequestManager rm, TransactionMetaData tm, DatabaseBackend dbBackend) 792 { 793 if (request.altersSomething()) 794 { 795 SemanticBehavior semantic = request.getSemantic(); 796 if (semantic == null) 797 { 798 logger.error("No semantic information found for request '" 800 + request.getSqlShortForm(rm.getVirtualDatabase() 801 .getSqlShortFormLength()) + "'. Schema cannot be updated."); 802 803 return; 804 } 805 806 if (semantic.altersDatabaseSchema()) 809 { 810 if (request.isCreate()) 811 { CreateRequest createRequest = (CreateRequest) request; 813 if (createRequest.getDatabaseTable() != null) 814 { 815 addTable(new DatabaseTable(((CreateRequest) request) 816 .getDatabaseTable())); 817 if (logger.isDebugEnabled()) 818 logger.debug(Translate.get("requestmanager.schema.add.table", 819 request.getTableName())); 820 setSchemaIsDirty(true, request, rm, dbBackend); 823 } 824 else 825 setSchemaIsDirty(true, request, rm, dbBackend); 828 } 829 else if (request.isDrop()) 830 { SortedSet tablesToRemove = ((DropRequest) request).getTablesToDrop(); 832 833 if ((tablesToRemove != null)) 834 { 835 for (Iterator iter = tablesToRemove.iterator(); iter.hasNext();) 836 { 837 String tableToRemove = (String ) iter.next(); 838 DatabaseTable t = getTable(tableToRemove); 839 if (t != null) 840 { 841 removeTable(t); 843 if (logger.isDebugEnabled()) 844 logger.debug("Removed table '" + t.getName() 845 + "' from schema"); 846 847 Iterator keys = getTables().keySet().iterator(); 849 while (keys.hasNext()) 850 { 851 String tableName = (String ) keys.next(); 852 DatabaseTable table = getTable(tableName); 853 if (table.getDependingTables() != null) 854 { 855 table.getDependingTables().remove(t.getName()); 856 if (logger.isDebugEnabled()) 857 logger.debug("Removed table '" + t.getName() 858 + "' from dependending tables list of table '" 859 + table.getName() + "' in schema"); 860 } 861 } 862 } 863 else 864 { 865 setSchemaIsDirty(true, request, rm, dbBackend); 867 return; 868 } 869 } 870 } 871 } 872 else if (request.isAlter() 873 && (rm.getRequiredParsingGranularity() > ParsingGranularities.TABLE)) 874 { AlterRequest req = (AlterRequest) request; 876 DatabaseTable alteredTable = getTable(req.getTableName()); 877 if ((alteredTable != null) && (req.getColumn() != null)) 878 { 879 if (req.isDrop()) 880 alteredTable.removeColumn(req.getColumn().getName()); 881 else if (req.isAdd()) 882 alteredTable.addColumn(req.getColumn()); 883 else 884 setSchemaIsDirty(true, request, rm, dbBackend); 886 } 887 else 888 setSchemaIsDirty(true, request, rm, dbBackend); 890 } 891 else 892 setSchemaIsDirty(true, request, rm, dbBackend); 894 } 895 } 896 } 897 898 908 private void setSchemaIsDirty(boolean schemaIsDirty, AbstractRequest request, 909 RequestManager rm, DatabaseBackend dbBackend) 910 { 911 if (dbBackend != null) 912 dbBackend.setSchemaIsDirty(schemaIsDirty, request); 913 else 914 rm.setSchemaIsDirty(schemaIsDirty); 915 } 916 917 } | Popular Tags |