1 10 11 package com.triactive.jdo.store; 12 13 import com.triactive.jdo.model.ClassMetaData; 14 import java.sql.Connection ; 15 import java.sql.DatabaseMetaData ; 16 import java.sql.ResultSet ; 17 import java.sql.SQLException ; 18 import java.sql.Statement ; 19 import java.util.ArrayList ; 20 import java.util.Collection ; 21 import java.util.HashMap ; 22 import java.util.HashSet ; 23 import java.util.Iterator ; 24 import java.util.List ; 25 import java.util.Map ; 26 import java.util.Set ; 27 import org.apache.log4j.Category; 28 29 30 abstract class BaseTable extends AbstractTable 31 { 32 private static final Category LOG = Category.getInstance(BaseTable.class); 33 34 public BaseTable(StoreManager storeMgr) 35 { 36 super(storeMgr); 37 } 38 39 40 public BaseTable(SQLIdentifier name, StoreManager storeMgr) 41 { 42 super(name, storeMgr); 43 } 44 45 46 public PrimaryKey getExpectedPrimaryKey() 47 { 48 PrimaryKey pk = null; 49 50 Iterator i = columns.iterator(); 51 52 while (i.hasNext()) 53 { 54 Column col = (Column)i.next(); 55 56 if (col.isPrimaryKeyPart()) 57 { 58 if (pk == null) 59 pk = new PrimaryKey(this); 60 61 pk.addColumn(col); 62 } 63 } 64 65 return pk; 66 } 67 68 69 public void create(Connection conn) throws SQLException 70 { 71 LOG.info("Creating table: " + this); 72 73 super.create(conn); 74 } 75 76 77 public boolean validate(int flags, Connection conn) throws SQLException 78 { 79 assertIsInitialized(); 80 81 boolean dbWasModified = false; 82 83 if ((flags & VALIDATE) != 0) 84 { 85 int tableType = storeMgr.getTableType(name, conn); 86 87 if (tableType == TABLE_TYPE_MISSING) 88 { 89 if ((flags & AUTO_CREATE) == 0) 90 throw new MissingTableException(this); 91 92 create(conn); 93 dbWasModified = true; 94 } 95 else 96 { 97 LOG.info("Validating table: " + this); 98 99 if (tableType != TABLE_TYPE_BASE_TABLE) 100 throw new NotABaseTableException(this); 101 102 HashMap unvalidated = new HashMap (columnsByName); 103 Iterator i = storeMgr.getColumnInfo(name, conn).iterator(); 104 105 while (i.hasNext()) 106 { 107 ColumnInfo ci = (ColumnInfo)i.next(); 108 SQLIdentifier colName = new SQLIdentifier(dba, ci.columnName); 109 110 Column col = (Column)unvalidated.get(colName); 111 112 if (col == null) 113 { 114 if (!hasColumnName(colName)) 115 throw new UnexpectedColumnException(this, colName); 116 121 } 122 else 123 { 124 col.validate(ci); 125 unvalidated.remove(colName); 126 } 127 } 128 129 if (unvalidated.size() > 0) 130 throw new MissingColumnException(this, unvalidated.values()); 131 132 PrimaryKey expectedPK = getExpectedPrimaryKey(); 133 Map actualPKs = getExistingPrimaryKeys(conn); 134 135 if (expectedPK == null) 136 { 137 if (!actualPKs.isEmpty()) 138 throw new WrongPrimaryKeyException(this, expectedPK, actualPKs.values()); 139 } 140 else 141 { 142 if (actualPKs.size() != 1 || !actualPKs.values().contains(expectedPK)) 143 throw new WrongPrimaryKeyException(this, expectedPK, actualPKs.values()); 144 } 145 } 146 } 147 148 state = TABLE_STATE_VALIDATED; 149 150 return dbWasModified; 151 } 152 153 154 public boolean validateConstraints(int flags, Connection conn) throws SQLException 155 { 156 assertIsInitialized(); 157 158 boolean dbWasModified = false; 159 160 if ((flags & VALIDATE) != 0) 161 { 162 boolean fksWereModified; 163 boolean idxsWereModified; 164 165 if (dba.createIndexesBeforeForeignKeys()) 166 { 167 idxsWereModified = validateIndices(flags, conn); 168 fksWereModified = validateForeignKeys(flags, conn); 169 } 170 else 171 { 172 fksWereModified = validateForeignKeys(flags, conn); 173 idxsWereModified = validateIndices(flags, conn); 174 } 175 176 dbWasModified = fksWereModified || idxsWereModified; 177 } 178 179 return dbWasModified; 180 } 181 182 183 private boolean validateForeignKeys(int flags, Connection conn) throws SQLException 184 { 185 boolean dbWasModified = false; 186 187 190 Map actualForeignKeysByName = getExistingForeignKeys(conn); 191 int numActualFKs = actualForeignKeysByName.size(); 192 193 Map stmtsByFKName = getSQLAddFKStatements(actualForeignKeysByName); 194 195 if (stmtsByFKName.isEmpty()) 196 { 197 if (numActualFKs > 0) 198 LOG.info("Validated " + numActualFKs + " foreign key(s) for table: " + this); 199 } 200 else 201 { 202 if ((flags & AUTO_CREATE) == 0) 203 throw new MissingForeignKeysException(this, stmtsByFKName.values()); 204 205 Statement stmt = conn.createStatement(); 206 207 try 208 { 209 Iterator i = stmtsByFKName.entrySet().iterator(); 210 211 while (i.hasNext()) 212 { 213 Map.Entry e = (Map.Entry )i.next(); 214 String fkName = (String )e.getKey(); 215 String stmtText = (String )e.getValue(); 216 217 LOG.info("Creating foreign key constraint: " + getSchemaName() + '.' + fkName); 218 219 long startTime = System.currentTimeMillis(); 220 221 stmt.execute(stmtText); 222 223 if (LOG.isDebugEnabled()) 224 LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + stmtText); 225 226 storeMgr.logSQLWarnings(stmt); 227 } 228 } 229 finally 230 { 231 stmt.close(); 232 } 233 234 dbWasModified = true; 235 } 236 237 return dbWasModified; 238 } 239 240 241 private boolean validateIndices(int flags, Connection conn) throws SQLException 242 { 243 boolean dbWasModified = false; 244 245 248 Map actualIndicesByName = getExistingIndices(conn); 249 250 256 int numActualIdxs = 0; 257 Iterator names = actualIndicesByName.keySet().iterator(); 258 259 while (names.hasNext()) 260 { 261 SQLIdentifier idxName = (SQLIdentifier)names.next(); 262 263 if (idxName.toString().startsWith(name.toString())) 264 ++numActualIdxs; 265 } 266 267 Map stmtsByIdxName = getSQLCreateIndexStatements(actualIndicesByName); 268 269 if (stmtsByIdxName.isEmpty()) 270 { 271 if (numActualIdxs > 0) 272 LOG.info("Validated " + numActualIdxs + " index(s) for table: " + this); 273 } 274 else 275 { 276 if ((flags & AUTO_CREATE) == 0) 277 throw new MissingIndicesException(this, stmtsByIdxName.values()); 278 279 Statement stmt = conn.createStatement(); 280 281 try 282 { 283 Iterator i = stmtsByIdxName.entrySet().iterator(); 284 285 while (i.hasNext()) 286 { 287 Map.Entry e = (Map.Entry )i.next(); 288 String idxName = (String )e.getKey(); 289 String stmtText = (String )e.getValue(); 290 291 LOG.info("Creating index: " + getSchemaName() + '.' + idxName); 292 293 long startTime = System.currentTimeMillis(); 294 295 stmt.execute(stmtText); 296 297 if (LOG.isDebugEnabled()) 298 LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + stmtText); 299 300 storeMgr.logSQLWarnings(stmt); 301 } 302 } 303 finally 304 { 305 stmt.close(); 306 } 307 308 dbWasModified = true; 309 } 310 311 return dbWasModified; 312 } 313 314 315 public void drop(Connection conn) throws SQLException 316 { 317 LOG.info("Dropping table: " + this); 318 319 super.drop(conn); 320 } 321 322 323 public void dropConstraints(Connection conn) throws SQLException 324 { 325 assertIsInitialized(); 326 327 if (!dba.supportsAlterTableDropConstraint()) 328 return; 329 330 334 DatabaseMetaData dmd = conn.getMetaData(); 335 336 HashSet fkNames = new HashSet (); 337 Iterator i = storeMgr.getForeignKeyInfo(name, conn).iterator(); 338 339 while (i.hasNext()) 340 { 341 ForeignKeyInfo fki = (ForeignKeyInfo)i.next(); 342 343 348 if (fki.fkName != null) 349 fkNames.add(fki.fkName); 350 } 351 352 int numFKs = fkNames.size(); 353 354 if (numFKs > 0) 355 { 356 LOG.info("Dropping " + numFKs + " foreign key(s) for table: " + this); 357 358 i = fkNames.iterator(); 359 Statement stmt = conn.createStatement(); 360 361 try 362 { 363 while (i.hasNext()) 364 { 365 String constraintName = (String )i.next(); 366 String stmtText = "ALTER TABLE " + name + " DROP CONSTRAINT " + constraintName; 367 368 long startTime = System.currentTimeMillis(); 369 370 stmt.execute(stmtText); 371 372 if (LOG.isDebugEnabled()) 373 LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + stmtText); 374 375 storeMgr.logSQLWarnings(stmt); 376 } 377 } 378 finally 379 { 380 stmt.close(); 381 } 382 } 383 } 384 385 386 protected List getExpectedForeignKeys() 387 { 388 assertIsInitialized(); 389 390 ArrayList foreignKeys = new ArrayList (); 391 Iterator i = columns.iterator(); 392 393 while (i.hasNext()) 394 { 395 Column col = (Column)i.next(); 396 397 ClassMetaData cmd = ClassMetaData.forClass(col.getType()); 398 399 if (cmd != null) 400 { 401 ClassBaseTable referencedTable = (ClassBaseTable)storeMgr.getTable(cmd); 402 403 if (referencedTable != null) 404 foreignKeys.add(new ForeignKey(col, referencedTable, true)); 405 } 406 } 407 408 return foreignKeys; 409 } 410 411 412 protected Set getExpectedIndices() 413 { 414 assertIsInitialized(); 415 416 HashSet indices = new HashSet (); 417 PrimaryKey pk = getExpectedPrimaryKey(); 418 Iterator i = getExpectedForeignKeys().iterator(); 419 420 427 while (i.hasNext()) 428 { 429 ForeignKey fk = (ForeignKey)i.next(); 430 431 if (!pk.startsWith(fk)) 432 indices.add(new Index(fk)); 433 } 434 435 return indices; 436 } 437 438 439 private Map getExistingPrimaryKeys(Connection conn) throws SQLException 440 { 441 DatabaseMetaData dmd = conn.getMetaData(); 442 443 HashMap primaryKeysByName = new HashMap (); 444 ResultSet rs = dmd.getPrimaryKeys(null, getSchemaName(), name.getSQLIdentifier()); 445 446 try 447 { 448 while (rs.next()) 449 { 450 SQLIdentifier pkName; 451 String s = rs.getString(6); 452 453 if (s == null) 454 pkName = new PrimaryKeyIdentifier(this); 455 else 456 pkName = new SQLIdentifier(dba, s); 457 458 PrimaryKey pk = (PrimaryKey)primaryKeysByName.get(pkName); 459 460 if (pk == null) 461 { 462 pk = new PrimaryKey(this); 463 primaryKeysByName.put(pkName, pk); 464 } 465 466 int keySeq = rs.getInt(5) - 1; 467 SQLIdentifier colName = new SQLIdentifier(dba, rs.getString(4)); 468 469 Column col = (Column)columnsByName.get(colName); 470 471 if (col == null) 472 throw new UnexpectedColumnException(this, colName); 473 474 pk.setColumn(keySeq, col); 475 } 476 } 477 finally 478 { 479 rs.close(); 480 } 481 482 return primaryKeysByName; 483 } 484 485 486 private Map getExistingForeignKeys(Connection conn) throws SQLException 487 { 488 DatabaseMetaData dmd = conn.getMetaData(); 489 490 HashMap foreignKeysByName = new HashMap (); 491 Iterator i = storeMgr.getForeignKeyInfo(name, conn).iterator(); 492 493 while (i.hasNext()) 494 { 495 ForeignKeyInfo fki = (ForeignKeyInfo)i.next(); 496 SQLIdentifier fkName; 497 498 if (fki.fkName == null) 499 fkName = new ForeignKeyIdentifier(this, foreignKeysByName.size()); 500 else 501 fkName = new SQLIdentifier(dba, fki.fkName); 502 503 boolean initiallyDeferred = fki.deferrability == DatabaseMetaData.importedKeyInitiallyDeferred; 504 505 ForeignKey fk = (ForeignKey)foreignKeysByName.get(fkName); 506 507 if (fk == null) 508 { 509 fk = new ForeignKey(initiallyDeferred); 510 foreignKeysByName.put(fkName, fk); 511 } 512 513 BaseTable refTable = (BaseTable)storeMgr.getTable(new SQLIdentifier(dba, fki.pkTableName)); 514 515 if (refTable != null) 516 { 517 SQLIdentifier colName = new SQLIdentifier(dba, fki.fkColumnName); 518 SQLIdentifier refColName = new SQLIdentifier(dba, fki.pkColumnName); 519 520 Column col = (Column)columnsByName.get(colName); 521 Column refCol = (Column)refTable.columnsByName.get(refColName); 522 523 if (col == null) 524 throw new UnexpectedColumnException(this, colName); 525 if (refCol == null) 526 throw new UnexpectedColumnException(this, refColName); 527 528 fk.addColumn(col, refCol); 529 } 530 } 531 532 return foreignKeysByName; 533 } 534 535 536 private Map getExistingIndices(Connection conn) throws SQLException 537 { 538 DatabaseMetaData dmd = conn.getMetaData(); 539 540 HashMap indicesByName = new HashMap (); 541 ResultSet rs = dmd.getIndexInfo(null, getSchemaName(), name.getSQLIdentifier(), false, true); 542 543 try 544 { 545 while (rs.next()) 546 { 547 short idxType = rs.getShort(7); 548 549 if (idxType == DatabaseMetaData.tableIndexStatistic) 550 continue; 551 552 SQLIdentifier idxName = new SQLIdentifier(dba, rs.getString(6)); 553 Index idx = (Index)indicesByName.get(idxName); 554 555 if (idx == null) 556 { 557 boolean isUnique = !rs.getBoolean(4); 558 idx = new Index(this, isUnique); 559 indicesByName.put(idxName, idx); 560 } 561 562 int colSeq = rs.getShort(8) - 1; 563 SQLIdentifier colName = new SQLIdentifier(dba, rs.getString(9)); 564 565 Column col = (Column)columnsByName.get(colName); 566 if (col == null) 567 throw new UnexpectedColumnException(this, colName); 568 569 idx.setColumn(colSeq, col); 570 } 571 } 572 finally 573 { 574 rs.close(); 575 } 576 577 return indicesByName; 578 } 579 580 581 protected List getSQLCreateStatements() 582 { 583 assertIsInitialized(); 584 585 ArrayList stmts = new ArrayList (); 586 587 stmts.add(dba.getCreateTableStatement(this, (Column[])columns.toArray(new Column[columns.size()]))); 588 589 PrimaryKey pk = getExpectedPrimaryKey(); 590 591 if (pk != null) 592 stmts.add(dba.getAddPrimaryKeyStatement(new PrimaryKeyIdentifier(this), pk)); 593 594 return stmts; 595 } 596 597 598 protected Map getSQLAddFKStatements(Map actualForeignKeysByName) 599 { 600 assertIsInitialized(); 601 602 HashMap stmtsByFKName = new HashMap (); 603 604 List expectedForeignKeys = getExpectedForeignKeys(); 605 606 Iterator i = expectedForeignKeys.iterator(); 607 int n = 1; 608 609 while (i.hasNext()) 610 { 611 ForeignKey fk = (ForeignKey)i.next(); 612 613 if (!actualForeignKeysByName.containsValue(fk)) 614 { 615 ForeignKeyIdentifier fkName; 616 617 do 618 { 619 fkName = new ForeignKeyIdentifier(this, n++); 620 } while (actualForeignKeysByName.containsKey(fkName)); 621 622 String stmtText = dba.getAddForeignKeyStatement(fkName, fk); 623 624 stmtsByFKName.put(fkName.getSQLIdentifier(), stmtText); 625 } 626 } 627 628 return stmtsByFKName; 629 } 630 631 632 private boolean isIndexReallyNeeded(Index requiredIdx, Collection actualIndices) 633 { 634 Iterator i = actualIndices.iterator(); 635 636 while (i.hasNext()) 637 { 638 Index actualIdx = (Index)i.next(); 639 640 if (actualIdx.startsWith(requiredIdx)) 641 return false; 642 } 643 644 return true; 645 } 646 647 648 protected Map getSQLCreateIndexStatements(Map actualIndicesByName) 649 { 650 assertIsInitialized(); 651 652 HashMap stmtsByIdxName = new HashMap (); 653 654 Set expectedIndices = getExpectedIndices(); 655 656 Iterator i = expectedIndices.iterator(); 657 int n = 1; 658 659 while (i.hasNext()) 660 { 661 Index idx = (Index)i.next(); 662 663 if (isIndexReallyNeeded(idx, actualIndicesByName.values())) 664 { 665 IndexIdentifier idxName; 666 667 do 668 { 669 idxName = new IndexIdentifier(this, idx.getUnique(), n++); 670 } while (actualIndicesByName.containsKey(idxName)); 671 672 String stmtText = dba.getCreateIndexStatement(idxName, idx); 673 674 stmtsByIdxName.put(idxName.getSQLIdentifier(), stmtText); 675 } 676 } 677 678 return stmtsByIdxName; 679 } 680 681 682 protected List getSQLDropStatements() 683 { 684 assertIsInitialized(); 685 686 ArrayList stmts = new ArrayList (); 687 stmts.add(dba.getDropTableStatement(this)); 688 689 return stmts; 690 } 691 } 692 | Popular Tags |