001 /** 002 * Copyright (c) 2003 Daffodil Software Ltd all rights reserved, 003 * Modifications Copyright (c) 2008 Regiscope Digital Imaging Co, LLC, All rights reserved. 004 * This program is free software; you can redistribute it and/or modify 005 * it under the terms of version 2 of the GNU General Public License as 006 * published by the Free Software Foundation. 007 * There are special exceptions to the terms and conditions of the GPL 008 * as it is applied to this software. See the GNU General Public License for more details. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018 */ 019 020 package org.dbreplicator.replication.synchronize; 021 022 import java.math.*; 023 import java.sql.*; 024 import java.util.*; 025 026 import org.dbreplicator.replication.*; 027 import org.dbreplicator.replication.DBHandler.*; 028 import org.dbreplicator.replication.column.*; 029 import org.dbreplicator.replication.xml.*; 030 import java.io.BufferedWriter; 031 032 /** 033 * This is the main class which handles the synchronization operations on main table 034 * when merge handler reads Update operation from the XML file. 035 */ 036 037 public class OperationUpdate extends AbstractSynchronize { 038 039 private PreparedStatement psForInitialRecordInShadowTable, psForActualUpdateInTable, 040 preparedStatementForLastRecord, preparedStatementForCommonId, 041 preparedStatementForDelete, updatePreparedStatement, psForOtherCommonId, 042 psForInsertIntoActualTable, psForUpdateInShadowTableForRemoteServer, 043 updatePreparedStatementSetNull; 044 private ArrayList voilationPkKeys; 045 private boolean NO_OPERATION = false; 046 047 048 049 /** 050 * OperationUpdate handles the case related to update operation i.e. when a 051 * record with update operation is found in XML File. 052 * @param repTable0 053 * @param clientConnection0 054 * @param columnObjectTreeMap0 055 * @param conisderedId0 056 * @param lastSyncId0 057 * @param remoteServerName0 058 * @param dbHandler0 AbstractDataBaseHandler 059 * @param bw0 BufferedWriter 060 * @param replicationType0 String 061 * @param transactionLogType0 String 062 * @param mdi0 MetaDataInfo 063 * @param isFirstPass0 boolean 064 * @param isCurrentTableCyclic0 boolean 065 * @throws Exception 066 */ 067 public OperationUpdate(RepTable repTable0, Connection clientConnection0, 068 TreeMap columnObjectTreeMap0, Object conisderedId0, 069 Object lastSyncId0, String remoteServerName0, /* bjt add last sync id */ 070 AbstractDataBaseHandler dbHandler0, BufferedWriter bw0, 071 String replicationType0, String transactionLogType0, 072 MetaDataInfo mdi0, boolean isFirstPass0, 073 boolean isCurrentTableCyclic0) throws SQLException { 074 repTable = repTable0; 075 allColumnsMap = repTable.getAllColumns(); 076 connection = clientConnection0; 077 columnObjectTreeMap = columnObjectTreeMap0; 078 remoteServerName = remoteServerName0; 079 conisderedId = conisderedId0; 080 lastSyncId = lastSyncId0; /* bjt - add last sync id */ 081 tableName = repTable.getSchemaQualifiedName().toString(); 082 dbHandler = dbHandler0; 083 mdi = mdi0; 084 shadowTable = dbHandler.getShadowTableName(tableName); 085 bw = bw0; 086 replicationType = replicationType0; 087 transactionLogType = transactionLogType0; 088 089 // so as to include the last record of the shadow table in the search. 090 // when traceOriginalRecord is called, then we dont want the last record searched to be included in the search. 091 // so we are increasing the Last UID by 1. 092 Object lastShadowUidObj = getLastUIDFromShadowTable(); 093 if ((lastShadowUidObj instanceof Integer)) { //for firebird 094 lastShadowUid = new Long( ( (Integer) lastShadowUidObj).longValue()+1); 095 } 096 else if ((lastShadowUidObj instanceof String)) { //for MySQL 097 lastShadowUid = new Long((Long.parseLong((String)lastShadowUidObj)+1)); 098 } 099 else { 100 lastShadowUid = new Long(((Long)lastShadowUidObj).longValue()+1); 101 } 102 xmlElement_NULL = new XMLElement("NULL"); 103 xmlElement_NULL.elementValue = "NULL"; 104 isFirstPass = isFirstPass0; 105 isCurrentTableCyclic = isCurrentTableCyclic0; 106 } 107 108 /** 109 * searches the record for pk_key in shadow table and updates it in the corresponding main table. 110 * @param currentElement 111 * @throws SQLException 112 * @throws RepException 113 */ 114 public void execute(XMLElement currentElement) throws SQLException, RepException { 115 try { 116 ArrayList updateElements = currentElement.getChildElements(); 117 ArrayList rowElements = ( (XMLElement) updateElements.get(0)).getChildElements(); 118 ArrayList changedColumns = ( (XMLElement) updateElements.get(1)).getChildElements(); 119 ArrayList primaryColumns = ( (XMLElement) updateElements.get(2)).getChildElements(); 120 tableColumnNames = new String[rowElements.size()]; 121 String[] tableColumnValues = new String[rowElements.size()]; 122 AbstractColumnObject[] tableColumnObj = new AbstractColumnObject[rowElements.size()]; 123 primaryColumnNames = repTable.getPrimaryColumns(); 124 int noOfPrimaryCol = primaryColumnNames.length; 125 Object[] newPrimaryColumnValues = new Object[primaryColumnNames.length]; 126 127 String columnNam; 128 for (int i = 0; i < rowElements.size(); i++) { 129 columnNam = ( (XMLElement) rowElements.get(i)).getAttribute(); 130 columnNam =(String)allColumnsMap.get(columnNam); 131 tableColumnNames[i] = columnNam; 132 tableColumnValues[i] = ( (XMLElement) rowElements.get(i)).elementValue; 133 tableColumnObj[i] = ( (AbstractColumnObject) columnObjectTreeMap.get(tableColumnNames[i])); 134 for (int j = 0; j < noOfPrimaryCol; j++) { 135 if (tableColumnNames[i].equalsIgnoreCase(primaryColumnNames[j])) { 136 newPrimaryColumnValues[j] = ( (AbstractColumnObject) tableColumnObj[i]).getObject(tableColumnValues[i]); 137 break; 138 } 139 } 140 } 141 Object[] objArray = getObjectArrayForTable(tableColumnValues,tableColumnObj); 142 Object[] tableColumnObjects = (Object[]) objArray[0]; 143 ArrayList nullTableColumnObjects = (ArrayList) objArray[1]; 144 145 // When No changes are done in updated column,i.e old row and new updated row is same ("NO_OPERATION") 146 boolean changedCol = true; 147 if (changedColumns.size() == 0) { 148 if (repTable.isLocalServerWinner()) { 149 return; 150 } 151 changedCol = false; // special handelling for NO_Operation ... same record with same pk willl be updated again 152 } 153 int noOfChangedColumns = changedColumns.size(); 154 changedColumnNames = new String[noOfChangedColumns]; 155 changedColumnValues = new String[noOfChangedColumns]; 156 XMLElement[] changedColElement = new XMLElement[noOfChangedColumns]; 157 AbstractColumnObject[] changedColumnObject = new AbstractColumnObject[noOfChangedColumns]; 158 for (int i = 0; i < noOfChangedColumns; i++) { 159 changedColElement[i] = (XMLElement) changedColumns.get(i); 160 changedColumnNames[i] = changedColElement[i].getAttribute(); 161 for (int j = 0; j < rowElements.size(); j++) { 162 if (changedColumnNames[i].equalsIgnoreCase(tableColumnNames[j])) { 163 changedColumnValues[i] = tableColumnValues[j]; 164 changedColElement[i].elementValue = tableColumnValues[j]; 165 break; 166 } 167 } 168 changedColumnObject[i] = ( (AbstractColumnObject) columnObjectTreeMap.get(changedColumnNames[i])); 169 } 170 171 String[] primaryColumnValues = new String[noOfPrimaryCol]; 172 primaryKeyColumnsObject = new AbstractColumnObject[noOfPrimaryCol]; 173 174 for (int i = 0; i < noOfPrimaryCol; i++) { 175 primaryColumnValues[i] = ( (XMLElement) primaryColumns.get(i)).elementValue; 176 primaryKeyColumnsObject[i] = ( (AbstractColumnObject)columnObjectTreeMap.get(primaryColumnNames[i])); 177 } 178 Object[] primaryValueObjects = getObjectArrayForPrimaryKey(primaryColumnValues); 179 180 // SPECIAL CASE ONLY FOR NO_OPERATION 181 if (!changedCol) { 182 // special handelling for NO_OPERATION ... assigning changedColumns to current primary keys 183 int pkLength = primaryColumnNames.length; 184 changedColElement = new XMLElement[pkLength]; 185 changedColumnValues = new String[pkLength]; 186 changedColumnNames = new String[pkLength]; 187 changedColumnObject = new AbstractColumnObject[pkLength]; 188 for (int i = 0; i < primaryColumnNames.length; i++) { 189 changedColumnNames[i] = primaryColumnNames[i]; 190 changedColumnValues[i] = primaryColumnValues[i]; 191 changedColumnObject[i] = primaryKeyColumnsObject[i]; 192 XMLElement temp = new XMLElement(changedColumnNames[i]); 193 temp.setElementValue(changedColumnValues[i]); 194 changedColElement[i] = temp; 195 } 196 noOfChangedColumns = pkLength; 197 NO_OPERATION = true; 198 } 199 200 if (psForInitialRecordInShadowTable == null) { 201 psForInitialRecordInShadowTable = makePSForInitialRecordInShadowTable(); 202 203 // if ( == null ) 204 // because we can get diffrent number of changed columns for all or other rows 205 // so we have to create ps every time for this. 206 } 207 psForActualUpdateInTable = makePSForActualUpdateInTable(); 208 209 if (psForUpdateInShadowTableForRemoteServer == null) { 210 psForUpdateInShadowTableForRemoteServer = 211 makePSForUpdateInShadowTableForRemoteServer(); 212 } 213 214 psForUpdateInShadowTableForRemoteServer.setString(1, remoteServerName); 215 int k = 0; 216 if (isFirstPass) { 217 for (int i = 0; i < noOfChangedColumns; i++) { 218 if (repTable.isIgnoredColumn(changedColumnNames[i])) 219 continue; 220 /** @todo Changes made by sube For cyclic handling */ 221 AbstractColumnObject columnOject = ( (AbstractColumnObject)columnObjectTreeMap.get(changedColumnNames[i])); 222 223 /* 224 Once setAutoCommitFlag is set to true,we shouldn't change it to false 225 by checking for other columns for that 'if' check is used 226 */ 227 if (setAutoCommitFlag) { 228 setAutoCommitFlag = checkAutocommit(dbHandler, columnOject); 229 connection.setAutoCommit(setAutoCommitFlag); 230 } 231 232 if (isCurrentTableCyclic && 233 repTable.isForeignKeyColumn(changedColumnNames[i])) { 234 columnOject.setColumnObject(psForActualUpdateInTable, "NULL", k + 1); 235 k++; 236 } 237 else { 238 columnOject.setColumnObject(psForActualUpdateInTable,changedColElement[i], k + 1); 239 k++; 240 } 241 242 } 243 244 for (int i = 0; i < noOfPrimaryCol; i++) { 245 psForActualUpdateInTable.setObject(noOfChangedColumns + i + 1,primaryValueObjects[i]); 246 psForUpdateInShadowTableForRemoteServer.setObject(3 + i,primaryValueObjects[i]); 247 } 248 249 Tracer tracer = new Tracer(); 250 //getLastRecord(primaryValueObjects, conisderedId, tracer); /* bjt - should use last_sync_id ? */ 251 /* bjt try using greater of consideredid or lastsyncid */ 252 if (Integer.parseInt(conisderedId.toString()) > Integer.parseInt(lastSyncId.toString())) { 253 getLastRecord(primaryValueObjects, conisderedId, tracer); 254 } else { 255 getLastRecord(primaryValueObjects, lastSyncId, tracer); 256 } 257 voilationPkKeys = new ArrayList(); 258 if (!tracer.recordFound) { 259 // get max UID/syncId from Shadow Table 260 Object lastSyncId = getLastUIDFromShadowTable(); 261 // setting Object values for Update query for Shadow TABLW for REMOTE SERVER 262 psForUpdateInShadowTableForRemoteServer.setObject(2, lastSyncId); 263 264 try { 265 psForActualUpdateInTable.executeUpdate(); 266 // STATUS - A update remote server name for update case WHEN NO OPERATION IS PERFORMED ON CLIENT SIDE i.e. no entry is found in shadow table 267 // update the old record in shadow table with remoteServer STATUIS -B 268 psForUpdateInShadowTableForRemoteServer.executeUpdate(); // Status- B Update in shadow table to remoteserverName 269 for (int i = 0; i < newPrimaryColumnValues.length; i++) { 270 psForUpdateInShadowTableForRemoteServer.setObject(i + 3,newPrimaryColumnValues[i]); 271 } 272 psForUpdateInShadowTableForRemoteServer.executeUpdate(); // Status- A Update in shadow table to remoteserverName 273 loggingUpdateOperation(tableName, primaryColumnNames,primaryValueObjects, changedColumnNames,changedColumnValues, replicationType); 274 writeUpdateOperationInTransactionLogFile(bw, tableName,primaryColumnNames, primaryValueObjects, changedColumnNames,changedColumnValues, replicationType, transactionLogType); 275 updateCount++; 276 } 277 catch (SQLException ex) { 278 279 //System.out.println(" Operation update Error Code :: "+ex.getErrorCode()); 280 281 // ex.printStackTrace(); 282 // PRIMARY CONSTRAINT VOILATION 283 if (!dbHandler.isPrimaryKeyException(ex)) { 284 RepConstants.writeERROR_FILE(ex); 285 throw ex; 286 } 287 288 /* 289 ============================= 290 COMMENTED CODE FOR ROLLBACK 291 IF WE DO NOT WANT TO DELETE, 292 BUT THIS MAKES A CASE TO FAIL 293 INVOLVING NO_OPERATION. 294 ============================= 295 primaryViolation(newPrimaryColumnValues, new Tracer()); // no use of tracer over here 296 lastSyncId = getLastUIDFromShadowTable(); 297 // setting Object values for Update query for Shadow TABLW for REMOTE SERVER 298 psForUpdateInShadowTableForRemoteServer.setObject(2, lastSyncId); 299 psForActualUpdateInTable.executeUpdate(); 300 psForUpdateInShadowTableForRemoteServer.executeUpdate(); 301 for (int i = 0; i < newPrimaryColumnValues.length; i++) { 302 psForUpdateInShadowTableForRemoteServer.setObject(i + 3,newPrimaryColumnValues[i]); 303 } 304 psForUpdateInShadowTableForRemoteServer.executeUpdate(); // Status- A Update in shadow table to remoteserverName 305 */ 306 // deleting the record 307 if (preparedStatementForDelete == null) { 308 preparedStatementForDelete =makePreparedStatementForDeleteOnTable(); 309 } 310 for (int i = 0; i < newPrimaryColumnValues.length; i++) { 311 preparedStatementForDelete.setObject(i + 1,newPrimaryColumnValues[i]); 312 } 313 314 if (isCurrentTableCyclic) { 315 setNullInColumnOfChildTableToUpdateRecord(primaryColumnNames,newPrimaryColumnValues); 316 } 317 preparedStatementForDelete.executeUpdate(); 318 lastSyncId = getLastUIDFromShadowTable(); 319 320 // setting Object values for Update query for Shadow TABLW for REMOTE SERVER 321 psForUpdateInShadowTableForRemoteServer.setObject(2, lastSyncId); 322 psForActualUpdateInTable.executeUpdate(); 323 loggingUpdateOperation(tableName, primaryColumnNames,primaryValueObjects, changedColumnNames,changedColumnValues, replicationType); 324 writeUpdateOperationInTransactionLogFile(bw, tableName, primaryColumnNames, primaryValueObjects, changedColumnNames,changedColumnValues, replicationType, transactionLogType); 325 updateCount++; 326 // remoteserver name is not updated so that operaion performed on subscriber is taken on the publisher node. 327 } 328 329 } 330 else { 331 if (tracer.type.equals(RepConstants.delete_operation)) { 332 if (NO_OPERATION) { 333 return; 334 } 335 if (repTable.isLocalServerWinner()) { 336 return; 337 } 338 if (psForInsertIntoActualTable == null) { 339 psForInsertIntoActualTable = connection.prepareStatement(repTable.createInsertQueryForSnapShot()); 340 } 341 int nullCnt = 0; 342 int position = 0; 343 for (int i = 0; i < tableColumnObjects.length; i++) { 344 Object obj = tableColumnObjects[i]; 345 String columnName = tableColumnNames[i]; 346 if (repTable.isIgnoredColumn(columnName)) 347 continue; 348 349 /** 350 * Following check has been added to handle the case of 351 * cyclic tables.Set value of all columns to null that are refering 352 * to other columns. 353 */ 354 355 if (isCurrentTableCyclic && 356 repTable.isForeignKeyColumn(columnName)) { 357 AbstractColumnObject aco = ( (AbstractColumnObject)columnObjectTreeMap.get(columnName)); 358 aco.setColumnObject(psForInsertIntoActualTable, "NULL",position + 1); 359 } 360 else if (obj != null) { 361 psForInsertIntoActualTable.setObject(position + 1, obj); 362 } 363 else { 364 ( (AbstractColumnObject) nullTableColumnObjects.get(nullCnt)). 365 setColumnObject(psForInsertIntoActualTable, xmlElement_NULL,position + 1); 366 nullCnt++; 367 } 368 position++; 369 } 370 Object lastSyncId; 371 try { 372 lastSyncId = getLastUIDFromShadowTable(); 373 psForInsertIntoActualTable.execute(); 374 } 375 catch (SQLException ex) { // primary constrant voilation 376 if (!dbHandler.isPrimaryKeyException(ex)) { 377 throw ex; 378 } 379 primaryViolation(newPrimaryColumnValues, new Tracer()); // no operation found for tracer till now 380 lastSyncId = getLastUIDFromShadowTable(); 381 psForInsertIntoActualTable.execute(); 382 } 383 384 // update shadow table with remoteserverNAme FOR INSERT 385 // get max UID/syncId from Shadow Table 386 psForUpdateInShadowTableForRemoteServer.setObject(2, lastSyncId); 387 for (int i = 0; i < newPrimaryColumnValues.length; i++) { 388 psForUpdateInShadowTableForRemoteServer.setObject(i + 3,newPrimaryColumnValues[i]); 389 } 390 psForUpdateInShadowTableForRemoteServer.executeUpdate(); // updating new Inserted Record in shadow table with remoteServerName 391 } 392 else { 393 // primary key values to be updated with newPrimaryColumnValues 394 /* 395 * bjt - if only foreign key fields are updated and foreign key is of type update...cascade 396 * and table referenced by foreign key is a replicated table, then 397 * we might not need to do any updating. Instead, for now do a select to see if fields have already been 398 * updated, and if so do nothing. We can comment the select out or make it a config option 399 * later. 400 * 401 */ 402 Object[] primaryKeyObj = tracer.primaryKeyValues; 403 if (!repTable.isLocalServerWinner()) { 404 for (int i = 0; i < primaryKeyObj.length; i++) { 405 psForActualUpdateInTable.setObject(noOfChangedColumns + i + 1,primaryKeyObj[i]); 406 psForUpdateInShadowTableForRemoteServer.setObject(3 + i,primaryKeyObj[i]); 407 } 408 Object lastSyncId = getLastUIDFromShadowTable(); 409 try { 410 psForActualUpdateInTable.executeUpdate(); 411 loggingUpdateOperation(tableName, primaryColumnNames,primaryValueObjects, changedColumnNames,changedColumnValues, replicationType); 412 writeUpdateOperationInTransactionLogFile(bw, tableName,primaryColumnNames, primaryValueObjects, changedColumnNames, changedColumnValues, replicationType, transactionLogType); 413 updateCount++; 414 } 415 catch (SQLException ex) { // primary key constrant voilation 416 if (!dbHandler.isPrimaryKeyException(ex)) { 417 throw ex; 418 } 419 420 Tracer trForRollback = new Tracer(); 421 trForRollback.primaryKeyValues = primaryKeyObj; 422 primaryViolation(newPrimaryColumnValues, trForRollback); 423 Object[] trPkValues = trForRollback.primaryKeyValues; 424 // setting the rollbacked pk values if original pk values has also been rollbacked. 425 for (int i = 0; i < trPkValues.length; i++) { 426 psForActualUpdateInTable.setObject(noOfChangedColumns + i + 1,trPkValues[i]); 427 psForUpdateInShadowTableForRemoteServer.setObject(3 + i,trPkValues[i]); 428 } 429 lastSyncId = getLastUIDFromShadowTable(); 430 psForActualUpdateInTable.executeUpdate(); 431 } 432 433 // update shadow table with remoteserverNAme FOR UODATE 434 psForUpdateInShadowTableForRemoteServer.setObject(2, lastSyncId); 435 psForUpdateInShadowTableForRemoteServer.executeUpdate(); // Status -B Updating record with remoteSErverNAme on shadowTable 436 437 for (int i = 0; i < newPrimaryColumnValues.length; i++) { 438 psForUpdateInShadowTableForRemoteServer.setObject(i + 3,newPrimaryColumnValues[i]); 439 } 440 psForUpdateInShadowTableForRemoteServer.executeUpdate(); // Status- A Updating record with remoteSErverNAme on shadowTable 441 } 442 else { 443 // case when both the records are updated on both the sides 444 Object[] oldRow = tracer.oldRow; 445 ResultSet trRS = tracer.rs; 446 ArrayList localChangedColumnsName = new ArrayList(); 447 for (int i = 0; i < tableColumnNames.length; i++) { 448 String columnName = tableColumnNames[i]; 449 Object newRecord = trRS.getObject(columnName); 450 if (newRecord != null || oldRow[i] != null) { 451 try { 452 if (!newRecord.equals(oldRow[i])) { 453 localChangedColumnsName.add(columnName); 454 } 455 } 456 catch (NullPointerException ex1) { 457 localChangedColumnsName.add(columnName); 458 } 459 } 460 else { 461 continue; 462 } 463 } 464 ArrayList actualListOFColumns = new ArrayList(); 465 ArrayList actualListOFValues = new ArrayList(); 466 for (int i = 0; i < changedColumnNames.length; i++) { 467 if (repTable.isIgnoredColumn(changedColumnNames[i])) 468 continue; 469 if (!localChangedColumnsName.contains(changedColumnNames[i])) { 470 actualListOFColumns.add(changedColumnNames[i]); 471 actualListOFValues.add(changedColumnValues[i]); 472 } 473 } 474 if (actualListOFColumns.size() > 0) { 475 Object lastSyncId = getLastUIDFromShadowTable(); 476 String query = repTable.getUpdatePreStmt(actualListOFColumns, primaryColumnNames); 477 PreparedStatement pst = connection.prepareStatement(query); 478 int index = actualListOFColumns.size(); 479 for (int i = 0; i < index; i++) { 480 AbstractColumnObject columnObject = (AbstractColumnObject)columnObjectTreeMap.get(actualListOFColumns.get(i)); 481 //once setAutoCommitFlag is set to false,we shouldn't change it to true 482 // by checking for other columns for that 'if' check is used 483 if (setAutoCommitFlag) { 484 setAutoCommitFlag = checkAutocommit(dbHandler, columnObject); 485 connection.setAutoCommit(setAutoCommitFlag); 486 } 487 488 // Changes required to handle the case of update for clob and blob data type 489 columnObject.setColumnObject(pst,(String) actualListOFValues.get(i), i + 1); 490 } 491 for (int j = 0; j < primaryKeyObj.length; j++) { 492 pst.setObject(index + j + 1, primaryKeyObj[j]); 493 } 494 try { 495 pst.executeUpdate(); 496 } 497 catch (SQLException ex) { 498 if (!dbHandler.isPrimaryKeyException(ex)) { 499 throw ex; 500 } 501 // primary Constrant Violation 502 primaryViolation(newPrimaryColumnValues, new Tracer()); // no case found till now for tracer 503 try { 504 pst.executeUpdate(); 505 } 506 catch (SQLException ex10) { 507 if (!dbHandler.isPrimaryKeyException(ex)) { 508 throw ex; 509 } 510 511 /** 512 * In primary key voilation method we try to rollback 513 * the record upto its original record.After rollback 514 * operation we try to update the record. During update 515 * operation if primary key voilation occure then we delete 516 * the record in case of cyclic table. 517 */ 518 519 if (isCurrentTableCyclic) { 520 deleteRecord(newPrimaryColumnValues); 521 pst.executeUpdate(); 522 } 523 } 524 } 525 finally { 526 if (setAutoCommitFlag == false) 527 setAutocomitTrueAndCommitRecord(connection); 528 } 529 530 if (pst != null) { 531 pst.close(); 532 } 533 // update in shadow table not done for remoteServerName because of this case will be true only in case of LOCAL SERVER WINNER AND 534 // THIS RECORD SHOULD BE SENT TO REMOTE SERVER FOR UPDATE... 535 } 536 } 537 } 538 } 539 } 540 else if (isCurrentTableCyclic) { 541 542 // System.out.println("<----------------------SECOND PASS CURRENT TABLE FOUND CYCLIC --------------------->"); 543 /** 544 * Code given below is executed in second pass for cyclic tables. 545 * Update all the columns with actual values that are set to null in first pass. 546 */ 547 boolean isForeignKeycolumn = false; 548 int columnIndex = 0; 549 String updateQueryForSecondPass = makeQueryToUpdateTheRecordInSecodPass(changedColumnNames, primaryColumnNames); 550 // System.out.println("^^^^^^^^^^^^^^^^^ UPDATE QUERY SECOND PASS ::" +updateQueryForSecondPass); 551 if (!updateQueryForSecondPass.equalsIgnoreCase("")) { 552 psForActualUpdateInTable = connection.prepareStatement(updateQueryForSecondPass); 553 for (int i = 0; i < noOfChangedColumns; i++) { 554 if (repTable.isIgnoredColumn(changedColumnNames[i])) 555 continue; 556 557 // Set value of foriegn key columns 558 if (isCurrentTableCyclic && 559 repTable.isForeignKeyColumn(changedColumnNames[i])) { 560 AbstractColumnObject columnOject = ( (AbstractColumnObject)columnObjectTreeMap.get(changedColumnNames[i])); 561 isForeignKeycolumn = true; 562 //once setAutoCommitFlag is set to false,we shouldn't change it to true 563 // by checking for other columns for that 'if' check is used 564 if (setAutoCommitFlag) { 565 setAutoCommitFlag = checkAutocommit(dbHandler, columnOject); 566 connection.setAutoCommit(setAutoCommitFlag); 567 } 568 columnOject.setColumnObject(psForActualUpdateInTable,changedColElement[i], columnIndex + 1); 569 columnIndex++; 570 } 571 } 572 // set the value of primary columns 573 for (int i = 0; i < primaryValueObjects.length; i++) { 574 psForActualUpdateInTable.setObject(columnIndex + 1,primaryValueObjects[i]); 575 } 576 // Update the records with actual values which are set to null in first pass 577 if (isForeignKeycolumn) { 578 Object lastSyncId = getLastUIDFromShadowTable(); 579 psForActualUpdateInTable.execute(); 580 // update the old record in shadow table with remoteServer STATUIS -B 581 // setting Object values for Update query for Shadow TABLW for REMOTE SERVER 582 psForUpdateInShadowTableForRemoteServer.setObject(2, lastSyncId); 583 for (int i = 0; i < newPrimaryColumnValues.length; i++) { 584 psForUpdateInShadowTableForRemoteServer.setObject(i + 3,newPrimaryColumnValues[i]); 585 } 586 psForUpdateInShadowTableForRemoteServer.executeUpdate(); 587 } 588 } 589 } 590 } 591 catch (SQLException ex2) { 592 if (!dbHandler.isPrimaryKeyException(ex2)) { 593 throw ex2; 594 } 595 /** 596 * This catch was added on 22 march 04 . 597 * There is Primary Key Violation occure in Scripttestcases 6 , 7 and 8 598 * All these are complex cases.So the Exception has been dumped for 599 * temporary solution as suggested. 600 */ 601 } 602 finally { 603 if (setAutoCommitFlag == false) { 604 setAutocomitTrueAndCommitRecord(connection); 605 } 606 607 if (psForActualUpdateInTable != null) { 608 try { 609 psForActualUpdateInTable.close(); 610 } 611 catch (SQLException ex3) { 612 //sqleception must be ignored 613 } 614 } 615 } 616 } 617 618 /** 619 * @param rst 620 * @return ResultSet containing only one record corresponding to commonId 621 * record may be of status 'A' or 'B'. 622 * @throws SQLException 623 */ 624 private ResultSet getOtherCommonIdResultSet(ResultSet rst) throws SQLException { 625 if (psForOtherCommonId == null) { 626 psForOtherCommonId = makePSForOtherCommonId(); 627 } 628 629 // showCurrentRow(rst); 630 631 Object commonId = rst.getObject(RepConstants.shadow_common_id2); 632 Object syncId = rst.getObject(RepConstants.shadow_sync_id1); 633 /* bjt - add .toString() to commonId and syncId to make postgres quote it and thus use the index */ 634 psForOtherCommonId.setObject(1, commonId.toString()); 635 psForOtherCommonId.setObject(2, syncId.toString()); 636 for (int i = 0; i < primaryColumnNames.length; i++) { 637 psForOtherCommonId.setObject(i + 3,rst.getObject("REP_OLD_" +primaryColumnNames[i])); 638 } 639 return psForOtherCommonId.executeQuery(); 640 } 641 642 /** 643 * Handles the case when primary key voilation occurs due to synchronisation. 644 * Records may be rollbacked to their original status as before synchronization 645 * @param conflictingPrimaryKeyValues 646 * @param trForRollback - a tracer holds the information related to the records. 647 * @throws RepException 648 * @throws SQLException 649 */ 650 private void primaryViolation(Object[] conflictingPrimaryKeyValues, Tracer trForRollback) throws RepException, SQLException { 651 Object lastShadowUidObj = getLastUIDFromShadowTable(); 652 if ((lastShadowUidObj instanceof Integer)) { //for firebird 653 lastShadowUid = new Long( ( (Integer) lastShadowUidObj).longValue() + 1); 654 } 655 else if ((lastShadowUidObj instanceof String)) { //for MySQL 656 lastShadowUid = new Long( ( Long.parseLong((String)lastShadowUidObj) + 1)); 657 } 658 else { 659 lastShadowUid = new Long( ( (Long) getLastUIDFromShadowTable()).longValue() +1); 660 } 661 662 Tracer tracer = new Tracer(); 663 // trace the orignal record inserted/updated in main table for which Primary voilation has occured 664 traceOriginalRecord(conflictingPrimaryKeyValues, tracer, lastShadowUid); 665 666 if (tracer.type == null) { 667 return; 668 } 669 670 if (tracer.type.equals(RepConstants.insert_operation)) { // Insert Case 671 672 if (preparedStatementForDelete == null) { 673 preparedStatementForDelete = makePreparedStatementForDeleteOnTable(); 674 } 675 Object[] primaryValues = tracer.primaryKeyValues; 676 for (int i = 0; i < primaryValues.length; i++) { 677 preparedStatementForDelete.setObject(i + 1, primaryValues[i]); 678 } 679 if (isCurrentTableCyclic) { 680 setNullInColumnOfChildTableToUpdateRecord(primaryColumnNames,primaryValues); 681 } 682 // possibility of deleting the record which is to be updated afterwards 683 preparedStatementForDelete.executeUpdate(); 684 } 685 else if (tracer.type.equals(RepConstants.update_operation)) { // Update Case 686 // special handlling if swapping has been done on primary keys and which due to which 687 // we get stuck in recurrsion . 688 boolean conflictingKeyAllreadyPresent = 689 checkForPresenceOfConflictingPkKeyinArray(voilationPkKeys, 690 conflictingPrimaryKeyValues); 691 if (!conflictingKeyAllreadyPresent) { 692 voilationPkKeys.add(conflictingPrimaryKeyValues); 693 } 694 else { 695 if (preparedStatementForDelete == null) { 696 preparedStatementForDelete = makePreparedStatementForDeleteOnTable(); 697 } 698 Object[] primaryValuesForDelete = (Object[]) voilationPkKeys.get( 699 voilationPkKeys.size()); 700 for (int i = 0; i < primaryValuesForDelete.length; i++) { 701 preparedStatementForDelete.setObject(i + 1, primaryValuesForDelete[i]); 702 } 703 // possibility of deleting the record which is to be updated afterwards 704 preparedStatementForDelete.executeUpdate(); 705 Object lastIdForInsert = getLastUIDFromShadowTable(); 706 return; 707 } 708 709 ResultSet newRS = tracer.rs; 710 711 if (updatePreparedStatement == null) { 712 updatePreparedStatement = makeUpdatePreparedStatement(); 713 714 } 715 Object[] primaryValues = tracer.primaryKeyValues; 716 717 // checking for record to be updated / rollbacked if it is the one to be updated originally 718 // i.e after primary voilation has been done 719 boolean flag = checkForConflictingRecord(trForRollback.primaryKeyValues,conflictingPrimaryKeyValues); 720 if (flag) { 721 trForRollback.primaryKeyValues = primaryValues; 722 723 // setting old values for the current row // updating current row to its prev version. 724 } 725 ArrayList[] listArray = setParametersInUpdatePS(conflictingPrimaryKeyValues, newRS); 726 ArrayList list = listArray[0]; 727 ArrayList nullColumnObj = listArray[1]; 728 729 try { 730 updatePreparedStatement.executeUpdate(); 731 } 732 catch (SQLException ex) { 733 /** 734 * Use foriegn key error code for deleting the 735 * conflicting record. 736 */ 737 if (ex.getErrorCode() == 547) { 738 //System.out.println(" primary key voilation :: "); 739 deleteRecord(conflictingPrimaryKeyValues); 740 //System.out.println(" record deleted successfully "); 741 return; 742 } 743 if (!dbHandler.isPrimaryKeyException(ex)) { 744 throw ex; 745 } 746 primaryViolation(primaryValues, trForRollback); 747 int nullCnt = 0; 748 for (int i = 0; i < list.size(); i++) { 749 Object obj = list.get(i); 750 if (obj != null) { 751 updatePreparedStatement.setObject(i + 1, obj); 752 } 753 else { 754 ( (AbstractColumnObject) nullColumnObj.get(nullCnt)).setColumnObject(updatePreparedStatement, xmlElement_NULL, i + 1); 755 nullCnt++; 756 } 757 } 758 //Updating Values to prev values 759 int countUpdatedR = updatePreparedStatement.executeUpdate(); 760 } 761 //we will not update the shadown table entries with remote server name in this case 762 } 763 } 764 765 private ArrayList[] setParametersInUpdatePS(Object[] conflictingPrimaryKeyValues, ResultSet newRS) throws SQLException { 766 ArrayList list = new ArrayList(); 767 ArrayList nullColumnObj = new ArrayList(); 768 int cnt = tableColumnNames.length; 769 int k = 0; 770 for (int i = 0; i < cnt; i++) { 771 if (repTable.isIgnoredColumn(tableColumnNames[i])) { 772 continue; 773 } 774 Object obj = newRS.getObject(tableColumnNames[i]); 775 if (obj == null) { 776 AbstractColumnObject columnObj = ( (AbstractColumnObject)columnObjectTreeMap.get(tableColumnNames[i])); 777 columnObj.setColumnObject(updatePreparedStatement, xmlElement_NULL,k + 1); 778 list.add(obj); 779 nullColumnObj.add(columnObj); 780 k++; 781 continue; 782 } 783 updatePreparedStatement.setObject(k + 1, obj); 784 k++; 785 list.add(obj); 786 } 787 788 for (int j = 0; j < conflictingPrimaryKeyValues.length; j++) { 789 Object obj = conflictingPrimaryKeyValues[j]; 790 updatePreparedStatement.setObject(cnt + j + 1, obj); 791 list.add(obj); 792 } 793 return new ArrayList[] {list, nullColumnObj}; 794 } 795 796 /** 797 * Get actual values from ColumnObjects for corresponding to primary key. 798 * @param primaryKeyValues primary key values passed through XML file. 799 * @return Object[] of primary keys 800 * @throws SQLException 801 */ 802 private Object[] getObjectArrayForPrimaryKey(String[] primaryKeyValues) throws 803 SQLException { 804 Object[] pkValues = new Object[primaryKeyColumnsObject.length]; 805 for (int i = 0; i < primaryKeyColumnsObject.length; i++) { 806 pkValues[i] = primaryKeyColumnsObject[i].getObject(primaryKeyValues[i]); 807 } 808 return pkValues; 809 } 810 811 /** 812 * Get actual values from ColumnObjects for corresponding to whole row passed in XML file. 813 * @param columnValues 814 * @param columnObj 815 * @return 816 * @throws SQLException 817 */ 818 private Object[] getObjectArrayForTable(String[] columnValues,AbstractColumnObject[] columnObj) throws SQLException { 819 Object[] values = new Object[columnValues.length]; 820 ArrayList nullColumnObj = new ArrayList(); 821 822 for (int i = 0; i < columnValues.length; i++) { 823 if (repTable.isIgnoredColumn(tableColumnNames[i])) { 824 continue; 825 } 826 AbstractColumnObject colObj = columnObj[i]; 827 values[i] = colObj.getObject(columnValues[i]); 828 if (values[i] == null) { 829 nullColumnObj.add(colObj); 830 } 831 } 832 return new Object[] {values, nullColumnObj}; 833 } 834 835 /** 836 * tarce the record present in shadow table for the primary key passed and having syncId less than UID passed. 837 * @param primaryKeyValues 838 * @param tracer 839 * @param Uid 840 * @return 841 * @throws RepException, SQLException 842 */ 843 private boolean traceOriginalRecord(Object[] primaryKeyValues, Tracer tracer,Object Uid) throws RepException, SQLException { 844 if (preparedStatementForLastRecord == null) { 845 preparedStatementForLastRecord = makePreparedStatementForLastRecord(); 846 } 847 preparedStatementForLastRecord.setObject(1, Uid); 848 for (int i = 0; i < primaryKeyValues.length; i++) { 849 preparedStatementForLastRecord.setObject(i + 2, primaryKeyValues[i]); 850 } 851 ResultSet lastRow = preparedStatementForLastRecord.executeQuery(); 852 lastRow = preparedStatementForLastRecord.executeQuery(); 853 if (!lastRow.next()) { 854 lastRow.close(); 855 return false; 856 } 857 858 // finding operation performed for current record 859 String operation = lastRow.getString(RepConstants.shadow_operation3); 860 if (operation.equals(RepConstants.insert_operation)) { 861 tracer.type = RepConstants.insert_operation; 862 tracer.rs = lastRow; 863 tracer.primaryKeyValues = primaryKeyValues; 864 return true; 865 } 866 else { 867 // Operation should be UPDATE 868 Object commonId = lastRow.getObject(RepConstants.shadow_common_id2); 869 if (preparedStatementForCommonId == null) { 870 preparedStatementForCommonId = makePreparedStatementForCommonId_WithBeforeUpdate_Status(); 871 } 872 preparedStatementForCommonId.setObject(1, commonId); 873 for (int i = 0; i < primaryColumnNames.length; i++) { 874 preparedStatementForCommonId.setObject(i + 2,lastRow.getObject("REP_OLD_" +primaryColumnNames[i])); 875 } 876 ResultSet rs = preparedStatementForCommonId.executeQuery(); 877 878 if (!rs.next()) { // other reocrd for common id and status before update not found 'B' 879 rs.close(); 880 throw new RepException("REP051", new Object[] {commonId, shadowTable}); 881 } 882 883 Object[] primaryColValues = new Object[primaryColumnNames.length]; 884 for (int j = 0; j < primaryColumnNames.length; j++) { 885 primaryColValues[j] = rs.getObject(primaryColumnNames[j]); 886 // System.out.println(" primaryColValues[j] :: " + primaryColValues[j]); 887 } 888 Object UID = rs.getObject(RepConstants.shadow_sync_id1); 889 // finding prev. record 890 boolean gotRow = traceOriginalRecord(primaryColValues, tracer, UID); 891 if (gotRow == false) { 892 tracer.rs = rs; 893 tracer.type = RepConstants.update_operation; 894 tracer.primaryKeyValues = primaryColValues; 895 } 896 else { 897 rs.close(); 898 } 899 return true; 900 } 901 // throw new Exception("ERROR ERROR < NOT POSSIBLE"); 902 } 903 904 /** 905 * fetches the last records from the shadpw tbale correspondinf the the 906 * primary key passed i.e the original status of the record after 907 * updataion/Insertion/Deletion 908 * @param primaryColValues 909 * @param UID 910 * @param tracer 911 * @return 912 * @throws RepException 913 * @throws SQLException 914 */ 915 private boolean getLastRecord(Object[] primaryColValues, Object UID,Tracer tracer) throws RepException, SQLException { 916 int noOfPrimaryColumns = primaryColValues.length; 917 for (int i = 0; i < noOfPrimaryColumns; i++) { 918 psForInitialRecordInShadowTable.setObject(i + 1, primaryColValues[i]); 919 } 920 psForInitialRecordInShadowTable.setObject(noOfPrimaryColumns + 1, UID); 921 psForInitialRecordInShadowTable.setObject(noOfPrimaryColumns + 2,remoteServerName); 922 ResultSet rs = psForInitialRecordInShadowTable.executeQuery(); 923 924 if (!rs.next()) { 925 rs.close(); 926 tracer.recordFound = false; 927 return false; 928 } 929 else { 930 String operation = rs.getString(RepConstants.shadow_operation3); 931 if (operation.equals(RepConstants.delete_operation)) { // Delete Operation 932 tracer.recordFound = true; 933 tracer.type = RepConstants.delete_operation; 934 return true; 935 } 936 else { 937 tracer.setOldRow(rs); 938 ResultSet resultSet = getOtherCommonIdResultSet(rs); 939 boolean updatedREcord = resultSet.next(); 940 if (!updatedREcord) { // other common record not found in shadow table, may be insert case 941 throw new RepException("REP051",new Object[] {rs.getObject(RepConstants.shadow_common_id2), shadowTable}); 942 } 943 primaryColValues = new Object[noOfPrimaryColumns]; 944 for (int i = 0; i < noOfPrimaryColumns; i++) { 945 primaryColValues[i] = resultSet.getObject(primaryColumnNames[i]); 946 } 947 Object currentUId = resultSet.getObject(RepConstants.shadow_sync_id1); 948 949 950 boolean nextFound = getLastRecord(primaryColValues, currentUId, tracer); 951 if (nextFound == false) { 952 tracer.rs = resultSet; 953 tracer.recordFound = true; 954 tracer.type = RepConstants.update_operation; 955 tracer.primaryKeyValues = primaryColValues; 956 } 957 else { 958 resultSet.close(); 959 960 } 961 return true; 962 } 963 } 964 } 965 966 private PreparedStatement makePSForInitialRecordInShadowTable() throws SQLException { 967 StringBuffer query = new StringBuffer(); 968 query.append("SELECT * FROM "); 969 query.append(shadowTable); 970 query.append(" WHERE "); 971 for (int i = 0; i < primaryColumnNames.length; i++) { 972 if (i != 0) { 973 query.append(" AND "); 974 } 975 query.append(primaryColumnNames[i]); 976 query.append("= ? "); 977 } 978 query.append(" AND "); 979 query.append(RepConstants.shadow_sync_id1); 980 query.append(" > ? "); // coincedered Id 981 query.append(" AND "); 982 query.append(RepConstants.shadow_serverName_n); 983 query.append(" != ? ORDER BY ").append(RepConstants.shadow_sync_id1); // remote servername 984 return connection.prepareStatement(query.toString()); 985 } 986 987 private PreparedStatement makePSForActualUpdateInTable() throws SQLException { 988 StringBuffer query = new StringBuffer(); 989 query.append("UPDATE "); 990 query.append(tableName); 991 query.append(" SET "); 992 for (int i = 0; i < changedColumnNames.length; i++) { 993 if (repTable.isIgnoredColumn(changedColumnNames[i])) { 994 continue; 995 } 996 query.append(" , " + changedColumnNames[i]); 997 query.append(" = ? "); 998 } 999 int indexOfFirstComma = query.indexOf(","); 1000 if (indexOfFirstComma != -1) { 1001 query.deleteCharAt(indexOfFirstComma); 1002 } 1003 1004 query.append("WHERE "); 1005 for (int i = 0; i < primaryColumnNames.length; i++) { 1006 if (i != 0) { 1007 query.append(" AND "); 1008 } 1009 query.append(primaryColumnNames[i]); 1010 query.append(" = ? "); 1011 } 1012 return connection.prepareStatement(query.toString()); 1013 } 1014 1015 private PreparedStatement makePreparedStatementForLastRecord() throws 1016 SQLException { 1017 StringBuffer query = new StringBuffer(); 1018 query.append(" SELECT * FROM "); 1019 query.append(shadowTable); 1020 query.append(" WHERE "); 1021 query.append(RepConstants.shadow_sync_id1); 1022 // and not >= because this will include the last record in traceOriginal Record , which will give wrong result 1023 query.append(" > "); 1024 //query.append(conisderedId); /* bjt - change to lastSyncId */ 1025 if (Integer.parseInt(conisderedId.toString()) > Integer.parseInt(lastSyncId.toString())) { 1026 query.append(conisderedId); 1027 } else { 1028 query.append(lastSyncId); 1029 } 1030 query.append(" AND "); 1031 query.append(RepConstants.shadow_sync_id1); 1032 query.append(" < ? "); 1033 for (int i = 0; i < primaryColumnNames.length; i++) { 1034 query.append(" AND "); 1035 query.append(primaryColumnNames[i]); 1036 query.append(" = ? "); 1037 } 1038 query.append(" ORDER BY "); 1039 query.append(RepConstants.shadow_sync_id1); 1040 query.append(" DESC "); 1041 return connection.prepareStatement(query.toString()); 1042 } 1043 1044 private PreparedStatement makePreparedStatementForCommonId_WithBeforeUpdate_Status() throws SQLException { 1045 StringBuffer query = new StringBuffer(); 1046 query.append(" SELECT * FROM "); 1047 query.append(shadowTable); 1048 query.append(" WHERE "); 1049 query.append(RepConstants.shadow_sync_id1); 1050 query.append(" >= "); 1051 //query.append(conisderedId); /* bjt - change to lastSyncId */ 1052 if (Integer.parseInt(conisderedId.toString()) > Integer.parseInt(lastSyncId.toString())) { 1053 query.append(conisderedId); 1054 } else { 1055 query.append(lastSyncId); 1056 } 1057 query.append(" AND "); 1058 query.append(RepConstants.shadow_common_id2); 1059 query.append(" = ? "); 1060 1061 // for matching oldPrimary keys 1062 for (int i = 0; i < primaryColumnNames.length; i++) { 1063 query.append(" AND "); 1064 query.append("REP_OLD_" + primaryColumnNames[i]); 1065 query.append(" = "); 1066 query.append(" ? "); 1067 } 1068 1069 query.append(" AND "); 1070 query.append(RepConstants.shadow_status4); 1071 query.append(" = '"); 1072 query.append(RepConstants.beforeUpdate); 1073 query.append("'"); 1074 return connection.prepareStatement(query.toString()); 1075 } 1076 1077 private PreparedStatement makePreparedStatementForDeleteOnTable() throws 1078 SQLException { 1079 StringBuffer query = new StringBuffer(); 1080 query.append(" DELETE FROM "); 1081 query.append(tableName); 1082 query.append(" WHERE "); 1083 for (int i = 0; i < primaryColumnNames.length; i++) { 1084 if (i != 0) { 1085 query.append(" AND "); 1086 } 1087 query.append(primaryColumnNames[i]); 1088 query.append("= ?"); 1089 } 1090 return connection.prepareStatement(query.toString()); 1091 } 1092 1093 private PreparedStatement makeUpdatePreparedStatement() throws SQLException { 1094 StringBuffer query = new StringBuffer(); 1095 query.append("UPDATE "); 1096 query.append(tableName); 1097 query.append(" SET "); 1098 1099 for (int i = 0; i < tableColumnNames.length; i++) { 1100 if (repTable.isIgnoredColumn(tableColumnNames[i])) { 1101 continue; 1102 } 1103 query.append(" , " + tableColumnNames[i]); 1104 query.append("= ?"); 1105 } 1106 int indexOfFirstComma = query.indexOf(","); 1107 if (indexOfFirstComma != -1) { 1108 query.deleteCharAt(indexOfFirstComma); 1109 } 1110 query.append(" WHERE "); 1111 for (int i = 0; i < primaryColumnNames.length; i++) { 1112 if (i != 0) { 1113 query.append(" AND "); 1114 } 1115 query.append(primaryColumnNames[i]); 1116 query.append("= ?"); 1117 } 1118 //System.out.println("PK violation makeUpdatePreparedStatement line 1063:::" +query.toString()); 1119 return connection.prepareStatement(query.toString()); 1120 } 1121 1122 private PreparedStatement makePSForOtherCommonId() throws SQLException { 1123 StringBuffer query = new StringBuffer(); 1124 query.append("SELECT * FROM "); 1125 query.append(shadowTable); 1126 query.append(" WHERE "); 1127 query.append(RepConstants.shadow_common_id2); 1128 query.append(" = ? "); 1129 query.append(" and "); 1130 query.append(RepConstants.shadow_sync_id1); 1131 query.append(" != ? "); 1132 1133 // for matching oldPrimary keys 1134 for (int i = 0; i < primaryColumnNames.length; i++) { 1135 query.append(" and "); 1136 query.append("REP_OLD_" + primaryColumnNames[i]); 1137 query.append(" = "); 1138 query.append(" ? "); 1139 } 1140 return connection.prepareStatement(query.toString()); 1141 } 1142 1143 /** 1144 * returns last synchronization id from shadopw table. 1145 * @return 1146 * @throws SQLException 1147 */ 1148 private Object getLastUIDFromShadowTable() throws SQLException { 1149 StringBuffer query = new StringBuffer(); 1150 ResultSet rs = null; 1151 try { 1152 query.append(" SELECT ").append(RepConstants.shadow_sync_id1). 1153 append(" FROM ").append(shadowTable). 1154 append(" order by ").append(RepConstants.shadow_sync_id1). 1155 append(" desc limit 1"); 1156 rs = connection.createStatement().executeQuery(query.toString()); 1157 boolean flag = rs.next(); 1158 Object lastId = new Long(0); 1159 if (flag) lastId = rs.getObject(1); 1160 //System.out.println("Operation update 1153 getLastUIDFromShadowTable lastId CLASS : "+lastId); 1161 if (lastId instanceof BigDecimal) { 1162 lastId = new Long( ( (BigDecimal) lastId).longValue()); 1163 } 1164 else if (lastId instanceof Double) { 1165 lastId = new Long( ( (Double) lastId).longValue()); 1166 } 1167 else if (lastId instanceof Long) { 1168 lastId = new Long( ( (Long) lastId).longValue()); 1169 } 1170 else if (lastId instanceof Integer) { 1171 lastId = new Long( ( (Integer) lastId).longValue()); 1172 } 1173 else if (lastId instanceof String) { 1174 lastId = new Long( ( Long.parseLong((String)lastId))); 1175 } 1176 1177 return flag ? (lastId == null ? new Long(0) : lastId) : new Long(0); 1178 } 1179 finally { 1180 Statement st = rs.getStatement(); 1181 rs.close(); 1182 st.close(); 1183 } 1184 } 1185 1186 private PreparedStatement makePSForUpdateInShadowTableForRemoteServer() throws SQLException { 1187 StringBuffer query = new StringBuffer(); 1188 query.append(" UPDATE ").append(shadowTable).append(" SET ").append( 1189 RepConstants.shadow_serverName_n) 1190 .append(" = ? ").append(" WHERE ").append(RepConstants.shadow_sync_id1). 1191 append("> ?"); 1192 for (int i = 0; i < primaryColumnNames.length; i++) { 1193 query.append(" AND "); 1194 query.append(primaryColumnNames[i]); 1195 query.append(" = ?"); 1196 } 1197 return connection.prepareStatement(query.toString()); 1198 } 1199 1200 /** 1201 * used for debugging purpose to show a resultSet 1202 * @param rs 1203 * @throws SQLException 1204 */ 1205 /* private void showCurrentRow(ResultSet rs) throws SQLException { 1206 ResultSetMetaData rsmd = rs.getMetaData(); 1207 int count = rsmd.getColumnCount(); 1208 System.out.print("[ "); 1209 for (int i = 1; i <= count; i++) { 1210 if (i != 1) 1211 System.out.print(" , "); 1212 System.out.print(rs.getObject(i)); 1213 } 1214 System.out.println(" ] "); 1215 System.out.println(""); 1216 } 1217 */ 1218 /** 1219 * searches for the conflicting Key in search key values. 1220 * @param searchKeyValues 1221 * @param conflictingPk 1222 * @return 1223 */ 1224 private boolean checkForConflictingRecord(Object[] searchKeyValues, Object[] conflictingPk) { 1225 try { 1226 for (int i = 0; i < searchKeyValues.length; i++) { 1227 if (searchKeyValues[i].equals(conflictingPk[i])) { 1228 continue; 1229 } 1230 else { 1231 return false; 1232 } 1233 } 1234 return true; 1235 } 1236 catch (NullPointerException ex) { 1237 return false; 1238 } 1239 } 1240 1241 private PreparedStatement makePsForUpdateChangedColumns_ExceptPk(ArrayList exceptPk_ChangedColumnNames) throws SQLException { 1242 StringBuffer query = new StringBuffer(); 1243 query.append("UPDATE "); 1244 query.append(tableName); 1245 query.append(" SET "); 1246 for (int i = 0; i < exceptPk_ChangedColumnNames.size(); i++) { 1247 if (i != 0) { 1248 query.append(" , "); 1249 } 1250 query.append(exceptPk_ChangedColumnNames.get(i)); 1251 query.append(" = ? "); 1252 } 1253 query.append(" WHERE "); 1254 for (int i = 0; i < primaryColumnNames.length; i++) { 1255 if (i != 0) { 1256 query.append(" AND "); 1257 } 1258 query.append(primaryColumnNames[i]); 1259 query.append(" = ? "); 1260 } 1261 return connection.prepareStatement(query.toString()); 1262 } 1263 1264 private void updateChangedColumnsAtLocalExcept_Pk(Object[] 1265 changedColumnObject, int noOfPrimaryCol, Object[] primaryValueObjects) throws SQLException { 1266 if (changedColumnNames.length > 0) { 1267 ArrayList exceptPk_ChangedColumnNames = new ArrayList(); 1268 ArrayList exceptPk_ChangedColumnValues = new ArrayList(); 1269 boolean isPk = false; 1270 // getting changes column names and values except for primary key 1271 for (int i = 0; i < changedColumnNames.length; i++) { 1272 for (int j = 0; j < primaryColumnNames.length; j++) { 1273 isPk = false; 1274 if (changedColumnNames[i].equalsIgnoreCase(primaryColumnNames[j])) { 1275 isPk = true; 1276 continue; 1277 } 1278 } 1279 if (!isPk) { 1280 exceptPk_ChangedColumnNames.add(changedColumnNames[i]); 1281 exceptPk_ChangedColumnValues.add(changedColumnObject[i]); 1282 } 1283 } 1284 // making prepared statement for updating values at except for primary key 1285 if (exceptPk_ChangedColumnNames.size() > 0) { 1286 PreparedStatement ps = makePsForUpdateChangedColumns_ExceptPk( 1287 exceptPk_ChangedColumnNames); 1288 for (int i = 0; i < exceptPk_ChangedColumnValues.size(); i++) { 1289 ps.setObject(i + 1, exceptPk_ChangedColumnValues.get(i)); 1290 } 1291 for (int i = 0; i < noOfPrimaryCol; i++) { 1292 ps.setObject(exceptPk_ChangedColumnValues.size() + i + 1, primaryValueObjects[i]); 1293 } 1294 ps.executeUpdate(); 1295 } 1296 } 1297 } 1298 1299 private boolean checkForPresenceOfConflictingPkKeyinArray(ArrayList 1300 voilationPkKeys, Object[] pkValues) { 1301 for (int i = 0; i < voilationPkKeys.size(); i++) { 1302 Object[] values = (Object[]) voilationPkKeys.get(i); 1303 boolean flag = true; 1304 for (int j = 0; j < values.length; j++) { 1305 if (!values[j].equals(pkValues[j])) { 1306 flag = false; 1307 break; 1308 } 1309 } 1310 if (flag) { 1311 return true; 1312 } 1313 } 1314 return false; 1315 } 1316 1317 /* 1318 * used for debugging. shows values stored in a resultSet. 1319 * @param rs 1320 * @throws SQLException 1321 */ 1322 /* public static void showResultSet(ResultSet rs) throws SQLException { 1323 ResultSetMetaData metaData = rs.getMetaData(); 1324 int columnCount = metaData.getColumnCount(); 1325 Object[] displayColumn = new Object[columnCount]; 1326 for (int i = 1; i <= columnCount; i++) 1327 displayColumn[i - 1] = metaData.getColumnName(i); 1328 System.out.println(Arrays.asList(displayColumn)); 1329 while (rs.next()) { 1330 Object[] columnValues = new Object[columnCount]; 1331 for (int i = 1; i <= columnCount; i++) 1332 columnValues[i - 1] = rs.getObject(i); 1333 System.out.println(Arrays.asList(columnValues)); 1334 } 1335 }*/ 1336 1337 public void closeAllStatments() { 1338 if (psForInitialRecordInShadowTable != null) { 1339 try { 1340 psForInitialRecordInShadowTable.close(); 1341 } 1342 catch (SQLException ex) { 1343 } 1344 } 1345 if (psForActualUpdateInTable != null) { 1346 try { 1347 psForActualUpdateInTable.close(); 1348 } 1349 catch (SQLException ex1) { 1350 } 1351 } 1352 if (preparedStatementForLastRecord != null) { 1353 try { 1354 preparedStatementForLastRecord.close(); 1355 } 1356 catch (SQLException ex2) { 1357 } 1358 } 1359 if (preparedStatementForCommonId != null) { 1360 try { 1361 preparedStatementForCommonId.close(); 1362 } 1363 catch (SQLException ex3) { 1364 } 1365 } 1366 1367 if (preparedStatementForDelete != null) { 1368 try { 1369 preparedStatementForDelete.close(); 1370 } 1371 catch (SQLException ex4) { 1372 } 1373 } 1374 if (updatePreparedStatement != null) { 1375 try { 1376 updatePreparedStatement.close(); 1377 } 1378 catch (SQLException ex5) { 1379 } 1380 } 1381 if (psForOtherCommonId != null) { 1382 try { 1383 psForOtherCommonId.close(); 1384 } 1385 catch (SQLException ex6) { 1386 } 1387 } 1388 1389 if (psForInsertIntoActualTable != null) { 1390 1391 try { 1392 psForInsertIntoActualTable.close(); 1393 } 1394 catch (SQLException ex7) { 1395 } 1396 } 1397 if (psForUpdateInShadowTableForRemoteServer != null) { 1398 try { 1399 psForUpdateInShadowTableForRemoteServer.close(); 1400 } 1401 catch (SQLException ex8) { 1402 } 1403 } 1404 } 1405 1406 /** 1407 * Make a update query to update all the columns that refering 1408 * to other columns and and set null during first pass.If referencing 1409 * columns are not found then blank string return by the method. 1410 * @param changesColumns String[] 1411 * @param primaryColumns String[] 1412 * @return String 1413 */ 1414 private String makeQueryToUpdateTheRecordInSecodPass(String[] changesColumns,String[] primaryColumns) { 1415 StringBuffer updateQuerySecondPass = new StringBuffer(); 1416 updateQuerySecondPass.append("UPDATE ").append(tableName).append(" SET "); 1417 int size = changesColumns.length; 1418 boolean flag = false; 1419 for (int i = 0; i < size; i++) { 1420 if (repTable.isForeignKeyColumn(changedColumnNames[i])) { 1421 if (flag) 1422 updateQuerySecondPass.append(","); 1423 flag = true; 1424 updateQuerySecondPass.append(changedColumnNames[i]).append("= ? "); 1425 } 1426 } 1427 updateQuerySecondPass.append(" WHERE "); 1428 for (int i = 0; i < primaryColumns.length; i++) { 1429 if (i != 0) { 1430 updateQuerySecondPass.append(" and "); 1431 } 1432 updateQuerySecondPass.append(primaryColumns[i]).append(" = ?"); 1433 } 1434 log.debug(updateQuerySecondPass.toString()); 1435 return flag ? updateQuerySecondPass.toString() : ""; 1436 } 1437 1438 /** 1439 * Set null in all column of child table that refering the 1440 * column of parent table for updated the record of parent 1441 * table. 1442 * @param parentPkCols String[] 1443 * @param parentPkColValue0 Object[] 1444 * @throws RepException 1445 * @throws SQLException 1446 */ 1447 private void setNullInColumnOfChildTableToUpdateRecord(String[] parentPkCols, 1448 Object[] parentPkColValue0) throws RepException, SQLException { 1449 Statement stmt = null; 1450 String[] childFkColumns = null, parentReferedCols = null, parentPkColValue = null; 1451 Object[] object = null; 1452 Object obj = null; 1453 ArrayList childTableList = mdi.getChildTables(tableName); 1454 String childTableName = null; 1455 parentPkColValue = new String[parentPkColValue0.length]; 1456 for (int j = 0; j < parentPkColValue0.length; j++) { 1457 obj = parentPkColValue0[j]; 1458 parentPkColValue[j] = obj.toString(); 1459 } 1460 for (int i = 0; i < childTableList.size(); i++) { 1461 childTableName = (String) childTableList.get(i); 1462 object = mdi.getImportedColsOfChildTable(tableName, childTableName); 1463 childFkColumns = (String[]) object[0]; 1464 parentReferedCols = (String[]) object[1]; 1465 updatePreparedStatementSetNull = makeupdateQueryToSetNull(childTableName, 1466 childFkColumns, parentReferedCols, parentPkCols, parentPkColValue); 1467 Object lastSynId = getLastSyncId(dbHandler.getShadowTableName(childTableName)); /* bjt - get rid of this */ 1468 updatePreparedStatementSetNull.execute(); 1469 String updateRemoteServerNameQuery = 1470 updateRemoteServerNameWhenSettingNullInChildTable(childTableName, 1471 childFkColumns, lastSynId, parentReferedCols, parentPkCols,parentPkColValue); 1472 stmt = connection.createStatement(); 1473 int howMayServerNameupdated = stmt.executeUpdate(updateRemoteServerNameQuery); 1474 if (stmt != null) { 1475 stmt.close(); 1476 } 1477 } 1478 } 1479 1480 private PreparedStatement makeupdateQueryToSetNull(String tableName, 1481 String columns[], String[] parentReferedColumns, String[] parentPkCols, String[] parentPkColValue) throws SQLException { 1482 StringBuffer sb = new StringBuffer(); 1483 sb.append("UPDATE ") 1484 .append(tableName) 1485 .append(" SET "); 1486 for (int i = 0; i < columns.length; i++) { 1487 if (i != 0) { 1488 sb.append(" , "); 1489 } 1490 sb.append(columns[i] + " = null") 1491 .append(" where " + columns[i] + " = " + 1492 getParentReferedColValue(parentPkCols, parentPkColValue,parentReferedColumns[i])); 1493 } 1494 return connection.prepareStatement(sb.toString()); 1495 } 1496 1497 /** 1498 * Get value of a column that is refer by other table 1499 * for creating a update query for setting null in child 1500 * tables. 1501 * @param primaryColsName String[] 1502 * @param primaryColsValues String[] 1503 * @param referdColName String 1504 * @throws SQLException 1505 * @return Object 1506 */ 1507 private Object getParentReferedColValue(String[] primaryColsName, String[] primaryColsValues, String referdColName) throws SQLException { 1508 Statement stmt = connection.createStatement(); 1509 StringBuffer sb = new StringBuffer(); 1510 sb.append("SELECT ").append(referdColName) 1511 .append(" FROM " + tableName) 1512 .append(" where "); 1513 for (int i = 0; i < primaryColsName.length; i++) { 1514 if (i != 0) { 1515 sb.append(" , "); 1516 } 1517 sb.append(primaryColsName[i] + " = " + primaryColsValues[i]); 1518 } 1519 ResultSet rs = stmt.executeQuery(sb.toString()); 1520 rs.next(); 1521 return rs.getObject(1); 1522 } 1523 1524 private String updateRemoteServerNameWhenSettingNullInChildTable(String 1525 childTableName, String[] childColumnName, Object lastSyncID, 1526 String[] parentReferedColumns, String[] parentPkCols, 1527 String[] parentPkColValue) throws SQLException { 1528 StringBuffer sb = new StringBuffer(); 1529 String tableName = dbHandler.getShadowTableName(childTableName); 1530 sb.append(" UPDATE " + tableName) 1531 .append(" SET " + RepConstants.shadow_serverName_n + " = '") 1532 .append(remoteServerName + "'") 1533 .append(" WHERE ") 1534 .append(RepConstants.shadow_common_id2) 1535 .append(" = ") 1536 .append("( ") 1537 .append("SELECT ").append(RepConstants.shadow_common_id2).append( 1538 " FROM " + tableName) 1539 .append(" WHERE ") 1540 .append(RepConstants.shadow_sync_id1) 1541 .append(" > ") 1542 .append("" + lastSyncID + " AND ") 1543 .append(RepConstants.shadow_status4) 1544 .append(" = 'B' AND "); 1545 for (int i = 0; i < childColumnName.length; i++) { 1546 if (i != 0) { 1547 sb.append(" , "); 1548 } 1549 sb.append(childColumnName[i] + " = " + 1550 getParentReferedColValue(parentPkCols, parentPkColValue,parentReferedColumns[i])); 1551 } 1552 sb.append(") "); 1553 return sb.toString(); 1554 } 1555 1556 /** 1557 * Get last Synchronization ID from shadow table.It 1558 * is used to used to get the original record. 1559 * @param shadowTableName String 1560 * @throws SQLException 1561 * @return Object 1562 */ 1563 private Object getLastSyncId(String shadowTableName) throws SQLException { 1564 Statement statement = null; 1565 ResultSet rs = null; 1566 boolean flag = false; 1567 Object lastId = null; 1568 try { 1569 StringBuffer query = new StringBuffer(); 1570 query.append("SELECT ").append(RepConstants.shadow_sync_id1). 1571 append(" FROM ").append(shadowTableName). 1572 append(" order by ").append(RepConstants.shadow_sync_id1). 1573 append(" desc limit 1"); 1574 statement = connection.createStatement(); 1575 rs = statement.executeQuery(query.toString()); 1576 flag = rs.next(); 1577 lastId = rs.getObject(1); 1578 } 1579 finally { 1580 if (rs != null) 1581 rs.close(); 1582 if (statement != null) 1583 statement.close(); 1584 } 1585 return flag ? (lastId == null ? new Long(0) : lastId) : new Long(0); 1586 } 1587 1588 /** 1589 * Delete record in case of cyclic table. First we try to rollback the record upto 1590 * original record.After that updated statement is executed.During execution of 1591 * statement if primary key voilation occurs then record is deleted to remove 1592 * the conflict. 1593 * @param primaryValues Object[] 1594 * @throws SQLException 1595 */ 1596 private void deleteRecord(Object[] primaryValues) throws SQLException, RepException { 1597 if (preparedStatementForDelete == null) { 1598 preparedStatementForDelete = makePreparedStatementForDeleteOnTable(); 1599 } 1600 for (int i = 0; i < primaryValues.length; i++) { 1601 preparedStatementForDelete.setObject(i + 1, primaryValues[i]); 1602 } 1603 // possibility of deleting the record which is to be updated afterwards 1604 setNullInColumnOfChildTableToUpdateRecord(primaryColumnNames, primaryValues); 1605 preparedStatementForDelete.executeUpdate(); 1606 } 1607 1608 }

