1 24 25 package org.continuent.sequoia.controller.recoverylog; 26 27 import java.sql.Connection ; 28 import java.sql.DatabaseMetaData ; 29 import java.sql.PreparedStatement ; 30 import java.sql.ResultSet ; 31 import java.sql.SQLException ; 32 import java.sql.Statement ; 33 import java.sql.Timestamp ; 34 import java.util.ArrayList ; 35 import java.util.Iterator ; 36 import java.util.List ; 37 import java.util.Map ; 38 import java.util.TreeMap ; 39 40 import org.continuent.sequoia.common.exceptions.VirtualDatabaseException; 41 import org.continuent.sequoia.common.i18n.Translate; 42 import org.continuent.sequoia.common.jmx.management.BackendState; 43 import org.continuent.sequoia.common.jmx.management.DumpInfo; 44 import org.continuent.sequoia.common.log.Trace; 45 import org.continuent.sequoia.common.xml.DatabasesXmlTags; 46 import org.continuent.sequoia.common.xml.XmlComponent; 47 import org.continuent.sequoia.controller.connection.DriverManager; 48 import org.continuent.sequoia.controller.core.ControllerConstants; 49 import org.continuent.sequoia.controller.loadbalancer.tasks.BeginTask; 50 import org.continuent.sequoia.controller.loadbalancer.tasks.CallableStatementExecuteTask; 51 import org.continuent.sequoia.controller.loadbalancer.tasks.ClosePersistentConnectionTask; 52 import org.continuent.sequoia.controller.loadbalancer.tasks.CommitTask; 53 import org.continuent.sequoia.controller.loadbalancer.tasks.OpenPersistentConnectionTask; 54 import org.continuent.sequoia.controller.loadbalancer.tasks.ReleaseSavepointTask; 55 import org.continuent.sequoia.controller.loadbalancer.tasks.RollbackTask; 56 import org.continuent.sequoia.controller.loadbalancer.tasks.RollbackToSavepointTask; 57 import org.continuent.sequoia.controller.loadbalancer.tasks.SavepointTask; 58 import org.continuent.sequoia.controller.loadbalancer.tasks.StatementExecuteQueryTask; 59 import org.continuent.sequoia.controller.loadbalancer.tasks.StatementExecuteTask; 60 import org.continuent.sequoia.controller.loadbalancer.tasks.StatementExecuteUpdateTask; 61 import org.continuent.sequoia.controller.recoverylog.events.DeleteLogEntriesAndCheckpointBetweenEvent; 62 import org.continuent.sequoia.controller.recoverylog.events.FindCommitEvent; 63 import org.continuent.sequoia.controller.recoverylog.events.FindRollbackEvent; 64 import org.continuent.sequoia.controller.recoverylog.events.GetCheckpointLogEntryEvent; 65 import org.continuent.sequoia.controller.recoverylog.events.GetCheckpointLogIdEvent; 66 import org.continuent.sequoia.controller.recoverylog.events.GetNumberOfLogEntriesEvent; 67 import org.continuent.sequoia.controller.recoverylog.events.GetUpdateCountEvent; 68 import org.continuent.sequoia.controller.recoverylog.events.LogCommitEvent; 69 import org.continuent.sequoia.controller.recoverylog.events.LogEntry; 70 import org.continuent.sequoia.controller.recoverylog.events.LogEvent; 71 import org.continuent.sequoia.controller.recoverylog.events.LogRequestCompletionEvent; 72 import org.continuent.sequoia.controller.recoverylog.events.LogRequestEvent; 73 import org.continuent.sequoia.controller.recoverylog.events.LogRollbackEvent; 74 import org.continuent.sequoia.controller.recoverylog.events.RemoveCheckpointEvent; 75 import org.continuent.sequoia.controller.recoverylog.events.ResetLogEvent; 76 import org.continuent.sequoia.controller.recoverylog.events.ShiftLogEntriesEvent; 77 import org.continuent.sequoia.controller.recoverylog.events.ShutdownLogEvent; 78 import org.continuent.sequoia.controller.recoverylog.events.StoreCheckpointWithLogIdEvent; 79 import org.continuent.sequoia.controller.requestmanager.TransactionMetaData; 80 import org.continuent.sequoia.controller.requestmanager.distributed.DistributedRequestManager; 81 import org.continuent.sequoia.controller.requests.AbstractRequest; 82 import org.continuent.sequoia.controller.requests.AbstractWriteRequest; 83 import org.continuent.sequoia.controller.requests.RequestFactory; 84 import org.continuent.sequoia.controller.requests.SelectRequest; 85 import org.continuent.sequoia.controller.requests.StoredProcedure; 86 import org.continuent.sequoia.controller.requests.UnknownWriteRequest; 87 import org.continuent.sequoia.controller.scheduler.AbstractScheduler; 88 89 99 public class RecoveryLog implements XmlComponent 100 { 101 static final String BEGIN = "begin"; 102 private static final String COMMIT = "commit"; 103 private static final String ROLLBACK = "rollback"; 104 private static final String CLOSE_PERSISTENT_CONNECTION = "close"; 105 private static final String OPEN_PERSISTENT_CONNECTION = "open"; 106 110 public static final String UNKNOWN_USER = "_u u_"; 111 112 113 public static final int COLUMN_INDEX_LOG_ID = 1; 114 115 public static final int COLUMN_INDEX_VLOGIN = 2; 116 117 public static final int COLUMN_INDEX_SQL = 3; 118 119 public static final int COLUMN_INDEX_SQL_PARAMS = 4; 120 121 public static final int COLUMN_INDEX_AUTO_CONN_TRAN = 5; 122 123 public static final int COLUMN_INDEX_TRANSACTION_ID = 6; 124 125 public static final int COLUMN_INDEX_REQUEST_ID = 7; 126 127 public static final int COLUMN_INDEX_EXEC_STATUS = 8; 128 129 public static final int COLUMN_INDEX_EXEC_TIME = 9; 130 131 public static final int COLUMN_INDEX_UPDATE_COUNT = 10; 132 133 static public final int COLUMN_INDEX_COMPLETION_LOG_ID = 11; 134 135 static Trace logger = Trace 136 .getLogger("org.continuent.sequoia.controller.recoverylog"); 137 static Trace endUserLogger = Trace 138 .getLogger("org.continuent.sequoia.enduser"); 139 140 private RequestFactory requestFactory = ControllerConstants.CONTROLLER_FACTORY 141 .getRequestFactory(); 142 143 144 private long recoveringNb = 0; 145 146 147 protected int recoveryBatchSize; 148 149 150 private String driverClassName; 151 152 153 private String driverPath; 154 155 156 private String url; 157 158 159 private String login; 160 161 162 private String password; 163 164 165 private Connection internalConnectionManagedByGetDatabaseConnection = null; 166 167 173 private String logTableCreateTable; 174 private String logTableName; 175 private String logTableCreateStatement; 176 private String logTableLogIdType; 177 private String logTableVloginType; 178 private String logTableSqlColumnName; 179 private String logTableSqlType; 180 private String logTableAutoConnTranColumnType; 181 private String logTableTransactionIdType; 182 private String logTableRequestIdType; 183 private String logTableExecTimeType; 184 private String logTableUpdateCountType; 185 private String logTableExtraStatement; 186 187 193 private String checkpointTableCreateTable; 194 private String checkpointTableName; 195 private String checkpointTableCreateStatement; 196 private String checkpointTableNameType; 197 private String checkpointTableLogIdType; 198 private String checkpointTableExtraStatement; 199 200 206 private String backendTableCreateStatement; 207 private String backendTableName; 208 private String backendTableCreateTable; 209 private String backendTableDatabaseName; 210 private String backendTableExtraStatement; 211 private String backendTableCheckpointName; 212 private String backendTableBackendState; 213 private String backendTableBackendName; 214 215 221 private String dumpTableCreateStatement; 222 private String dumpTableCreateTable; 223 private String dumpTableName; 224 private String dumpTableDumpNameColumnType; 225 private String dumpTableDumpDateColumnType; 226 private String dumpTableDumpPathColumnType; 227 private String dumpTableDumpFormatColumnType; 228 private String dumpTableCheckpointNameColumnType; 229 private String dumpTableBackendNameColumnType; 230 private String dumpTableTablesColumnName; 231 private String dumpTableTablesColumnType; 232 private String dumpTableExtraStatementDefinition; 233 234 235 private long logTableId = 0; 236 237 238 private int timeout; 239 240 private LoggerThread loggerThread; 241 242 private boolean isShuttingDown = false; 243 private String logTableAddCompletionLogIdStatement; 244 245 258 public RecoveryLog(String driverPath, String driverClassName, String url, 259 String login, String password, int requestTimeout, int recoveryBatchSize) 260 { 261 this.driverPath = driverPath; 262 this.driverClassName = driverClassName; 263 this.url = url; 264 this.login = login; 265 this.password = password; 266 this.timeout = requestTimeout; 267 if (recoveryBatchSize < 1) 268 { 269 logger 270 .warn("RecoveryBatchSize was set to a value lesser than 1, resetting value to 1."); 271 recoveryBatchSize = 1; 272 } 273 this.recoveryBatchSize = recoveryBatchSize; 274 275 if (url.startsWith("jdbc:hsqldb:file:")) 276 { 277 if (url.indexOf("shutdown=true") == -1) 278 { 279 this.url = url + ";shutdown=true"; 280 String msg = "Hsqldb RecoveryLog url has no shutdown=true option.\n" 281 + "This prevents the recovery log from shutting down correctly.\n" 282 + "Please update your vdb.xml file.\n" + "Setting url to '" 283 + this.url + "'."; 284 logger.warn(msg); 285 endUserLogger.warn(msg); 286 } 287 } 288 289 try 291 { 292 getDatabaseConnection(); 293 } 294 catch (SQLException e) 295 { 296 throw new RuntimeException ("Unable to connect to the database: " + e); 297 } 298 299 } 302 303 307 314 protected Connection getDatabaseConnection() throws SQLException 315 { 316 try 317 { 318 if (internalConnectionManagedByGetDatabaseConnection == null) 319 { 320 if (logger.isDebugEnabled()) 321 logger.debug(Translate.get("recovery.jdbc.connect", new String []{url, 322 login})); 323 internalConnectionManagedByGetDatabaseConnection = DriverManager 324 .getConnection(url, login, password, driverPath, driverClassName); 325 } 326 return internalConnectionManagedByGetDatabaseConnection; 327 } 328 catch (RuntimeException e) 329 { 330 String msg = Translate.get("recovery.jdbc.connect.failed", e); 331 if (logger.isDebugEnabled()) 332 logger.debug(msg, e); 333 throw new SQLException (msg); 334 } 335 catch (SQLException e) 336 { 337 invalidateInternalConnection(); 338 String msg = Translate.get("recovery.jdbc.connect.failed", e); 339 if (logger.isDebugEnabled()) 340 logger.debug(msg, e); 341 throw new SQLException (msg); 342 } 343 } 344 345 348 synchronized long incrementLogTableId() 349 { 350 logTableId++; 351 return logTableId; 352 } 353 354 358 private void intializeDatabase() throws SQLException 359 { 360 boolean createLogTable = true; 361 boolean createCheckpointTable = true; 362 boolean createBackendTable = true; 363 boolean createDumpTable = true; 364 boolean addCompletionLogId = false; 365 Connection connection; 366 try 368 { 369 connection = getDatabaseConnection(); 370 371 if (connection == null) 372 throw new SQLException (Translate.get("recovery.jdbc.connect.failed", 373 "null connection returned by DriverManager")); 374 375 connection.setAutoCommit(false); 376 DatabaseMetaData metaData = connection.getMetaData(); 378 379 String [] types = {"TABLE", "VIEW"}; 386 ResultSet rs = metaData.getTables(null, null, "%", types); 387 388 String tableName; 390 while (rs.next()) 391 { 392 tableName = rs.getString(3); 394 if (logger.isDebugEnabled()) 395 logger.debug(Translate.get("recovery.jdbc.table.found", tableName)); 396 if (tableName.equalsIgnoreCase(logTableName)) 397 { 398 if (tableName.compareTo(logTableName) != 0) 399 logger.warn(Translate.get("recovery.jdbc.logtable.case.mismatch", 400 new String []{logTableName, tableName})); 401 createLogTable = false; 402 PreparedStatement p = null; 404 try 405 { 406 ResultSet result = null; 407 p = connection 408 .prepareStatement("SELECT MAX(log_id) AS max_log_id FROM " 409 + logTableName); 410 result = p.executeQuery(); 411 if (result.next()) 412 logTableId = result.getLong(1); 413 else 414 logTableId = 0; 415 p.close(); 416 String name = "completion_log_id"; 417 if (!metaData.supportsMixedCaseIdentifiers() 418 && metaData.storesUpperCaseIdentifiers()) 419 name = name.toUpperCase(); 420 ResultSet columns = metaData 421 .getColumns(null, null, tableName, name); 422 if (!columns.next()) 423 addCompletionLogId = true; 424 } 425 catch (SQLException e) 426 { 427 try 428 { 429 if (p != null) 430 p.close(); 431 } 432 catch (Exception ignore) 433 { 434 } 435 throw new RuntimeException (Translate.get( 436 "recovery.jdbc.logtable.getvalue.failed", e)); 437 } 438 439 } 440 if (tableName.equalsIgnoreCase(checkpointTableName)) 441 { 442 if (tableName.compareTo(checkpointTableName) != 0) 443 logger.warn(Translate.get( 444 "recovery.jdbc.checkpointtable.case.mismatch", new String []{ 445 checkpointTableName, tableName})); 446 createCheckpointTable = false; 447 if (logger.isDebugEnabled()) 448 { 449 StringBuffer sb = new StringBuffer (); 451 sb.append("Checkpoint list..."); 452 453 Map checkpoints = this.getCheckpoints(); 454 Iterator checkPointIterator = checkpoints.keySet().iterator(); 455 while (checkPointIterator.hasNext()) 456 { 457 String name = (String ) checkPointIterator.next(); 458 String logId = (String ) checkpoints.get(name); 459 sb.append("\n"); 460 sb.append("name=[").append(name).append("] log_id=[").append( 461 logId).append("]"); 462 } 463 logger.debug(sb.toString()); 464 } 465 } 466 else if (tableName.equalsIgnoreCase(backendTableName)) 467 { 468 if (tableName.compareTo(backendTableName) != 0) 469 logger.warn(Translate.get( 470 "recovery.jdbc.backendtable.case.mismatch", new String []{ 471 backendTableName, tableName})); 472 createBackendTable = false; 473 } 474 else if (tableName.equalsIgnoreCase(dumpTableName)) 475 { 476 if (tableName.compareTo(dumpTableName) != 0) 477 logger.warn(Translate.get("recovery.jdbc.dumptable.case.mismatch", 478 new String []{backendTableName, tableName})); 479 createDumpTable = false; 480 } 481 } 482 try 483 { 484 connection.commit(); 485 connection.setAutoCommit(true); 486 } 487 catch (Exception ignore) 488 { 489 } 491 } 492 catch (SQLException e) 493 { 494 logger.error(Translate.get("recovery.jdbc.table.no.description"), e); 495 throw e; 496 } 497 498 Statement stmt = null; 500 if (createLogTable) 501 { 502 if (logger.isInfoEnabled()) 503 logger.info(Translate 504 .get("recovery.jdbc.logtable.create", logTableName)); 505 try 506 { 507 stmt = connection.createStatement(); 508 stmt.executeUpdate(logTableCreateStatement); 509 stmt.close(); 510 } 511 catch (SQLException e) 512 { 513 throw new SQLException (Translate.get( 514 "recovery.jdbc.logtable.create.failed", new String []{logTableName, 515 e.getMessage()})); 516 } 517 } 518 else if (addCompletionLogId) 519 { 520 if (logger.isInfoEnabled()) 521 logger.info(Translate.get("recovery.jdbc.logtable.add.completionlogid", 522 logTableName)); 523 try 524 { 525 stmt = connection.createStatement(); 526 stmt.executeUpdate(logTableAddCompletionLogIdStatement); 527 stmt.close(); 528 } 529 catch (SQLException e) 530 { 531 throw new SQLException (Translate.get( 532 "recovery.jdbc.logtable.add.completionlogid.failed", new String []{ 533 logTableName, e.getMessage()})); 534 } 535 } 536 if (createCheckpointTable) 537 { 538 if (logger.isInfoEnabled()) 539 logger.info(Translate.get("recovery.jdbc.checkpointtable.create", 540 checkpointTableName)); 541 try 542 { 543 stmt = connection.createStatement(); 544 stmt.executeUpdate(checkpointTableCreateStatement); 545 stmt.close(); 546 } 547 catch (SQLException e) 548 { 549 throw new SQLException (Translate.get( 550 "recovery.jdbc.checkpointtable.create.failed", new String []{ 551 logTableName, e.getMessage()})); 552 } 553 554 setInitialEmptyRecoveryLogCheckpoint(); 556 } 557 if (createBackendTable) 558 { 559 if (logger.isInfoEnabled()) 560 logger.info(Translate.get("recovery.jdbc.backendtable.create", 561 backendTableName)); 562 try 563 { 564 stmt = connection.createStatement(); 565 stmt.executeUpdate(backendTableCreateStatement); 566 stmt.close(); 567 } 568 catch (SQLException e) 569 { 570 throw new SQLException (Translate.get( 571 "recovery.jdbc.backendtable.create.failed", new String []{ 572 logTableName, e.getMessage()})); 573 } 574 } 575 if (createDumpTable) 576 { 577 if (logger.isInfoEnabled()) 578 logger.info(Translate.get("recovery.jdbc.dumptable.create", 579 dumpTableName)); 580 try 581 { 582 stmt = connection.createStatement(); 583 stmt.executeUpdate(dumpTableCreateStatement); 584 stmt.close(); 585 } 586 catch (SQLException e) 587 { 588 throw new SQLException (Translate.get( 589 "recovery.jdbc.dumptable.create.failed", new String []{ 590 dumpTableName, e.getMessage()})); 591 } 592 } 593 } 594 595 601 private void setInitialEmptyRecoveryLogCheckpoint() throws SQLException 602 { 603 String checkpointName = "Initial_empty_recovery_log"; 604 PreparedStatement pstmt = null; 605 try 606 { 607 if (logger.isDebugEnabled()) 608 logger.debug("Storing checkpoint " + checkpointName + " at request id " 609 + logTableId); 610 pstmt = getDatabaseConnection().prepareStatement( 611 "INSERT INTO " + checkpointTableName + " VALUES(?,?)"); 612 pstmt.setString(1, checkpointName); 613 pstmt.setLong(2, logTableId); 614 pstmt.executeUpdate(); 615 pstmt.close(); 616 } 617 catch (SQLException e) 618 { 619 try 620 { 621 if (pstmt != null) 622 pstmt.close(); 623 } 624 catch (Exception ignore) 625 { 626 } 627 throw new SQLException (Translate.get( 628 "recovery.jdbc.checkpoint.store.failed", new String []{checkpointName, 629 e.getMessage()})); 630 } 631 } 632 633 639 public void checkRecoveryLogConsistency() throws SQLException 640 { 641 logger.info(Translate.get("recovery.consistency.checking")); 642 643 PreparedStatement stmt = null; 644 ResultSet rs = null; 645 PreparedStatement updateStmt = null; 646 try 647 { 648 stmt = getDatabaseConnection().prepareStatement( 650 "SELECT * FROM " + getLogTableName() + " WHERE exec_status LIKE ?"); 651 stmt.setString(1, LogEntry.EXECUTING); 652 rs = stmt.executeQuery(); 653 654 if (rs.next()) 655 { updateStmt = getDatabaseConnection() 657 .prepareStatement( 658 "UPDATE " + getLogTableName() 659 + " SET exec_status=? WHERE log_id=?"); 660 updateStmt.setString(1, LogEntry.UNKNOWN); 661 do 662 { 663 long logId = rs.getLong("log_id"); 664 if (logger.isWarnEnabled()) 665 logger 666 .warn("Log entry " 667 + logId 668 + " (" 669 + rs.getString(getLogTableSqlColumnName()) 670 + ") still has an executing status in the recovery log. Switching to unknown state."); 671 updateStmt.setLong(2, logId); 672 updateStmt.executeUpdate(); 673 } 674 while (rs.next()); 675 updateStmt.close(); 676 } 677 rs.close(); 678 stmt.close(); 679 680 stmt = getDatabaseConnection().prepareStatement( 682 "SELECT * FROM " + getLogTableName() + " WHERE " 683 + getLogTableSqlColumnName() + " LIKE ? AND " 684 + "transaction_id not in (SELECT transaction_id" + " FROM " 685 + getLogTableName() + " WHERE " + getLogTableSqlColumnName() 686 + " = ? OR " + getLogTableSqlColumnName() + " = ?) "); 687 688 stmt.setString(1, BEGIN + "%"); 689 stmt.setString(2, COMMIT); 690 stmt.setString(3, ROLLBACK); 691 692 rs = stmt.executeQuery(); 693 while (rs.next()) 694 { 695 long tid = rs.getLong("transaction_id"); 697 if (logger.isWarnEnabled()) 698 logger.warn("Transaction " + tid 699 + " has not completed. Inserting a rollback in the recovery log"); 700 long logId = logRollback(new TransactionMetaData(tid, 0, UNKNOWN_USER, 701 false, 0)); 702 logRequestCompletion(logId, true, 0); 703 } 704 rs.close(); 705 } 706 catch (SQLException e) 707 { 708 logger.error("Failed to check recovery log consistency", e); 709 throw new SQLException (Translate.get( 710 "recovery.consistency.checking.failed", e.getMessage())); 711 } 712 finally 713 { 714 try 715 { 716 if (rs != null) 717 rs.close(); 718 } 719 catch (Exception ignore) 720 { 721 } 722 try 723 { 724 if (stmt != null) 725 stmt.close(); 726 } 727 catch (Exception ignore) 728 { 729 } 730 try 731 { 732 if (updateStmt != null) 733 updateStmt.close(); 734 } 735 catch (Exception ignore) 736 { 737 } 738 } 739 740 } 741 742 752 public void resetLogTableIdAndDeleteRecoveryLog(String checkpointName, 753 long newCheckpointId) throws SQLException 754 { 755 long oldId = getCheckpointLogId(checkpointName); 756 synchronized (this) 757 { 758 loggerThread 761 .log(new ResetLogEvent(oldId, newCheckpointId, checkpointName)); 762 logTableId = newCheckpointId + logTableId - oldId; 763 } 764 } 765 766 774 public void resetRecoveryLog() throws SQLException 775 { 776 PreparedStatement pstmt = null; 777 try 778 { 779 781 if (logger.isDebugEnabled()) 782 logger.debug("Deleting " + logTableName + " table."); 783 pstmt = getDatabaseConnection().prepareStatement( 784 "DELETE FROM " + logTableName); 785 pstmt.executeUpdate(); 786 pstmt.close(); 787 788 if (logger.isDebugEnabled()) 789 logger.debug("Deleting " + checkpointTableName + " table."); 790 pstmt = getDatabaseConnection().prepareStatement( 791 "DELETE FROM " + checkpointTableName); 792 pstmt.executeUpdate(); 793 pstmt.close(); 794 795 if (logger.isDebugEnabled()) 796 logger.debug("Resetting checkpoint associated to dumps in " 797 + dumpTableName + " table."); 798 pstmt = getDatabaseConnection().prepareStatement( 799 "UPDATE " + dumpTableName + " SET checkpoint_name=?"); 800 pstmt.setString(1, ""); 801 pstmt.executeUpdate(); 802 pstmt.close(); 803 } 804 catch (SQLException e) 805 { 806 String msg = "Error while resetting recovery log"; 807 logger.error(msg, e); 808 try 809 { 810 if (pstmt != null) 811 pstmt.close(); 812 } 813 catch (Exception ignore) 814 { 815 } 816 throw new SQLException (msg + " (" + e + ")"); 817 } 818 819 setInitialEmptyRecoveryLogCheckpoint(); 821 } 822 823 829 protected void invalidateInternalConnection() 830 { 831 try 832 { 833 internalConnectionManagedByGetDatabaseConnection.close(); 834 } 835 catch (Exception ignore) 836 { 837 } 838 internalConnectionManagedByGetDatabaseConnection = null; 839 } 840 841 847 856 public long logAbort(TransactionMetaData tm) 857 { 858 return logRollback(tm); 860 } 861 862 868 public long logBegin(TransactionMetaData tm) 869 { 870 long id = incrementLogTableId(); 872 LogEntry logEntry; 873 if (tm.isPersistentConnection()) 874 { logEntry = new LogEntry(id, tm.getLogin(), BEGIN + " " 876 + tm.getPersistentConnectionId(), null, LogEntry.TRANSACTION, tm 877 .getTransactionId()); 878 } 879 else 880 { 881 logEntry = new LogEntry(id, tm.getLogin(), BEGIN, null, 882 LogEntry.TRANSACTION, tm.getTransactionId()); 883 } 884 logEntry.setExecutionStatus(LogEntry.SUCCESS); 885 loggerThread.log(new LogRequestEvent(logEntry)); 886 return id; 887 } 888 889 896 public long logClosePersistentConnection(String login, 897 long persistentConnectionId) 898 { 899 long id = incrementLogTableId(); 900 LogEntry logEntry = new LogEntry(id, login, CLOSE_PERSISTENT_CONNECTION, 901 null, LogEntry.PERSISTENT_CONNECTION, persistentConnectionId); 902 logEntry.setExecutionStatus(LogEntry.SUCCESS); 903 loggerThread.log(new LogRequestEvent(logEntry)); 904 return id; 905 } 906 907 913 public long logCommit(TransactionMetaData tm) 914 { 915 long id = incrementLogTableId(); 916 loggerThread.log(new LogCommitEvent(new LogEntry(id, tm.getLogin(), COMMIT, 917 null, LogEntry.TRANSACTION, tm.getTransactionId()))); 918 tm.setLogId(id); 919 return id; 920 } 921 922 927 public void logLogEntry(LogEntry logEntry) 928 { 929 loggerThread.log(new LogRequestEvent(logEntry)); 930 } 931 932 939 public long logOpenPersistentConnection(String login, 940 long persistentConnectionId) 941 { 942 long id = incrementLogTableId(); 943 LogEntry logEntry = new LogEntry(id, login, OPEN_PERSISTENT_CONNECTION, 944 null, LogEntry.PERSISTENT_CONNECTION, persistentConnectionId); 945 logEntry.setExecutionStatus(LogEntry.EXECUTING); 946 loggerThread.log(new LogRequestEvent(logEntry)); 947 return id; 948 } 949 950 957 public long logReleaseSavepoint(TransactionMetaData tm, String name) 958 { 959 long id = incrementLogTableId(); 960 loggerThread.log(new LogRequestEvent(new LogEntry(id, tm.getLogin(), 961 "release " + name, null, LogEntry.TRANSACTION, tm.getTransactionId()))); 962 return id; 963 } 964 965 private LogEntry buildLogEntry(AbstractRequest request, long id, 966 long execTime, int updateCount) 967 { 968 String autoConnTrans; 969 long tid; 970 if (request.isAutoCommit()) 971 { 972 if (request.isPersistentConnection()) 973 { 974 autoConnTrans = LogEntry.PERSISTENT_CONNECTION; 975 tid = request.getPersistentConnectionId(); 976 } 977 else 978 { 979 autoConnTrans = LogEntry.AUTOCOMMIT; 980 tid = 0; 981 } 982 } 983 else 984 { 985 autoConnTrans = LogEntry.TRANSACTION; 986 tid = request.getTransactionId(); 987 } 988 LogEntry logEntry = new LogEntry(id, request.getLogin(), request 989 .getSqlOrTemplate(), request.getPreparedStatementParameters(), 990 autoConnTrans, tid, request.getEscapeProcessing(), request.getId(), 991 execTime, updateCount); 992 return logEntry; 993 } 994 995 1002 public long logRequestExecuting(AbstractRequest request) 1003 { 1004 long id = incrementLogTableId(); 1005 long execTime = 0; 1006 if (request.getStartTime() != 0) 1007 { 1008 if (request.getEndTime() != 0) 1009 execTime = request.getEndTime() - request.getStartTime(); 1010 else 1011 execTime = System.currentTimeMillis() - request.getStartTime(); 1012 } 1013 loggerThread.log(new LogRequestEvent( 1014 buildLogEntry(request, id, execTime, 0))); 1015 request.setLogId(id); 1016 return id; 1017 } 1018 1019 1027 public void logRequestCompletion(long logId, boolean success, long execTime) 1028 { 1029 logRequestExecuteUpdateCompletion(logId, success, 0, execTime); 1030 } 1031 1032 1041 public void logRequestCompletion(long logId, boolean success, 1042 long execTimeInMs, int updateCount) 1043 { 1044 logRequestExecuteUpdateCompletion(logId, success, updateCount, execTimeInMs); 1045 1046 } 1047 1048 1057 public void logRequestExecuteUpdateCompletion(long logId, boolean success, 1058 int updateCount, long execTime) 1059 { 1060 loggerThread.log(new LogRequestCompletionEvent(logId, success, updateCount, 1061 execTime, getCurrentLogId())); 1062 } 1063 1064 1070 public long logRollback(TransactionMetaData tm) 1071 { 1072 long id = incrementLogTableId(); 1073 loggerThread.log(new LogRollbackEvent(new LogEntry(id, tm.getLogin(), 1075 ROLLBACK, null, LogEntry.TRANSACTION, tm.getTransactionId()))); 1076 tm.setLogId(id); 1077 return id; 1078 } 1079 1080 1087 public long logRollbackToSavepoint(TransactionMetaData tm, 1088 String savepointName) 1089 { 1090 long id = incrementLogTableId(); 1091 loggerThread.log(new LogRequestEvent(new LogEntry(id, tm.getLogin(), 1092 "rollback " + savepointName, null, LogEntry.TRANSACTION, tm 1093 .getTransactionId()))); 1094 tm.setLogId(id); 1095 return id; 1096 } 1097 1098 1105 public long logSetSavepoint(TransactionMetaData tm, String name) 1106 { 1107 long id = incrementLogTableId(); 1108 loggerThread 1109 .log(new LogRequestEvent(new LogEntry(id, tm.getLogin(), "savepoint " 1110 + name, null, LogEntry.TRANSACTION, tm.getTransactionId()))); 1111 tm.setLogId(id); 1112 return id; 1113 } 1114 1115 1120 private synchronized void checkIfShuttingDown() throws SQLException 1121 { 1122 if (isShuttingDown) 1123 throw new SQLException ( 1124 "Recovery log is shutting down, log access has been denied"); 1125 } 1126 1127 1131 public synchronized void shutdown() 1132 { 1133 if (isShuttingDown) 1134 return; 1135 isShuttingDown = true; 1136 if (loggerThread != null) 1137 { 1138 ShutdownLogEvent event = new ShutdownLogEvent(); 1139 try 1140 { 1141 postAndWaitFor(event); 1142 } 1143 catch (SQLException e) { 1145 logger.warn("Thread interrupted while awaiting log shutdown", e); 1146 } 1147 } 1148 } 1149 1150 1154 1157 public synchronized void beginRecovery() 1158 { 1159 recoveringNb++; 1160 } 1161 1162 1169 public void cleanRecoveryLog() throws SQLException 1170 { 1171 checkIfShuttingDown(); 1172 1173 PreparedStatement stmt = null; 1174 1175 ResultSet rs = null; 1176 PreparedStatement pstmt = null; 1177 try 1178 { 1179 stmt = getDatabaseConnection().prepareStatement( 1181 "SELECT log_id," + logTableSqlColumnName + " FROM " + logTableName 1182 + " WHERE exec_status LIKE ?"); 1183 stmt.setString(1, LogEntry.FAILED); 1184 rs = stmt.executeQuery(); 1185 if (rs.next()) 1186 { pstmt = getDatabaseConnection().prepareStatement( 1188 "DELETE FROM " + logTableName + " WHERE log_id=?"); 1189 do 1190 { 1191 String sql = rs.getString(2); 1192 AbstractRequest decodedRequest = requestFactory.requestFromString( 1193 sql, false, false, timeout, "\n"); 1194 1196 if (decodedRequest instanceof StoredProcedure) 1199 { 1200 pstmt.setLong(1, rs.getLong(1)); 1201 pstmt.executeUpdate(); 1202 } 1203 } 1204 while (rs.next()); 1205 pstmt.close(); 1206 } 1207 rs.close(); 1208 stmt.close(); 1209 } 1210 catch (SQLException e) 1211 { 1212 invalidateInternalConnection(); 1213 try 1214 { 1215 if (stmt != null) 1216 stmt.close(); 1217 } 1218 catch (Exception ignore) 1219 { 1220 } 1221 try 1222 { 1223 if (pstmt != null) 1224 pstmt.close(); 1225 } 1226 catch (Exception ignore) 1227 { 1228 } 1229 try 1230 { 1231 if (rs != null) 1232 rs.close(); 1233 } 1234 catch (Exception ignore) 1235 { 1236 } 1237 throw new SQLException ("Unable get cleanup recovery log (" + e + ")"); 1238 } 1239 } 1240 1241 1247 public synchronized void endRecovery() 1248 { 1249 recoveringNb--; 1250 if (recoveringNb == 0) 1251 { 1252 try 1253 { 1254 cleanRecoveryLog(); 1255 } 1256 catch (SQLException e) 1257 { 1258 logger.error(Translate.get("recovery.cleaning.failed"), e); 1259 } 1260 } 1261 } 1262 1263 1272 public BackendRecoveryInfo getBackendRecoveryInfo(String databaseName, 1273 String backendName) throws SQLException 1274 { 1275 checkIfShuttingDown(); 1276 1277 PreparedStatement stmt = null; 1278 String checkpoint = null; 1279 int backendState = BackendState.UNKNOWN; 1280 try 1281 { 1282 stmt = getDatabaseConnection().prepareStatement( 1284 "SELECT * FROM " + backendTableName 1285 + " WHERE backend_name LIKE ? AND database_name LIKE ?"); 1286 stmt.setString(1, backendName); 1287 stmt.setString(2, databaseName); 1288 ResultSet rs = stmt.executeQuery(); 1289 1290 if (rs.next()) 1291 { 1292 checkpoint = rs.getString("checkpoint_name"); 1293 backendState = rs.getInt("backend_state"); 1294 } 1295 rs.close(); 1296 } 1297 catch (SQLException e) 1298 { 1299 invalidateInternalConnection(); 1300 logger.info( 1301 "An error occured while retrieving backend recovery information", e); 1302 throw e; 1303 } 1304 finally 1305 { 1306 try 1307 { 1308 if (stmt != null) 1309 stmt.close(); 1310 } 1311 catch (Exception ignore) 1312 { 1313 } 1314 } 1315 return new BackendRecoveryInfo(backendName, checkpoint, backendState, 1316 databaseName); 1317 } 1318 1319 1324 synchronized protected long getCurrentLogId() 1325 { 1326 return logTableId; 1327 } 1328 1329 1337 public long getLastRequestId(long controllerId) throws SQLException 1338 { 1339 String request = "select max(request_id) from " + logTableName 1340 + " where (request_id > ?) and (request_id <= ?)"; 1341 return getLastId(request, controllerId, null); 1342 } 1343 1344 1352 public long getLastTransactionId(long controllerId) throws SQLException 1353 { 1354 String request = "select max(transaction_id) from " + logTableName 1355 + " where (transaction_id > ?) and (transaction_id <= ?)"; 1356 return getLastId(request, controllerId, null); 1357 } 1358 1359 1367 public long getLastConnectionId(long controllerId) throws SQLException 1368 { 1369 String request = "select max(transaction_id) from " + logTableName 1370 + " where (transaction_id > ?) and (transaction_id <= ?) " + " and " 1371 + getLogTableSqlColumnName() + " like ?"; 1372 return getLastId(request, controllerId, OPEN_PERSISTENT_CONNECTION); 1373 } 1374 1375 1389 private long getLastId(String request, long controllerId, String sql) 1390 throws SQLException 1391 { 1392 checkIfShuttingDown(); 1393 1394 PreparedStatement stmt = null; 1395 ResultSet rs = null; 1396 try 1397 { 1398 stmt = getDatabaseConnection().prepareStatement(request); 1399 long minIdForThisController = controllerId << DistributedRequestManager.CONTROLLER_ID_SHIFT_BITS; 1400 long maxIdForThisController = minIdForThisController 1401 | ~DistributedRequestManager.CONTROLLER_ID_BIT_MASK; 1402 stmt.setLong(1, minIdForThisController); 1403 stmt.setLong(2, maxIdForThisController); 1404 if (sql != null) 1405 stmt.setString(3, sql); 1406 rs = stmt.executeQuery(); 1407 if (rs.next()) 1408 return rs.getLong(1); 1409 else 1410 return minIdForThisController; 1412 } 1413 catch (SQLException e) 1414 { 1415 invalidateInternalConnection(); 1416 throw e; 1417 } 1418 finally 1419 { 1420 try 1421 { 1422 if (rs != null) 1423 rs.close(); 1424 } 1425 catch (Exception ignore) 1426 { 1427 } 1428 try 1429 { 1430 if (stmt != null) 1431 stmt.close(); 1432 } 1433 catch (Exception ignore) 1434 { 1435 } 1436 } 1437 } 1438 1439 1444 public long getRecoveringNb() 1445 { 1446 return recoveringNb; 1447 } 1448 1449 1454 public int getRecoveryBatchSize() 1455 { 1456 return recoveryBatchSize; 1457 } 1458 1459 1467 public boolean hasLoggedBeginForTransaction(Long tid) throws SQLException 1468 { 1469 if (loggerThread.hasLogEntryForTransaction(tid.longValue())) 1471 return true; 1472 1473 PreparedStatement pstmt = null; 1475 ResultSet rs = null; 1476 try 1477 { 1478 pstmt = getDatabaseConnection().prepareStatement( 1479 "select count(*) from " + logTableName + " where transaction_id=?"); 1480 pstmt.setLong(1, tid.longValue()); 1483 rs = pstmt.executeQuery(); 1484 return rs.next(); 1485 } 1486 catch (SQLException e) 1487 { 1488 invalidateInternalConnection(); 1489 throw e; 1490 } 1491 finally 1492 { 1493 try 1494 { 1495 if (rs != null) 1496 rs.close(); 1497 } 1498 catch (Exception ignore) 1499 { 1500 } 1501 try 1502 { 1503 if (pstmt != null) 1504 pstmt.close(); 1505 } 1506 catch (Exception ignore) 1507 { 1508 } 1509 } 1510 } 1511 1512 1518 public synchronized boolean isRecovering() 1519 { 1520 return recoveringNb > 0; 1521 } 1522 1523 1532 public LogEntry getNextLogEntry(long previousLogEntryId) throws SQLException 1533 { 1534 checkIfShuttingDown(); 1535 1536 ResultSet rs = null; 1537 boolean emptyResult; 1538 PreparedStatement stmt = null; 1539 try 1540 { 1541 stmt = getDatabaseConnection().prepareStatement( 1542 "SELECT * FROM " + logTableName + " WHERE log_id=?"); 1543 do 1545 { 1546 previousLogEntryId++; 1547 stmt.setLong(1, previousLogEntryId); 1548 if (rs != null) 1550 rs.close(); 1551 rs = stmt.executeQuery(); 1552 emptyResult = !rs.next(); 1553 } 1554 while (emptyResult && (previousLogEntryId <= logTableId)); 1555 1556 if (emptyResult) 1558 return null; 1559 1560 long id = rs.getLong(COLUMN_INDEX_LOG_ID); 1563 String user = rs.getString(COLUMN_INDEX_VLOGIN); 1564 String sql = rs.getString(COLUMN_INDEX_SQL); 1565 String sqlParams = rs.getString(COLUMN_INDEX_SQL_PARAMS); 1566 String autoConnTran = rs.getString(COLUMN_INDEX_AUTO_CONN_TRAN); 1567 long transactionId = rs.getLong(COLUMN_INDEX_TRANSACTION_ID); 1568 long requestId = rs.getLong(COLUMN_INDEX_REQUEST_ID); 1569 long execTime = rs.getLong(COLUMN_INDEX_EXEC_TIME); 1570 int updateCount = rs.getInt(COLUMN_INDEX_UPDATE_COUNT); 1571 String status = rs.getString(COLUMN_INDEX_EXEC_STATUS); 1572 long completionLogId = rs.getLong(COLUMN_INDEX_COMPLETION_LOG_ID); 1573 return new LogEntry(id, user, sql, sqlParams, autoConnTran, 1576 transactionId, false, requestId, execTime, updateCount, status, 1577 completionLogId); 1578 } 1579 catch (SQLException e) 1580 { 1581 invalidateInternalConnection(); 1582 throw new SQLException (Translate.get("recovery.jdbc.recover.failed", e)); 1583 } 1584 finally 1585 { 1586 try 1587 { 1588 if (rs != null) 1589 rs.close(); 1590 } 1591 catch (Exception ignore) 1592 { 1593 } 1594 try 1595 { 1596 if (stmt != null) 1597 stmt.close(); 1598 } 1599 catch (Exception ignore) 1600 { 1601 } 1602 } 1603 } 1604 1605 1616 public long getNumberOfLogEntries(long lowerLogId, long upperLogId) 1617 throws SQLException 1618 { 1619 checkIfShuttingDown(); 1620 1621 GetNumberOfLogEntriesEvent event = new GetNumberOfLogEntriesEvent( 1622 lowerLogId, upperLogId); 1623 postAndWaitFor(event); 1624 return event.getNbOfLogEntries(); 1625 } 1626 1627 1636 long getNumberOfLogEntries() throws SQLException 1637 { 1638 Statement stmt; 1639 stmt = getDatabaseConnection().createStatement(); 1640 ResultSet rs = null; 1641 try 1642 { 1643 rs = stmt.executeQuery("select count(*) from " + logTableName); 1644 rs.next(); 1645 return rs.getLong(1); 1646 } 1647 finally 1648 { 1649 if (rs != null) 1650 { 1651 rs.close(); 1652 } 1653 if (stmt != null) 1654 { 1655 stmt.close(); 1656 } 1657 } 1658 } 1659 1660 1668 public int getUpdateCountResultForQuery(long requestId) throws SQLException 1669 { 1670 checkIfShuttingDown(); 1671 1672 GetUpdateCountEvent event = new GetUpdateCountEvent( 1673 getDatabaseConnection(), this, requestId); 1674 postAndWaitFor(event); 1675 if (event.getCatchedException() instanceof SQLException ) 1676 throw (SQLException ) event.getCatchedException(); 1677 1678 return event.getUpdateCount(); 1683 } 1684 1685 1693 public boolean findCommitForTransaction(long transactionId) 1694 throws SQLException 1695 { 1696 checkIfShuttingDown(); 1697 1698 FindCommitEvent event = new FindCommitEvent(getDatabaseConnection(), this, 1699 transactionId); 1700 postAndWaitFor(event); 1701 if (event.getCatchedException() != null) 1702 throw event.getCatchedException(); 1703 return event.wasFound(); 1704 } 1705 1706 1714 public String getCommitStatusForTransaction(long transactionId) 1715 throws SQLException 1716 { 1717 checkIfShuttingDown(); 1718 1719 FindCommitEvent event = new FindCommitEvent(getDatabaseConnection(), this, 1720 transactionId); 1721 postAndWaitFor(event); 1722 if (event.getCatchedException() != null) 1723 throw event.getCatchedException(); 1724 return event.getStatus(); 1725 } 1726 1727 1735 public boolean findRollbackForTransaction(long transactionId) 1736 throws SQLException 1737 { 1738 checkIfShuttingDown(); 1739 1740 FindRollbackEvent event = new FindRollbackEvent(getDatabaseConnection(), 1741 this, transactionId); 1742 postAndWaitFor(event); 1743 if (event.getCatchedException() != null) 1744 throw event.getCatchedException(); 1745 return event.wasFound(); 1746 } 1747 1748 1757 public String getRollbackStatusForTransaction(long transactionId) 1758 throws SQLException 1759 { 1760 checkIfShuttingDown(); 1761 1762 FindRollbackEvent event = new FindRollbackEvent(getDatabaseConnection(), 1763 this, transactionId); 1764 postAndWaitFor(event); 1765 if (event.getCatchedException() != null) 1766 throw event.getCatchedException(); 1767 return event.getStatus(); 1768 } 1769 1770 1785 public RecoveryTask recoverNextRequest(long previousRequestId, 1786 AbstractScheduler scheduler) throws SQLException 1787 { 1788 RecoveryTask task = null; 1789 1790 LogEntry logEntry = getNextLogEntry(previousRequestId); 1792 if (logEntry == null) 1793 return null; 1794 1795 long transactionId = logEntry.getTid(); 1797 long id = logEntry.getLogId(); 1798 String user = logEntry.getLogin(); 1799 String sql = logEntry.getQuery().trim(); 1800 String status = logEntry.getExecutionStatus(); 1801 long completionLogId = logEntry.getCompletionLogId(); 1802 1803 boolean escapeProcessing = true; 1804 1805 if (CLOSE_PERSISTENT_CONNECTION.equals(sql)) 1806 { 1807 if (logger.isDebugEnabled()) 1808 logger.debug("closing persistent connection: " + transactionId); 1809 task = new RecoveryTask(transactionId, id, 1810 new ClosePersistentConnectionTask(1, 1, user, transactionId), status); 1811 } 1812 else if (OPEN_PERSISTENT_CONNECTION.equals(sql)) 1813 { 1814 if (logger.isDebugEnabled()) 1815 logger.debug("opening persistent connection: " + transactionId); 1816 task = new RecoveryTask(transactionId, id, 1817 new OpenPersistentConnectionTask(1, 1, user, transactionId), status); 1818 } 1819 else if (BEGIN.equals(sql)) 1820 { 1821 task = new RecoveryTask(transactionId, id, new BeginTask(1, 1, 1824 new TransactionMetaData(transactionId, (long) timeout * 1000, user, 1825 false, 0)), status); 1826 if (logger.isDebugEnabled()) 1827 logger.debug("begin transaction: " + transactionId); 1828 } 1829 else if (sql.startsWith(BEGIN)) 1830 { 1831 long persistentConnectionId = Long.parseLong(sql 1834 .substring(BEGIN.length()).trim()); 1835 task = new RecoveryTask(transactionId, id, new BeginTask(1, 1, 1836 new TransactionMetaData(transactionId, (long) timeout * 1000, user, 1837 true, persistentConnectionId)), status); 1838 if (logger.isDebugEnabled()) 1839 logger.debug("begin transaction: " + transactionId 1840 + " on persistent connection " + persistentConnectionId); 1841 } 1842 else if (COMMIT.equals(sql)) 1843 { task = new RecoveryTask(transactionId, id, new CommitTask(1, 1, 1845 new TransactionMetaData(transactionId, (long) timeout * 1000, user, 1846 false, 0)), status); 1847 if (logger.isDebugEnabled()) 1848 logger.debug("commit transaction: " + transactionId); 1849 } 1850 else if (ROLLBACK.equals(sql)) 1851 { int index = sql.indexOf(' '); 1853 if (index == -1) 1854 { 1855 task = new RecoveryTask(transactionId, id, new RollbackTask(1, 1, 1856 new TransactionMetaData(transactionId, (long) timeout * 1000, user, 1857 false, 0)), status); 1858 if (logger.isDebugEnabled()) 1859 logger.debug("rollback transaction: " + transactionId); 1860 } 1861 else 1862 { String savepointName = sql.substring(index).trim(); 1864 task = new RecoveryTask(transactionId, id, new RollbackToSavepointTask( 1865 1, 1, new TransactionMetaData(transactionId, (long) timeout * 1000, 1866 user, false, 0), savepointName), status); 1867 if (logger.isDebugEnabled()) 1868 logger.debug("rollback transaction to savepoint: " + transactionId); 1869 } 1870 } 1871 else if (sql.startsWith("savepoint ")) 1872 { String savepointName = sql.substring(sql.indexOf(' ')).trim(); 1874 task = new RecoveryTask(transactionId, id, new SavepointTask(1, 1, 1875 new TransactionMetaData(transactionId, (long) timeout * 1000, user, 1876 false, 0), savepointName), status); 1877 if (logger.isDebugEnabled()) 1878 logger.debug("transaction set savepoint: " + transactionId); 1879 } 1880 else if (sql.startsWith("release ")) 1881 { String savepointName = sql.substring(sql.indexOf(' ')); 1884 task = new RecoveryTask(transactionId, id, new ReleaseSavepointTask(1, 1, 1885 new TransactionMetaData(transactionId, (long) timeout * 1000, user, 1886 false, 0), savepointName), status); 1887 if (logger.isDebugEnabled()) 1888 logger.debug("transaction release savepoint: " + transactionId); 1889 } 1890 else 1891 { 1892 AbstractRequest decodedRequest = requestFactory.requestFromString(sql, 1894 false, escapeProcessing, timeout, "\n"); 1895 if (decodedRequest != null) 1896 { 1897 setRequestParameters(logEntry, decodedRequest, scheduler); 1898 if (logger.isDebugEnabled()) 1899 logger.debug("recovering " + decodedRequest.getType()); 1900 1901 if (decodedRequest instanceof AbstractWriteRequest) 1902 { 1903 task = new RecoveryTask(transactionId, id, 1904 new StatementExecuteUpdateTask(1, 1, 1905 (AbstractWriteRequest) decodedRequest), status); 1906 } 1907 else if (decodedRequest instanceof StoredProcedure) 1908 { 1909 task = new RecoveryTask(transactionId, id, 1910 new CallableStatementExecuteTask(1, 1, 1911 (StoredProcedure) decodedRequest, null), status); 1912 } 1913 else 1914 { 1915 if (decodedRequest instanceof UnknownWriteRequest) 1916 { 1917 task = new RecoveryTask(transactionId, id, 1918 new StatementExecuteTask(1, 1, 1919 (AbstractWriteRequest) decodedRequest, null), status); 1920 } 1921 else 1922 { 1924 if (decodedRequest instanceof SelectRequest) 1925 { 1926 decodedRequest.setCursorName("replay_cursor"); 1928 } 1929 1933 task = new RecoveryTask(transactionId, id, 1934 new StatementExecuteQueryTask(1, 1, 1935 (SelectRequest) decodedRequest, null), status); 1936 } 1937 } 1938 } 1939 } 1940 1941 task.setCompletionLogId(completionLogId); 1942 return task; 1943 } 1944 1945 private void setRequestParameters(LogEntry entry, 1946 AbstractRequest decodedRequest, AbstractScheduler scheduler) 1947 { 1948 decodedRequest.setLogin(entry.getLogin()); 1949 decodedRequest.setSqlOrTemplate(entry.getQuery()); 1950 decodedRequest.setPreparedStatementParameters(entry.getQueryParams()); 1951 if (LogEntry.TRANSACTION.equals(entry.getAutoConnTrans())) 1952 { 1953 decodedRequest.setIsAutoCommit(false); 1954 decodedRequest.setTransactionId(entry.getTid()); 1955 decodedRequest 1956 .setTransactionIsolation(org.continuent.sequoia.driver.Connection.DEFAULT_TRANSACTION_ISOLATION_LEVEL); 1957 } 1958 else 1959 { 1960 decodedRequest.setIsAutoCommit(true); 1961 decodedRequest.setTransactionId(scheduler.getNextTransactionId()); 1962 if (LogEntry.PERSISTENT_CONNECTION.equals(entry.getAutoConnTrans())) 1963 { 1964 decodedRequest.setPersistentConnection(true); 1965 decodedRequest.setPersistentConnectionId(entry.getTid()); 1966 } 1967 } 1968 } 1969 1970 1976 1985 public void deleteLogEntriesBeforeCheckpoint(String checkpointName) 1986 throws SQLException 1987 { 1988 checkIfShuttingDown(); 1989 1990 long id = getCheckpointLogId(checkpointName); 1991 PreparedStatement stmt = null; 1992 try 1993 { 1994 stmt = getDatabaseConnection().prepareStatement( 1995 "DELETE FROM " + getLogTableName() + " WHERE log_id<=?"); 1996 stmt.setLong(1, id); 1997 stmt.executeUpdate(); 1998 } 1999 catch (SQLException e) 2000 { 2001 throw new SQLException (Translate.get( 2002 "recovery.jdbc.entries.remove.failed", e.getMessage())); 2003 } 2004 finally 2005 { 2006 try 2007 { 2008 if (stmt != null) 2009 stmt.close(); 2010 } 2011 catch (Exception ignore) 2012 { 2013 } 2014 } 2015 } 2016 2017 2028 public ArrayList getCheckpointNames() throws SQLException 2029 { 2030 checkIfShuttingDown(); 2031 2032 PreparedStatement stmt = null; 2033 2034 try 2035 { 2036 if (logger.isDebugEnabled()) 2041 logger.debug("Retrieving checkpoint names list"); 2042 stmt = getDatabaseConnection().prepareStatement( 2043 "SELECT name from " + checkpointTableName 2044 + " ORDER BY log_id DESC, name DESC"); 2045 ResultSet rs = stmt.executeQuery(); 2046 ArrayList list = new ArrayList (); 2047 while (rs.next()) 2048 { 2049 list.add(rs.getString(1)); 2050 } 2051 rs.close(); 2052 return list; 2053 } 2054 catch (Exception e) 2055 { 2056 invalidateInternalConnection(); 2057 throw new SQLException (Translate.get( 2058 "recovery.jdbc.checkpoint.list.failed", e)); 2059 } 2060 finally 2061 { 2062 try 2063 { 2064 if (stmt != null) 2065 stmt.close(); 2066 } 2067 catch (SQLException ignore) 2068 { 2069 } 2070 } 2071 } 2072 2073 2083 Map getCheckpoints() throws SQLException 2084 { 2085 checkIfShuttingDown(); 2086 2087 PreparedStatement stmt = null; 2088 2089 try 2090 { 2091 if (logger.isDebugEnabled()) 2092 logger.debug("Retrieving checkpoint names list"); 2093 stmt = getDatabaseConnection().prepareStatement( 2094 "SELECT log_id, name from " + checkpointTableName 2095 + " ORDER BY log_id DESC"); 2096 ResultSet rs = stmt.executeQuery(); 2097 Map checkpoints = new TreeMap (); 2098 while (rs.next()) 2099 { 2100 String name = rs.getString("name"); 2101 String id = rs.getString("log_id"); 2102 checkpoints.put(name, id); 2103 } 2104 rs.close(); 2105 return checkpoints; 2106 } 2107 catch (Exception e) 2108 { 2109 invalidateInternalConnection(); 2110 throw new SQLException (Translate.get( 2111 "recovery.jdbc.checkpoint.list.failed", e)); 2112 } 2113 finally 2114 { 2115 try 2116 { 2117 if (stmt != null) 2118 stmt.close(); 2119 } 2120 catch (SQLException ignore) 2121 { 2122 } 2123 } 2124 } 2125 2126 2136 public String [] getCheckpointNameAliases(String checkpointName) 2137 throws SQLException 2138 { 2139 long logId = getCheckpointLogId(checkpointName); 2140 Object ret = executeQuery("SELECT name from " + checkpointTableName 2141 + " where log_id=" + logId + " ORDER BY log_id DESC", 2142 new QueryHandler() 2143 { 2144 public Object run(ResultSet rs) throws SQLException 2145 { 2146 ArrayList aliases = new ArrayList (); 2147 while (rs.next()) 2148 aliases.add(rs.getString("name")); 2149 return aliases.toArray(new String [aliases.size()]); 2150 } 2151 }); 2152 return (String []) ret; 2153 } 2154 2155 abstract class QueryHandler 2156 { 2157 2165 abstract public Object run(ResultSet rs) throws SQLException ; 2166 }; 2167 2168 Object executeQuery(String query, QueryHandler handler) throws SQLException 2169 { 2170 checkIfShuttingDown(); 2171 2172 PreparedStatement stmt = null; 2173 try 2174 { 2175 stmt = getDatabaseConnection().prepareStatement(query); 2176 ResultSet rs = stmt.executeQuery(); 2177 return handler.run(rs); 2178 } 2179 catch (Exception e) 2180 { 2181 invalidateInternalConnection(); 2182 throw new SQLException (Translate.get("recovery.jdbc.query.failed", e)); 2183 } 2184 finally 2185 { 2186 try 2187 { 2188 if (stmt != null) 2189 stmt.close(); } 2191 catch (SQLException ignore) 2192 { 2193 } 2194 } 2195 } 2196 2197 2207 public long getCheckpointLogId(String checkpointName) throws SQLException 2208 { 2209 checkIfShuttingDown(); 2210 2211 GetCheckpointLogIdEvent event = new GetCheckpointLogIdEvent( 2212 getDatabaseConnection(), getCheckpointTableName(), checkpointName); 2213 postAndWaitFor(event); 2214 if (event.getCatchedException() != null) 2215 throw event.getCatchedException(); 2216 return event.getCheckpointLogId(); 2217 } 2218 2219 2228 public CheckpointLogEntry getCheckpointLogEntry(String checkpointName) 2229 throws SQLException 2230 { 2231 checkIfShuttingDown(); 2232 2233 GetCheckpointLogEntryEvent event = new GetCheckpointLogEntryEvent( 2234 getDatabaseConnection(), this, checkpointName); 2235 postAndWaitFor(event); 2236 if (event.getCatchedException() != null) 2237 throw event.getCatchedException(); 2238 return event.getCheckpointLogEntry(); 2239 } 2240 2241 2249 public void moveEntries(long fromId, long shift) 2250 { 2251 synchronized (this) 2252 { 2253 loggerThread.log(new ShiftLogEntriesEvent(fromId, shift)); 2255 logTableId = logTableId + shift; 2258 } 2259 } 2260 2261 2267 public void deleteLogEntriesAndCheckpointBetween(long fromId, long toId) 2268 { 2269 synchronized (this) 2270 { 2271 loggerThread.log(new DeleteLogEntriesAndCheckpointBetweenEvent(fromId, 2273 toId)); 2274 } 2275 } 2276 2277 2283 public void removeCheckpoint(String checkpointName) 2284 { 2285 RemoveCheckpointEvent removeCheckpointEvent = new RemoveCheckpointEvent( 2286 checkpointName); 2287 synchronized (removeCheckpointEvent) 2288 { 2289 loggerThread.log(removeCheckpointEvent); 2290 try 2291 { 2292 removeCheckpointEvent.wait(); 2293 } 2294 catch (InterruptedException ignore) 2295 { 2296 } 2297 } 2298 } 2299 2300 2307 public void storeBackendRecoveryInfo(String databaseName, 2308 BackendRecoveryInfo backendRecoveryInfo) throws SQLException 2309 { 2310 checkIfShuttingDown(); 2311 2312 PreparedStatement stmt = null; 2313 PreparedStatement stmt2 = null; 2314 if ((backendRecoveryInfo.getCheckpoint() == null) 2315 || ((backendRecoveryInfo.getBackendState() != BackendState.DISABLED) && (backendRecoveryInfo 2316 .getBackendState() != BackendState.UNKNOWN))) 2317 backendRecoveryInfo.setCheckpoint(""); else 2319 { getCheckpointLogId(backendRecoveryInfo.getCheckpoint()); 2321 } 2322 2323 try 2324 { 2325 stmt = getDatabaseConnection().prepareStatement( 2327 "SELECT * FROM " + backendTableName 2328 + " WHERE backend_name LIKE ? and database_name LIKE ?"); 2329 stmt.setString(1, backendRecoveryInfo.getBackendName()); 2330 stmt.setString(2, databaseName); 2331 ResultSet rs = stmt.executeQuery(); 2332 boolean mustUpdate = rs.next(); 2333 rs.close(); 2334 if (!mustUpdate) 2335 { 2336 stmt2 = getDatabaseConnection().prepareStatement( 2337 "INSERT INTO " + backendTableName + " values(?,?,?,?)"); 2338 stmt2.setString(1, databaseName); 2339 stmt2.setString(2, backendRecoveryInfo.getBackendName()); 2340 stmt2.setInt(3, backendRecoveryInfo.getBackendState()); 2341 stmt2.setString(4, backendRecoveryInfo.getCheckpoint()); 2342 if (stmt2.executeUpdate() != 1) 2343 throw new SQLException ( 2344 "Error while inserting new backend reference. Incorrect number of rows"); 2345 } 2346 else 2347 { 2348 stmt2 = getDatabaseConnection() 2349 .prepareStatement( 2350 "UPDATE " 2351 + backendTableName 2352 + " set backend_state=?,checkpoint_name=? where backend_name=? and database_name=?"); 2353 stmt2.setInt(1, backendRecoveryInfo.getBackendState()); 2354 stmt2.setString(2, backendRecoveryInfo.getCheckpoint()); 2355 stmt2.setString(3, backendRecoveryInfo.getBackendName()); 2356 stmt2.setString(4, databaseName); 2357 if (stmt2.executeUpdate() != 1) 2358 throw new SQLException ( 2359 "Error while updating backend reference. Incorrect number of rows"); 2360 } 2361 } 2362 catch (SQLException e) 2363 { 2364 invalidateInternalConnection(); 2365 2366 logger.warn("Failed to store backend recovery info", e); 2367 2368 throw new SQLException ("Unable to update checkpoint '" 2369 + backendRecoveryInfo.getCheckpoint() + "' for backend:" 2370 + backendRecoveryInfo.getBackendName()); 2371 } 2372 finally 2373 { 2374 try 2375 { 2376 if (stmt != null) 2377 stmt.close(); 2378 } 2379 catch (Exception ignore) 2380 { 2381 } 2382 try 2383 { 2384 if (stmt2 != null) 2385 stmt2.close(); 2386 } 2387 catch (Exception ignore) 2388 { 2389 } 2390 } 2391 } 2392 2393 2399 public void storeCheckpoint(String checkpointName) throws SQLException 2400 { 2401 if (!validCheckpointName(checkpointName)) 2403 { 2404 throw new SQLException (Translate.get( 2405 "recovery.jdbc.checkpoint.duplicate", checkpointName)); 2406 } 2407 2408 loggerThread.log(new StoreCheckpointWithLogIdEvent(checkpointName, 2409 logTableId)); 2410 } 2411 2412 2419 public void storeCheckpoint(String checkpointName, long logId) 2420 throws SQLException 2421 { 2422 if (!validCheckpointName(checkpointName)) 2424 { 2425 throw new SQLException (Translate.get( 2426 "recovery.jdbc.checkpoint.duplicate", checkpointName)); 2427 } 2428 2429 loggerThread.log(new StoreCheckpointWithLogIdEvent(checkpointName, logId)); 2430 } 2431 2432 2438 2446 public DumpInfo getDumpInfo(String dumpName) throws SQLException 2447 { 2448 checkIfShuttingDown(); 2449 2450 PreparedStatement stmt = null; 2451 2452 try 2453 { 2454 if (logger.isDebugEnabled()) 2455 logger.debug("Retrieving dump " + dumpName + " information"); 2456 stmt = getDatabaseConnection().prepareStatement( 2457 "SELECT * from " + dumpTableName + " WHERE dump_name LIKE ?"); 2458 stmt.setString(1, dumpName); 2459 2460 ResultSet rs = stmt.executeQuery(); 2461 DumpInfo dumpInfo = null; 2462 if (rs.next()) 2463 { 2464 dumpInfo = new DumpInfo(rs.getString("dump_name"), rs 2465 .getTimestamp("dump_date"), rs.getString("dump_path"), rs 2466 .getString("dump_format"), rs.getString("checkpoint_name"), rs 2467 .getString("backend_name"), rs.getString(dumpTableTablesColumnName)); 2468 } 2469 2471 rs.close(); 2472 return dumpInfo; 2473 } 2474 catch (Exception e) 2475 { 2476 invalidateInternalConnection(); 2477 throw new SQLException (Translate.get("recovery.jdbc.dump.info.failed", 2478 new String []{dumpName, e.getMessage()})); 2479 } 2480 finally 2481 { 2482 try 2483 { 2484 if (stmt != null) 2485 stmt.close(); 2486 } 2487 catch (SQLException ignore) 2488 { 2489 } 2490 } 2491 } 2492 2493 2499 public ArrayList getDumpList() throws SQLException 2500 { 2501 checkIfShuttingDown(); 2502 2503 PreparedStatement stmt = null; 2504 2505 try 2506 { 2507 if (logger.isDebugEnabled()) 2508 logger.debug("Retrieving dump list"); 2509 stmt = getDatabaseConnection().prepareStatement( 2510 "SELECT * FROM " + dumpTableName + " ORDER BY dump_date DESC"); 2511 ResultSet rs = stmt.executeQuery(); 2512 ArrayList list = new ArrayList (); 2513 while (rs.next()) 2514 { 2515 list 2516 .add(new DumpInfo(rs.getString("dump_name"), rs 2517 .getTimestamp("dump_date"), rs.getString("dump_path"), rs 2518 .getString("dump_format"), rs.getString("checkpoint_name"), rs 2519 .getString("backend_name"), rs 2520 .getString(dumpTableTablesColumnName))); 2521 } 2522 rs.close(); 2523 return list; 2524 } 2525 catch (Exception e) 2526 { 2527 invalidateInternalConnection(); 2528 throw new SQLException (Translate.get("recovery.jdbc.dump.list.failed", e)); 2529 } 2530 finally 2531 { 2532 try 2533 { 2534 if (stmt != null) 2535 stmt.close(); 2536 } 2537 catch (SQLException ignore) 2538 { 2539 } 2540 } 2541 } 2542 2543 2550 public void removeDump(DumpInfo dumpInfo) throws SQLException 2551 { 2552 checkIfShuttingDown(); 2553 2554 PreparedStatement stmt = null; 2555 2556 try 2557 { 2558 if (logger.isDebugEnabled()) 2559 { 2560 logger.debug("removing dump " + dumpInfo.getDumpName()); 2561 } 2562 stmt = getDatabaseConnection().prepareStatement( 2563 "DELETE FROM " + dumpTableName + " WHERE dump_name=?"); 2564 stmt.setString(1, dumpInfo.getDumpName()); 2565 2566 stmt.executeUpdate(); 2567 } 2568 catch (Exception e) 2569 { 2570 invalidateInternalConnection(); 2571 throw new SQLException (Translate.get("recovery.jdbc.dump.remove.failed", 2572 new String []{dumpInfo.getDumpName(), e.getMessage()})); 2573 } 2574 finally 2575 { 2576 try 2577 { 2578 if (stmt != null) 2579 stmt.close(); 2580 } 2581 catch (SQLException ignore) 2582 { 2583 } 2584 } 2585 } 2586 2587 2593 public void setDumpInfo(DumpInfo dumpInfo) throws VirtualDatabaseException 2594 { 2595 try 2596 { 2597 storeDump(dumpInfo); 2598 } 2599 catch (SQLException e) 2600 { 2601 throw new VirtualDatabaseException(e); 2602 } 2603 } 2604 2605 2611 public void storeDump(DumpInfo dump) throws SQLException 2612 { 2613 checkIfShuttingDown(); 2614 2615 PreparedStatement stmt = null; 2616 2617 if (dump == null) 2618 throw new NullPointerException ( 2619 "Invalid null dump in JDBCRecoverylog.storeDump"); 2620 2621 try 2622 { 2623 if (logger.isDebugEnabled()) 2624 logger.debug("Storing dump " + dump.getDumpName()); 2625 stmt = getDatabaseConnection().prepareStatement( 2626 "INSERT INTO " + dumpTableName + " VALUES (?,?,?,?,?,?,?)"); 2627 stmt.setString(1, dump.getDumpName()); 2628 stmt.setTimestamp(2, new Timestamp (dump.getDumpDate().getTime())); 2629 stmt.setString(3, dump.getDumpPath()); 2630 stmt.setString(4, dump.getDumpFormat()); 2631 stmt.setString(5, dump.getCheckpointName()); 2632 stmt.setString(6, dump.getBackendName()); 2633 stmt.setString(7, dump.getTables()); 2634 2635 stmt.executeUpdate(); 2636 } 2637 catch (Exception e) 2638 { 2639 invalidateInternalConnection(); 2640 throw new SQLException (Translate.get("recovery.jdbc.dump.store.failed", 2641 new String []{dump.getDumpName(), e.getMessage()})); 2642 } 2643 finally 2644 { 2645 try 2646 { 2647 if (stmt != null) 2648 stmt.close(); 2649 } 2650 catch (SQLException ignore) 2651 { 2652 } 2653 } 2654 } 2655 2656 2661 public void storeDumpCheckpointName(String dumpCheckpointName, 2662 long checkpointId) 2663 { 2664 loggerThread.log(new StoreCheckpointWithLogIdEvent(dumpCheckpointName, 2665 checkpointId)); 2666 } 2667 2668 2677 void updateDumpTableColumn(String dumpName, String columnName, String value) 2678 throws SQLException 2679 { 2680 checkIfShuttingDown(); 2681 2682 DumpInfo dumpInfo = getDumpInfo(dumpName); 2683 if (dumpInfo == null) 2684 throw new SQLException ("No such dump name: " + dumpName); 2685 2686 PreparedStatement stmt = null; 2687 int updateCount = 0; 2688 try 2689 { 2690 if (logger.isDebugEnabled()) 2691 logger.debug("Updating '" + columnName + "' for dump '" 2692 + dumpInfo.getDumpName() + "' to " + value); 2693 stmt = getDatabaseConnection().prepareStatement( 2694 "UPDATE " + dumpTableName + " SET " + columnName 2695 + "=? WHERE dump_name=?"); 2696 stmt.setString(1, value); 2697 stmt.setString(2, dumpName); 2698 updateCount = stmt.executeUpdate(); 2699 } 2700 catch (Exception e) 2701 { 2702 invalidateInternalConnection(); 2703 throw new SQLException (Translate.get( 2704 "recovery.jdbc.dump.update.column.failed", new String []{dumpName, 2705 e.getMessage()})); 2706 } 2707 finally 2708 { 2709 try 2710 { 2711 if (stmt != null) 2712 stmt.close(); 2713 } 2714 catch (SQLException ignore) 2715 { 2716 } 2717 } 2718 2719 if (updateCount != 1) 2720 { 2721 String msg = "Invalid update count after dumpTable update."; 2722 logger.error(msg); 2723 throw new SQLException (msg); 2724 } 2725 } 2726 2727 2734 public void updateDumpName(String dumpName, String newDumpName) 2735 throws SQLException 2736 { 2737 updateDumpTableColumn(dumpName, "dump_name", newDumpName); 2738 } 2739 2740 2747 public void updateDumpPath(String dumpName, String newPath) 2748 throws SQLException 2749 { 2750 updateDumpTableColumn(dumpName, "dump_path", newPath); 2751 } 2752 2753 2761 public void updateDumpCheckpoint(String dumpName, String checkpointName) 2762 throws SQLException 2763 { 2764 updateDumpTableColumn(dumpName, "checkpoint_name", checkpointName); 2765 } 2766 2767 2773 2777 public void checkRecoveryLogTables() 2778 { 2779 try 2780 { 2781 intializeDatabase(); 2782 } 2783 catch (SQLException e) 2784 { 2785 throw new RuntimeException ("Unable to initialize the database: " + e); 2786 } 2787 2788 loggerThread = new LoggerThread(this); 2790 loggerThread.start(); 2791 } 2792 2793 2798 public String getBackendTableName() 2799 { 2800 return backendTableName; 2801 } 2802 2803 2808 public String getCheckpointTableName() 2809 { 2810 return checkpointTableName; 2811 } 2812 2813 2818 public String getLogTableName() 2819 { 2820 return logTableName; 2821 } 2822 2823 2828 public String getLogTableSqlColumnName() 2829 { 2830 return logTableSqlColumnName; 2831 } 2832 2833 2844 public void setBackendTableCreateStatement(String createTable, 2845 String tableName, String checkpointNameType, String backendNameType, 2846 String backendStateType, String databaseNameType, String extraStatement) 2847 { 2848 this.backendTableCreateTable = createTable; 2849 this.backendTableName = tableName; 2850 this.backendTableDatabaseName = databaseNameType; 2851 this.backendTableBackendName = backendNameType; 2852 this.backendTableBackendState = backendStateType; 2853 this.backendTableCheckpointName = checkpointNameType; 2854 this.backendTableExtraStatement = extraStatement; 2855 this.backendTableCreateStatement = createTable + " " + backendTableName 2856 + " (database_name " + databaseNameType + ", backend_name " 2857 + backendNameType + ",backend_state " + backendStateType 2858 + ", checkpoint_name " + checkpointNameType + " " + extraStatement 2859 + ")"; 2860 2861 if (logger.isDebugEnabled()) 2862 logger.debug(Translate.get("recovery.jdbc.backendtable.statement", 2863 backendTableCreateStatement)); 2864 } 2865 2866 2875 public void setCheckpointTableCreateStatement(String createTable, 2876 String tableName, String nameType, String logIdType, String extraStatement) 2877 { 2878 this.checkpointTableCreateTable = createTable; 2879 this.checkpointTableName = tableName; 2880 this.checkpointTableNameType = nameType; 2881 this.checkpointTableLogIdType = logIdType; 2882 this.checkpointTableExtraStatement = extraStatement; 2883 2888 checkpointTableCreateStatement = createTable + " " + tableName + " (name " 2889 + nameType + ",log_id " + logIdType + extraStatement + ")"; 2890 if (logger.isDebugEnabled()) 2891 logger.debug(Translate.get("recovery.jdbc.checkpointtable.statement", 2892 checkpointTableCreateStatement)); 2893 } 2894 2895 2910 public void setDumpTableCreateStatement(String createTable, String tableName, 2911 String dumpNameColumnType, String dumpDateColumnType, 2912 String dumpPathColumnType, String dumpFormatColumnType, 2913 String checkpointNameColumnType, String backendNameColumnType, 2914 String tablesColumnName, String tablesColumnType, String extraStatement) 2915 { 2916 this.dumpTableCreateTable = createTable; 2917 this.dumpTableName = tableName; 2918 this.dumpTableDumpNameColumnType = dumpNameColumnType; 2919 this.dumpTableDumpDateColumnType = dumpDateColumnType; 2920 this.dumpTableDumpPathColumnType = dumpPathColumnType; 2921 this.dumpTableDumpFormatColumnType = dumpFormatColumnType; 2922 this.dumpTableCheckpointNameColumnType = checkpointNameColumnType; 2923 this.dumpTableBackendNameColumnType = backendNameColumnType; 2924 this.dumpTableTablesColumnName = tablesColumnName; 2925 this.dumpTableTablesColumnType = tablesColumnType; 2926 this.dumpTableExtraStatementDefinition = extraStatement; 2927 2928 2938 dumpTableCreateStatement = dumpTableCreateTable + " " + dumpTableName 2939 + " (dump_name " + dumpTableDumpNameColumnType + ",dump_date " 2940 + dumpDateColumnType + ",dump_path " + dumpPathColumnType 2941 + ",dump_format " + dumpFormatColumnType + ",checkpoint_name " 2942 + checkpointNameColumnType + ",backend_name " + backendNameColumnType 2943 + "," + dumpTableTablesColumnName + " " + tablesColumnType 2944 + extraStatement + ")"; 2945 if (logger.isDebugEnabled()) 2946 logger.debug(Translate.get("recovery.jdbc.dumptable.statement", 2947 dumpTableCreateStatement)); 2948 } 2949 2950 2967 public void setLogTableCreateStatement(String createTable, String tableName, 2968 String idType, String vloginType, String sqlName, String sqlType, 2969 String sqlParamsType, String autoConnTranColumnType, 2970 String transactionIdType, String requestIdType, String execTimeType, 2971 String updateCountType, String extraStatement) 2972 { 2973 this.logTableCreateTable = createTable; 2974 this.logTableName = tableName; 2975 this.logTableLogIdType = idType; 2976 this.logTableVloginType = vloginType; 2977 this.logTableSqlColumnName = sqlName; 2978 this.logTableSqlType = sqlType; 2979 this.logTableAutoConnTranColumnType = autoConnTranColumnType; 2980 this.logTableTransactionIdType = transactionIdType; 2981 this.logTableRequestIdType = requestIdType; 2982 this.logTableExecTimeType = execTimeType; 2983 this.logTableUpdateCountType = updateCountType; 2984 this.logTableExtraStatement = extraStatement; 2985 logTableCreateStatement = createTable + " " + tableName + " (log_id " 2986 + idType + ",vlogin " + vloginType + "," + logTableSqlColumnName + " " 2987 + sqlType + "," + logTableSqlColumnName + "_param " + sqlParamsType 2988 + ",auto_conn_tran " + autoConnTranColumnType + ",transaction_id " 2989 + transactionIdType + ",request_id " + requestIdType + ",exec_status " 2990 + autoConnTranColumnType + ",exec_time " + execTimeType 2991 + ",update_count " + updateCountType + ", completion_log_id " + idType 2992 + extraStatement + ")"; 2993 if (logger.isDebugEnabled()) 2994 logger.debug(Translate.get("recovery.jdbc.logtable.statement", 2995 logTableCreateStatement)); 2996 String type = idType; 2997 int index = type.toUpperCase().indexOf("NOT NULL"); 2998 if (index >= 0) 2999 type = type.substring(0, index) + " DEFAULT -1 NOT NULL"; 3000 else 3001 type += " DEFAULT -1"; 3002 3003 logTableAddCompletionLogIdStatement = "alter table " + tableName 3004 + " add completion_log_id " + type; 3005 } 3006 3007 3013 3020 private boolean validCheckpointName(String checkpointName) 3021 throws SQLException 3022 { 3023 PreparedStatement stmt = null; 3024 ResultSet rs = null; 3025 try 3026 { 3027 stmt = getDatabaseConnection().prepareStatement( 3028 "SELECT * FROM " + checkpointTableName + " WHERE name LIKE ?"); 3029 stmt.setString(1, checkpointName); 3030 rs = stmt.executeQuery(); 3031 3032 boolean checkpointExists = rs.next(); 3035 rs.close(); 3036 return !checkpointExists; 3037 } 3038 catch (SQLException e) 3039 { 3040 invalidateInternalConnection(); 3041 throw new SQLException (Translate.get( 3042 "recovery.jdbc.checkpoint.check.failed", e)); 3043 } 3044 finally 3045 { 3046 try 3047 { 3048 if (stmt != null) 3049 stmt.close(); 3050 } 3051 catch (SQLException ignore) 3052 { 3053 } 3054 } 3055 } 3056 3057 3063 3066 public String getAssociatedString() 3067 { 3068 return "jdbcrecoverylog"; 3069 } 3070 3071 3077 public String [][] getData() 3078 { 3079 Statement stmt = null; 3080 ResultSet rs = null; 3081 try 3082 { 3083 stmt = getDatabaseConnection().createStatement(); 3084 rs = stmt.executeQuery("select * from " + logTableName); 3085 ArrayList list = new ArrayList (); 3086 while (rs.next()) 3087 { 3088 list.add(new String []{rs.getString(3), rs.getString(2), 3090 rs.getString(1), rs.getString(4)}); 3091 } 3092 String [][] result = new String [list.size()][4]; 3093 for (int i = 0; i < list.size(); i++) 3094 result[i] = (String []) list.get(i); 3095 return result; 3096 } 3097 catch (SQLException e) 3098 { 3099 return null; 3100 } 3101 finally 3102 { 3103 try 3104 { 3105 rs.close(); 3106 } 3107 catch (SQLException ignore) 3108 { 3109 } 3110 try 3111 { 3112 stmt.close(); 3113 } 3114 catch (SQLException ignore) 3115 { 3116 } 3117 } 3118 } 3119 3120 String [] getColumnNames() 3121 { 3122 Statement stmt = null; 3123 ResultSet rs = null; 3124 try 3125 { 3126 stmt = getDatabaseConnection().createStatement(); 3127 rs = stmt.executeQuery("select * from " + logTableName 3128 + " where log_id=0"); 3129 int columnCount = rs.getMetaData().getColumnCount(); 3130 String [] columnNames = new String [columnCount]; 3131 for (int i = 0; i < columnCount; i++) 3132 { 3133 columnNames[i] = rs.getMetaData().getColumnName(i + 1); 3134 } 3135 return columnNames; 3136 } 3137 catch (SQLException e) 3138 { 3139 return new String [0]; 3140 } 3141 finally 3142 { 3143 try 3144 { 3145 rs.close(); 3146 } 3147 catch (SQLException ignore) 3148 { 3149 } 3150 try 3151 { 3152 stmt.close(); 3153 } 3154 catch (SQLException ignore) 3155 { 3156 } 3157 } 3158 } 3159 3160 3167 long[] getIndexes() throws SQLException 3168 { 3169 Statement stmt; 3170 stmt = getDatabaseConnection().createStatement(); 3171 ResultSet rs = null; 3172 try 3173 { 3174 rs = stmt.executeQuery("select min(log_id),max(log_id) from " 3175 + logTableName); 3176 rs.next(); 3177 long min = rs.getLong(1); 3178 long max = rs.getLong(2); 3179 return new long[]{min, max}; 3180 } 3181 finally 3182 { 3183 if (rs != null) 3184 { 3185 rs.close(); 3186 } 3187 if (stmt != null) 3188 { 3189 stmt.close(); 3190 } 3191 } 3192 } 3193 3194 3204 String [][] getLogEntries(long from, int maxrows) 3205 { 3206 Statement stmt = null; 3207 ResultSet rs = null; 3208 try 3209 { 3210 stmt = getDatabaseConnection().createStatement(); 3211 stmt.setMaxRows(maxrows); 3212 rs = stmt.executeQuery("select * from " + logTableName 3213 + " where log_id >= " + from); 3214 int columnCount = rs.getMetaData().getColumnCount(); 3215 List logEntries = new ArrayList (); 3216 while (rs.next()) 3217 { 3218 String [] logEntry = new String [columnCount]; 3219 for (int i = 0; i < columnCount; i++) 3220 { 3221 logEntry[i] = rs.getString(i + 1); 3222 } 3223 logEntries.add(logEntry); 3224 } 3225 String [][] result = new String [logEntries.size()][columnCount]; 3226 for (int i = 0; i < logEntries.size(); i++) 3227 result[i] = (String []) logEntries.get(i); 3228 return result; 3229 } 3230 catch (SQLException e) 3231 { 3232 return null; 3233 } 3234 finally 3235 { 3236 try 3237 { 3238 rs.close(); 3239 } 3240 catch (SQLException ignore) 3241 { 3242 } 3243 try 3244 { 3245 stmt.close(); 3246 } 3247 catch (SQLException ignore) 3248 { 3249 } 3250 } 3251 } 3252 3253 3257 3263 void postAndWaitFor(LogEvent event) throws SQLException 3264 { 3265 synchronized (event) 3266 { 3267 loggerThread.log(event); 3268 try 3269 { 3270 event.wait(); 3271 } 3272 catch (InterruptedException e) 3273 { 3274 throw new SQLException ("Interrupted while waiting for LogEvent " 3275 + event); 3276 } 3277 } 3278 } 3279 3280 3285 public String getXml() 3286 { 3287 StringBuffer info = new StringBuffer (); 3288 info.append("<" + DatabasesXmlTags.ELT_RecoveryLog + " " 3289 + DatabasesXmlTags.ATT_driver + "=\"" + driverClassName + "\" " 3290 + DatabasesXmlTags.ATT_url + "=\"" + url + "\" "); 3291 if (driverPath != null) 3292 { 3293 info.append(DatabasesXmlTags.ATT_driverPath + "=\"" + driverPath + "\" "); 3294 } 3295 info.append(DatabasesXmlTags.ATT_login + "=\"" + login + "\" " 3296 + DatabasesXmlTags.ATT_password + "=\"" + password + "\" " 3297 + DatabasesXmlTags.ATT_requestTimeout + "=\"" + (timeout / 1000) 3298 + "\" " + DatabasesXmlTags.ATT_recoveryBatchSize + "=\"" 3299 + recoveryBatchSize + "\">"); 3300 info.append("<" + DatabasesXmlTags.ELT_RecoveryLogTable + " " 3302 + DatabasesXmlTags.ATT_createTable + "=\"" + logTableCreateTable 3303 + "\" " + DatabasesXmlTags.ATT_tableName + "=\"" + logTableName + "\" " 3304 + DatabasesXmlTags.ATT_logIdColumnType + "=\"" + logTableLogIdType 3305 + "\" " + DatabasesXmlTags.ATT_vloginColumnType + "=\"" 3306 + logTableVloginType + "\" " + DatabasesXmlTags.ATT_sqlColumnType 3307 + "=\"" + logTableSqlType + "\" " 3308 + DatabasesXmlTags.ATT_autoConnTranColumnType + "=\"" 3309 + logTableAutoConnTranColumnType + "\" " 3310 + DatabasesXmlTags.ATT_transactionIdColumnType + "=\"" 3311 + logTableTransactionIdType + "\" " 3312 + DatabasesXmlTags.ATT_requestIdColumnType + "=\"" 3313 + logTableRequestIdType + "\" " 3314 + DatabasesXmlTags.ATT_execTimeColumnType + "=\"" 3315 + logTableExecTimeType + "\" " 3316 + DatabasesXmlTags.ATT_updateCountColumnType + "=\"" 3317 + logTableUpdateCountType + "\" " 3318 + DatabasesXmlTags.ATT_extraStatementDefinition + "=\"" 3319 + logTableExtraStatement + "\"/>"); 3320 info.append("<" + DatabasesXmlTags.ELT_CheckpointTable + " " 3322 + DatabasesXmlTags.ATT_createTable + "=\"" + checkpointTableCreateTable 3323 + "\" " + DatabasesXmlTags.ATT_tableName + "=\"" + checkpointTableName 3324 + "\" " + DatabasesXmlTags.ATT_checkpointNameColumnType + "=\"" 3325 + checkpointTableNameType + "\" " 3326 + DatabasesXmlTags.ATT_logIdColumnType + "=\"" 3327 + checkpointTableLogIdType + "\" " 3328 + DatabasesXmlTags.ATT_extraStatementDefinition + "=\"" 3329 + checkpointTableExtraStatement + "\"" + "/>"); 3330 info.append("<" + DatabasesXmlTags.ELT_BackendTable + " " 3332 + DatabasesXmlTags.ATT_createTable + "=\"" + backendTableCreateTable 3333 + "\" " + DatabasesXmlTags.ATT_tableName + "=\"" + backendTableName 3334 + "\" " + DatabasesXmlTags.ATT_databaseNameColumnType + "=\"" 3335 + backendTableDatabaseName + "\" " 3336 + DatabasesXmlTags.ATT_backendNameColumnType + "=\"" 3337 + backendTableBackendName + "\" " 3338 + DatabasesXmlTags.ATT_backendStateColumnType + "=\"" 3339 + backendTableBackendState + "\" " 3340 + DatabasesXmlTags.ATT_checkpointNameColumnType + "=\"" 3341 + backendTableCheckpointName + "\" " 3342 + DatabasesXmlTags.ATT_extraStatementDefinition + "=\"" 3343 + backendTableExtraStatement + "\"" + "/>"); 3344 info.append("<" + DatabasesXmlTags.ELT_DumpTable + " " 3346 + DatabasesXmlTags.ATT_createTable + "=\"" + dumpTableCreateTable 3347 + "\" " + DatabasesXmlTags.ATT_tableName + "=\"" + dumpTableName 3348 + "\" " + DatabasesXmlTags.ATT_dumpNameColumnType + "=\"" 3349 + dumpTableDumpNameColumnType + "\" " 3350 + DatabasesXmlTags.ATT_dumpDateColumnType + "=\"" 3351 + dumpTableDumpDateColumnType + "\" " 3352 + DatabasesXmlTags.ATT_dumpPathColumnType + "=\"" 3353 + dumpTableDumpPathColumnType + "\" " 3354 + DatabasesXmlTags.ATT_dumpFormatColumnType + "=\"" 3355 + dumpTableDumpFormatColumnType + "\" " 3356 + DatabasesXmlTags.ATT_checkpointNameColumnType + "=\"" 3357 + dumpTableCheckpointNameColumnType + "\" " 3358 + DatabasesXmlTags.ATT_backendNameColumnType + "=\"" 3359 + dumpTableBackendNameColumnType + "\" " 3360 + DatabasesXmlTags.ATT_tablesColumnName + "=\"" 3361 + dumpTableTablesColumnName + "\" " 3362 + DatabasesXmlTags.ATT_tablesColumnType + "=\"" 3363 + dumpTableTablesColumnType + "\" " 3364 + DatabasesXmlTags.ATT_extraStatementDefinition + "=\"" 3365 + dumpTableExtraStatementDefinition + "\"" + "/>"); 3366 info.append("</" + DatabasesXmlTags.ELT_RecoveryLog + ">"); 3367 3368 return info.toString(); 3369 } 3370 3371} | Popular Tags |