1 /***************************************************************************
2 * Copyright (C) 2007 by www.databasecache.com *
3 * Contact: praba_tuty@databasecache.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 ***************************************************************************/
18 #include <SqlFactory.h>
19 #include <SqlOdbcConnection.h>
20 #include <SqlOdbcStatement.h>
24 printf("Usage: cacheverify [-U username] [-P passwd] -t tablename [-p] [-f]\n");
25 printf(" username -> username to connect with csql.\n");
26 printf(" passwd -> password for the above username to connect with csql.\n");
27 printf(" tablename -> cached table name in csql from target db.\n");
28 printf(" p -> verification at primary key field level\n");
29 printf(" f -> verification at record level\n");
30 printf(" ? -> help\n");
34 typedef struct PrimaryKeyField
{
41 typedef struct FldVal
{
54 // functions to sort the list for STL list
55 bool cmp(const PrimKeyFldVal
*a
, const PrimKeyFldVal
*b
)
57 return a
->val
< b
->val
;
60 bool cmpRec(const Record
*a
, const Record
*b
)
62 return a
->val
< b
->val
;
65 DbRetVal
verifyCount(const char *tblName
, long numTuples
)
68 AbsSqlConnection
*con
= SqlFactory::createConnection(CSqlAdapter
);
69 DbRetVal rv
= con
->connect("root", "manager");
70 if (rv
!= OK
) { delete con
; return ErrSysInit
; }
71 AbsSqlStatement
*stmt
= SqlFactory::createStatement(CSqlAdapter
);
72 stmt
->setConnection(con
);
75 sprintf(statement
, "select count(*) from %s;", tblName
);
76 rv
= con
->beginTrans();
77 rv
= stmt
->prepare(statement
);
79 delete stmt
; delete con
;
80 printf("Prepare failed\n");
83 stmt
->bindField(1, &count
);
84 rv
= stmt
->execute(rows
);
86 delete stmt
; delete con
;
87 printf("Execute failed\n");
90 if (stmt
->fetch()== NULL
) {
91 delete stmt
; delete con
;
92 printf("Fetch failed\n");
93 return ErrSysInternal
;
97 printf("\nNumber of Records:\n");
98 printf("-------------------+-------------------+-------------------+\n");
99 printf(" Data | In CSQL | In TargetDB |\n");
100 printf("-------------------+-------------------+-------------------+\n");
101 printf(" No. Of Records | %-6ld | %-6d |\n", numTuples
, count
);
102 printf("-------------------+-------------------+-------------------+\n");
103 delete stmt
; delete con
;
107 DbRetVal
verifyMismatchingRecords(const char *tblName
, int option
)
110 DbRetVal rv
= conn
.open("root", "manager");
111 if (rv
!= OK
) return ErrSysInit
;
112 DatabaseManager
*dbMgr
= (DatabaseManager
*) conn
.getDatabaseManager();
115 printf("Auth failed\n");
118 Table
*table
= dbMgr
->openTable(tblName
);
121 printf("Table \'%s\' does not exist", tblName
);
126 AbsSqlConnection
*trgtDbCon
= SqlFactory::createConnection(CSqlAdapter
);
127 rv
= trgtDbCon
->connect("root", "manager");
129 dbMgr
->closeTable(table
); conn
.close();
130 delete trgtDbCon
; return ErrSysInit
;
132 AbsSqlStatement
*trgtDbStmt
= SqlFactory::createStatement(CSqlAdapter
);
133 trgtDbStmt
->setConnection(trgtDbCon
);
135 char fieldName
[IDENTIFIER_LENGTH
];
137 SqlOdbcStatement
*ostmt
= (SqlOdbcStatement
*) trgtDbStmt
;
138 ostmt
->getPrimaryKeyFieldName((char*)tblName
, fieldName
);
139 if (fieldName
[0] == '\0') {
140 dbMgr
->closeTable(table
); conn
.close();
141 delete trgtDbStmt
; delete trgtDbCon
;
142 printf("Primary key does not exist on table %s\n", tblName
);
145 printf("\nPrimary key field name is \'%s\'\n", fieldName
);
146 //FieldInfo *fldInfo = new FieldInfo();
147 //table->getFieldInfo(fieldName, fldInfo);
148 //if(! fldInfo->isPrimary) { printError(ErrBadArg, "\'%s\' is not a primary key field", fldName); return ErrBadArg; }
151 //List for primary key field values present in csql server
153 table
->bindFld(fieldName
, &csqlVal
);
154 conn
.startTransaction();
155 table
->setCondition(NULL
);
156 rv
= table
->execute();
158 dbMgr
->closeTable(table
); conn
.close();
159 delete trgtDbStmt
; delete trgtDbCon
;
160 printf("Execute failed\n");
164 void* tuple
= table
->fetch(rv
);
165 if (tuple
== NULL
) break;
166 PrimKeyFldVal
*pkFldVal
= new PrimKeyFldVal();
167 pkFldVal
->val
= csqlVal
;
168 pkFldVal
->inCsql
= true;
169 pkFldVal
->inTrgtDb
= true;
170 valListInCsql
.append(pkFldVal
);
177 // List for primary key field values present in target DB
178 List valListInTrgtDb
;
179 sprintf(statement
, "select %s from %s;", fieldName
, tblName
);
180 rv
= trgtDbCon
->beginTrans();
181 rv
= trgtDbStmt
->prepare(statement
);
183 dbMgr
->closeTable(table
); conn
.close();
184 delete trgtDbStmt
; delete trgtDbCon
;
185 printf("Prepare failed\n");
188 trgtDbStmt
->bindField(1, &trgtDbVal
);
189 rv
= trgtDbStmt
->execute(rows
);
191 dbMgr
->closeTable(table
); conn
.close();
192 delete trgtDbStmt
; delete trgtDbCon
;
193 printf("Execute failed\n");
196 while (trgtDbStmt
->fetch() != NULL
) {
197 PrimKeyFldVal
*pkFldVal
= new PrimKeyFldVal();
198 pkFldVal
->val
= trgtDbVal
;
199 pkFldVal
->inCsql
= true;
200 pkFldVal
->inTrgtDb
= true;
201 valListInTrgtDb
.append(pkFldVal
);
206 // List for primary key field values present in either of the databases
209 // List for primary key field values present in both the databases
210 List sameInBothDbList
;
211 ListIterator csqlValIter
= valListInCsql
.getIterator();
212 ListIterator trgtDbValIter
= valListInTrgtDb
.getIterator();
213 PrimKeyFldVal
*csqlelem
, *trgtdbelem
;
214 while( (csqlelem
= (PrimKeyFldVal
*) csqlValIter
.nextElement()) != NULL
) {
215 while ( (trgtdbelem
= (PrimKeyFldVal
*) trgtDbValIter
.nextElement()) != NULL
) {
216 if (csqlelem
->val
== trgtdbelem
->val
) {
217 PrimKeyFldVal
*elm
= new PrimKeyFldVal();
219 sameInBothDbList
.append(elm
);
220 trgtDbValIter
.reset();
224 if (trgtdbelem
== NULL
) {
225 csqlelem
->inTrgtDb
= false;
226 PrimKeyFldVal
*elm
= new PrimKeyFldVal();
228 diffInValList
.append(elm
);
229 trgtDbValIter
.reset();
233 while((trgtdbelem
= (PrimKeyFldVal
*)trgtDbValIter
.nextElement()) != NULL
) {
234 while((csqlelem
= (PrimKeyFldVal
*)csqlValIter
.nextElement()) != NULL
) {
235 if (trgtdbelem
->val
== csqlelem
->val
) {
240 if (csqlelem
== NULL
) {
241 trgtdbelem
->inCsql
= false;
242 PrimKeyFldVal
*elm
= new PrimKeyFldVal();
244 diffInValList
.append(elm
);
249 // Sorting the primary key field values present in either of the databases
250 list
<PrimKeyFldVal
*> li
;
251 ListIterator diffValIter
= diffInValList
.getIterator();
252 bool missingRecords
= false;
253 printf("\nMissing Records: Marked by \'X\'\n");
254 printf("-------------------+-------------------+-------------------+\n");
255 printf(" Primary Key | In CSQL | In Target DB |\n");
256 printf("-------------------+-------------------+-------------------+\n");
257 if (diffInValList
.size()) {
258 missingRecords
= true;
259 PrimKeyFldVal
*elem
= NULL
;
260 while ((elem
= (PrimKeyFldVal
*) diffValIter
.nextElement()) != NULL
)
263 list
<PrimKeyFldVal
*>::iterator it
;
265 for (it
= li
.begin(); it
!= li
.end(); it
++) {
266 if ((*it
)->inCsql
== false)
267 printf(" %-6d | X | |\n", (*it
)->val
);
268 else if ((*it
)->inTrgtDb
== false)
269 printf(" %-6d | | X |\n", (*it
)->val
);
271 printf("-------------------+-------------------+-------------------+\n");
274 printf(" No missing Records in either of the databases |\n");
275 printf("-------------------+-------------------+-------------------+\n");
278 // Need to clean up the mess that is no more required
279 PrimKeyFldVal
*pkFldVal
= NULL
;
280 while ((pkFldVal
= (PrimKeyFldVal
*) csqlValIter
.nextElement()) != NULL
)
282 valListInCsql
.reset();
283 while ((pkFldVal
= (PrimKeyFldVal
*) trgtDbValIter
.nextElement()) != NULL
)
285 valListInTrgtDb
.reset();
286 while ((pkFldVal
= (PrimKeyFldVal
*) diffValIter
.nextElement()) != NULL
)
288 diffInValList
.reset();
292 AbsSqlConnection
*csqlCon
= SqlFactory::createConnection(CSql
);
293 rv
= csqlCon
->connect("root", "manager");
295 dbMgr
->closeTable(table
); conn
.close();
296 delete trgtDbStmt
; delete trgtDbCon
;
297 delete csqlCon
; return ErrSysInit
;
299 AbsSqlStatement
*csqlStmt
= SqlFactory::createStatement(CSql
);
300 csqlStmt
->setConnection(csqlCon
);
302 //statement to fetch the values from the database
303 sprintf(statement
, "select * from %s where %s = ?;", tblName
, fieldName
);
304 rv
= csqlStmt
->prepare(statement
);
306 dbMgr
->closeTable(table
); conn
.close();
307 delete trgtDbStmt
; delete trgtDbCon
;
308 delete csqlStmt
; delete csqlCon
;
309 printf("Prepare failed\n");
313 // need to bind each field with buffer which is list of field values
314 List fldNameList
= table
->getFieldNameList();
315 ListIterator iter
= fldNameList
.getIterator();
316 Identifier
*fname
= NULL
;
317 FieldInfo
*fldInfo
= new FieldInfo();
320 // List to hold all the records that are present in both the databases
323 while (iter
.hasElement()) {
324 fname
= (Identifier
*) iter
.nextElement();
326 dbMgr
->closeTable(table
); conn
.close();
327 delete trgtDbStmt
; delete trgtDbCon
;
328 delete csqlStmt
; delete csqlCon
;
331 printf("Should never happen. Field Name list has NULL\n");
334 rv
= table
->getFieldInfo(fname
->name
, fldInfo
);
335 if (ErrNotFound
== rv
) {
336 dbMgr
->closeTable(table
); conn
.close();
337 delete trgtDbStmt
; delete trgtDbCon
;
338 delete csqlStmt
; delete csqlCon
;
341 printf("Field %s does not exist in table\n",
343 return ErrSyntaxError
;
345 FldVal
*fldVal
= new FldVal();
346 fldVal
->type
= fldInfo
->type
;
347 fldVal
->length
= fldInfo
->length
;
348 fldVal
->value
= AllDataType::alloc(fldInfo
->type
, fldInfo
->length
);
349 fieldValueList
.append(fldVal
);
350 csqlStmt
->bindField(paramPos
++, fldVal
->value
);
354 // WHERE parameter should be binded with the primary key field value of the list that is present in both the databases
355 ListIterator sameValIter
= sameInBothDbList
.getIterator();
356 PrimKeyFldVal
*sameElem
= NULL
;
357 while((sameElem
= (PrimKeyFldVal
*)sameValIter
.nextElement()) != NULL
) {
358 csqlCon
->beginTrans();
359 csqlStmt
->setIntParam(1, sameElem
->val
);
360 rv
= csqlStmt
->execute(rows
);
362 dbMgr
->closeTable(table
); conn
.close();
363 delete trgtDbStmt
; delete trgtDbCon
;
364 delete csqlStmt
; delete csqlCon
;
365 printf("Execute failed\n");
368 while (csqlStmt
->fetch() != NULL
) {
369 Record
*rec
= new Record();
370 rec
->val
= sameElem
->val
;
371 ListIterator fldValIter
= fieldValueList
.getIterator();
372 while (fldValIter
.hasElement()) {
373 FldVal
*fldVal
= (FldVal
*) fldValIter
.nextElement();
374 FldVal
*fldValue
= new FldVal();
375 fldValue
->type
= fldVal
->type
;
376 fldValue
->length
= fldVal
->length
;
377 fldValue
->value
= AllDataType::alloc(fldValue
->type
, fldValue
->length
);
378 AllDataType::copyVal(fldValue
->value
, fldVal
->value
, fldVal
->type
, fldVal
->length
);
379 rec
->fldValList
.append(fldValue
);
381 csqlRecordList
.append(rec
);
388 //statement to fetch the values from the database
389 sprintf(statement
, "select * from %s where %s = ?;", tblName
, fieldName
);
390 rv
= trgtDbStmt
->prepare(statement
);
392 dbMgr
->closeTable(table
); conn
.close();
393 delete csqlStmt
; delete csqlCon
;
394 delete trgtDbStmt
; delete trgtDbCon
;
395 printf("Prepare failed\n");
399 // need to bind each field with buffer which is list of field values
400 ListIterator fldValIter
= fieldValueList
.getIterator();
402 List trgtDbRecordList
;
404 while (fldValIter
.hasElement()) {
405 FldVal
*fldVal
= (FldVal
*) fldValIter
.nextElement();
406 trgtDbStmt
->bindField(paramPos
++, fldVal
->value
);
409 // WHERE parameter should be binded
411 while((sameElem
= (PrimKeyFldVal
*)sameValIter
.nextElement()) != NULL
) {
412 trgtDbCon
->beginTrans();
413 trgtDbStmt
->setIntParam(1, sameElem
->val
);
414 rv
= trgtDbStmt
->execute(rows
);
416 dbMgr
->closeTable(table
); conn
.close();
417 delete csqlStmt
; delete csqlCon
;
418 delete trgtDbStmt
; delete trgtDbCon
;
419 printf("Execute failed\n");
422 while (trgtDbStmt
->fetch() != NULL
) {
423 Record
*rec
= new Record();
424 rec
->val
= sameElem
->val
;
426 while (fldValIter
.hasElement()) {
427 FldVal
*fldVal
= (FldVal
*) fldValIter
.nextElement();
428 FldVal
*fldValue
= new FldVal();
429 fldValue
->type
= fldVal
->type
;
430 fldValue
->length
= fldVal
->length
;
431 fldValue
->value
= AllDataType::alloc(fldValue
->type
, fldValue
->length
);
432 AllDataType::copyVal(fldValue
->value
, fldVal
->value
, fldVal
->type
, fldVal
->length
);
433 rec
->fldValList
.append(fldValue
);
435 trgtDbRecordList
.append(rec
);
442 // freeing the fieldValue buffer list which is not required any more
443 FldVal
*fldVal
= NULL
;
444 while ((fldVal
=(FldVal
*) fldValIter
.nextElement()) != NULL
) {
448 fieldValueList
.reset();
450 // freeing the field value list that is present in both the databases
451 PrimKeyFldVal
*pkFldVal
= NULL
;
452 while ((pkFldVal
= (PrimKeyFldVal
*) sameValIter
.nextElement()) != NULL
)
454 sameInBothDbList
.reset();
456 // sort the records based on Primary key that is present in both the databases
457 list
<Record
*> csqlRecList
;
458 ListIterator csqlRecListIter
= csqlRecordList
.getIterator();
460 while ((elem
= (Record
*) csqlRecListIter
.nextElement()) != NULL
)
461 csqlRecList
.push_back(elem
);
462 csqlRecList
.sort(cmpRec
);
464 list
<Record
*> trgtDbRecList
;
465 ListIterator trgtDbRecListIter
= trgtDbRecordList
.getIterator();
466 while ((elem
= (Record
*) trgtDbRecListIter
.nextElement()) != NULL
)
467 trgtDbRecList
.push_back(elem
);
468 trgtDbRecList
.sort(cmpRec
);
471 bool isConsistent
= true;
472 list
<Record
*>::iterator itCsql
;
473 list
<Record
*>::iterator itTrgtDb
;
475 printf("\nInconsistent Records for the same key:\n");
476 printf("-------------------+-------------------+-------------------+-------------------+\n");
477 printf(" %-16s | %-16s | %-16s | %-16s |\n", "Primary Key", "Field Name", "In CSQL", "In Trgt DB");
478 printf("-------------------+-------------------+-------------------+-------------------+\n");
479 for (itCsql
= csqlRecList
.begin(), itTrgtDb
= trgtDbRecList
.begin();
480 itCsql
!= csqlRecList
.end() && itTrgtDb
!= trgtDbRecList
.end();
481 itCsql
++, itTrgtDb
++) {
482 ListIterator csqlIt
= (ListIterator
)((*itCsql
)->fldValList
).getIterator();
483 ListIterator trgtDbIt
= (ListIterator
)((*itTrgtDb
)->fldValList
).getIterator();
486 while (csqlIt
.hasElement() && trgtDbIt
.hasElement()) {
487 FldVal
*csqlElem
= (FldVal
*) csqlIt
.nextElement();
488 FldVal
*trgtDbElem
= (FldVal
*) trgtDbIt
.nextElement();
489 fname
= (Identifier
*) iter
.nextElement();
490 if (AllDataType::compareVal(csqlElem
->value
, trgtDbElem
->value
, OpEquals
, csqlElem
->type
, csqlElem
->length
) == false) {
491 isConsistent
= false;
493 printf(" %-16d | %-16s | ", (*itCsql
)->val
, fname
);
496 else printf(" | %-16s | ", fname
);
497 int cnt
= AllDataType::printVal(csqlElem
->value
, csqlElem
->type
, csqlElem
->length
);
502 cnt
= AllDataType::printVal(trgtDbElem
->value
, trgtDbElem
->type
, trgtDbElem
->length
);
510 if (isConsistent
== true && missingRecords
== false)
511 printf(" The data is consistent in both the databases |\n");
512 else if (isConsistent
== true && missingRecords
== true)
513 printf(" The data is consistent for the records with the same key |\n");
514 printf("-------------------+-------------------+-------------------+-------------------+\n");
516 // clean up all the mess before leaving
518 trgtDbRecList
.clear();
521 while ((fname
= (Identifier
*) iter
.nextElement()) != NULL
)
526 while((item
= (Record
*) csqlRecListIter
.nextElement()) != NULL
) {
527 ListIterator it
= (ListIterator
) item
->fldValList
.getIterator();
528 FldVal
*fldVal
= NULL
;
529 while((fldVal
= (FldVal
*) it
.nextElement()) != NULL
) {
530 free (fldVal
->value
);
535 csqlRecordList
.reset();
537 while((item
= (Record
*) trgtDbRecListIter
.nextElement()) != NULL
) {
538 ListIterator it
= (ListIterator
) item
->fldValList
.getIterator();
539 FldVal
*fldVal
= NULL
;
540 while((fldVal
= (FldVal
*) it
.nextElement()) != NULL
) {
541 free (fldVal
->value
);
546 trgtDbRecordList
.reset();
547 delete csqlStmt
; delete csqlCon
;
550 dbMgr
->closeTable(table
);
552 delete trgtDbStmt
; delete trgtDbCon
;
556 int main(int argc
, char **argv
)
558 char username
[IDENTIFIER_LENGTH
];
560 char password
[IDENTIFIER_LENGTH
];
563 char tableName
[IDENTIFIER_LENGTH
];
565 bool tableNameSpecified
= false;
567 while ((c
= getopt(argc
, argv
, "U:P:t:pf?")) != EOF
)
571 case 'U' : { strcpy(username
, argv
[optind
- 1]); opt
=10; break; }
572 case 'P' : { strcpy(password
, argv
[optind
- 1]); opt
=10; break; }
573 case 't' : { strcpy(tableName
, argv
[optind
- 1]);
574 if (opt
==10) opt
= 2;
575 tableNameSpecified
= true;
578 case 'p' : { opt
= 3; break; } //verify at primary key level
579 case 'f' : { opt
= 4; break; } //verify at record level
580 case '?' : { opt
= 10; break; } //print help
590 if (!tableNameSpecified
) {
591 printf("Table name is not specified. Check usage with ?\n");
595 //printf("%s %s \n", username, password);
596 if (username
[0] == '\0' )
598 strcpy(username
, "root");
599 strcpy(password
, "manager");
603 DbRetVal rv
= conn
.open(username
, password
);
605 printf("Authentication failed\n");
609 DatabaseManager
*dbMgr
= (DatabaseManager
*) conn
.getDatabaseManager();
612 printf("could not connect to the database\n");
617 Table
*table
= dbMgr
->openTable(tableName
);
620 printf("Table \'%s\' does not exist\n", tableName
);
625 fp
= fopen(Conf::config
.getTableConfigFile(),"r");
627 dbMgr
->closeTable(table
);
629 printf("cachetable.conf file does not exist\n");
632 char tablename
[IDENTIFIER_LENGTH
];
634 bool filePresent
= false;
636 fscanf(fp
, "%d:%s\n", &mode
, tablename
);
637 if (mode
==2 ) //just replicated table and not cached
639 if (strcmp(tableName
, tablename
) == 0) {
645 long numTuples
= table
->numTuples();
646 dbMgr
->closeTable(table
);
649 if (filePresent
== false) { printf("The table \'%s\' is not cached\n", tableName
);
654 rv
= verifyCount(tableName
, numTuples
);
655 if (rv
!= OK
) return 6;
658 if (opt
== 3 || opt
== 4) {
659 rv
= verifyCount(tableName
, numTuples
);
660 if (rv
!= OK
) return 7;
661 rv
= verifyMismatchingRecords(tableName
, opt
);
662 if (rv
!= OK
) return 8;