From bc4f1b3ca51c551c5277a81b1686e12786eb8585 Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Thu, 7 Jun 2018 18:07:10 +0000 Subject: [PATCH] 2018-06-07 Fred Gleason * Implemented the '--check' command in rddbmgr(8). --- ChangeLog | 2 + utils/rddbmgr/check.cpp | 884 ++++++++++++++++++++++++++++++++++++++ utils/rddbmgr/rddbmgr.cpp | 77 ++++ utils/rddbmgr/rddbmgr.h | 31 ++ 4 files changed, 994 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5eb68e90..3c8bd879 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17019,3 +17019,5 @@ * Implemented the '--modify' command in rddbmgr(8). 2018-06-07 Fred Gleason * Implemented show database status in rddbmgr(8). +2018-06-07 Fred Gleason + * Implemented the '--check' command in rddbmgr(8). diff --git a/utils/rddbmgr/check.cpp b/utils/rddbmgr/check.cpp index 19f66720..81604e42 100644 --- a/utils/rddbmgr/check.cpp +++ b/utils/rddbmgr/check.cpp @@ -18,10 +18,894 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "rddbmgr.h" bool MainObject::Check(QString *err_msg) const { + if(GetCurrentSchema()!=RD_VERSION_DATABASE) { + *err_msg="unsupported schema for checking"; + return false; + } + + // + // Recover Audio + // + if(!db_relink_audio.isEmpty()) { + RelinkAudio(db_relink_audio); + return true; + } + + // + // Check for Orphaned Voice Tracks + // + printf("Checking voice tracks...\n"); + CheckOrphanedTracks(); + printf("done.\n\n"); + + // + // Check RDLogManager Consistency + // + printf("Checking RDLogManager events...\n"); + CheckEvents(); + printf("done.\n\n"); + printf("Checking RDLogManager clocks...\n"); + CheckClocks(); + printf("done.\n\n"); + + // + // Check for orphaned tables + // + printf("Checking for orphaned tables...\n"); + CheckOrphanedTables(); + printf("done.\n\n"); + + // + // Check for stale reservations + // + printf("Checking for stale cart reservations...\n"); + CheckPendingCarts(); + printf("done.\n\n"); + + // + // Check for orphaned carts + // + printf("Checking for orphaned carts...\n"); + CheckOrphanedCarts(); + printf("done.\n\n"); + + // + // Check for orphaned cuts + // + printf("Checking for orphaned cuts...\n"); + CheckOrphanedCuts(); + printf("done.\n\n"); + + // + // Check Cart->Cut Counts + // + printf("Checking cart->cuts counters...\n"); + CheckCutCounts(); + printf("done.\n\n"); + + // + // Check Orphaned Audio + // + printf("Checking for orphaned audio...\n"); + CheckOrphanedAudio(); + printf("done.\n\n"); + + // + // Validating Audio Lengths + // + printf("Validating audio lengths (this may take some time)...\n"); + ValidateAudioLengths(); + printf("done.\n\n"); + + // + // Rehash + // + if(!db_rehash.isEmpty()) { + printf("Checking hashes...\n"); + Rehash(db_rehash); + printf("done.\n\n"); + } + *err_msg="ok"; return true; } + + +void MainObject::RelinkAudio(const QString &srcdir) const +{ + QString sql; + RDSqlQuery *q; + + QDir dir(srcdir); + QStringList files=dir.entryList(QDir::Files|QDir::Readable|QDir::Hidden); + for(unsigned i=0;inext()) { + printf(" Recovering %06u/%03d [%s]...", + RDCut::cartNumber(q->value(0).toString()), + RDCut::cutNumber(q->value(0).toString()), + (const char *)q->value(1).toString()); + fflush(stdout); + if(db_relink_audio_move) { + unlink(RDCut::pathName(q->value(0).toString())); + if(link(filename,RDCut::pathName(q->value(0).toString()))<0) { + if(errno==EXDEV) { + if(firstdest.isEmpty()) { + if(CopyFile(RDCut::pathName(q->value(0).toString()),filename)) { + firstdest=RDCut::pathName(q->value(0).toString()); + } + else { + fprintf(stderr,"unable to copy file \"%s\"\n", + (const char *)filename); + delete_source=false; + } + } + else { + unlink(RDCut::pathName(q->value(0).toString())); + link(firstdest,RDCut::pathName(q->value(0).toString())); + } + } + else { + fprintf(stderr,"unable to move file \"%s\" [%s]\n", + (const char *)filename,strerror(errno)); + delete_source=false; + } + } + } + else { + if(firstdest.isEmpty()) { + if(CopyFile(RDCut::pathName(q->value(0).toString()),filename)) { + firstdest=RDCut::pathName(q->value(0).toString()); + } + else { + fprintf(stderr,"unable to copy file \"%s\"\n", + (const char *)filename); + } + } + else { + unlink(RDCut::pathName(q->value(0).toString())); + link(firstdest,RDCut::pathName(q->value(0).toString())); + } + } + printf(" done.\n"); + } + if(db_relink_audio_move&&delete_source) { + unlink(filename); + } + } +} + + +void MainObject::CheckOrphanedTracks() const +{ + QString logname; + QString sql="select NUMBER,TITLE,OWNER from CART where OWNER!=\"\""; + QSqlQuery *q=new QSqlQuery(sql); + QSqlQuery *q1; + + while(q->next()) { + logname=q->value(2).toString()+"_LOG"; + logname.replace(" ","_"); + sql=QString().sprintf("select ID from `%s` where CART_NUMBER=%u", + (const char *)logname,q->value(0).toUInt()); + q1=new QSqlQuery(sql); + if(!q1->first()) { + printf(" Found orphaned track %u - \"%s\". Delete? (y/N) ", + q->value(0).toUInt(),(const char *)q->value(1).toString()); + fflush(NULL); + if(UserResponse()) { + RDCart *cart=new RDCart(q->value(0).toUInt()); + cart->remove(NULL,NULL,db_config); + delete cart; + RDLog *log=new RDLog(q->value(2).toString()); + if(log->exists()) { + log->updateTracks(); + } + delete log; + } + } + delete q1; + } + delete q; +} + + +void MainObject::CheckClocks() const +{ + QString sql; + QSqlQuery *q; + QSqlQuery *q1; + QSqlQuery *q2; + QSqlQuery *q3; + QString eventname; + + sql="select NAME from CLOCKS"; + q=new QSqlQuery(sql); + while(q->next()) { + // + // Check the CLK Table + // + sql=QString("select EVENT_NAME,ID from ")+ + RDClock::tableName(q->value(0).toString()); + q1=new QSqlQuery(sql); + if(q1->isActive()) { + // + // Check the clock -> event linkage + // + while(q1->next()) { + sql=QString().sprintf("select NAME from EVENTS where NAME=\"%s\"", + (const char *)q1->value(0).toString()); + q2=new QSqlQuery(sql); + if(q2->first()) { + if(q1->value(0)!=q2->value(0)) { // Make sure the cases match! + printf(" Clock %s's linkage to event %s is broken -- fix (y/N)? ", + (const char *)q->value(0).toString(), + (const char *)q2->value(0).toString()); + fflush(NULL); + if(UserResponse()) { + sql=QString("update ")+ + RDClock::tableName(q->value(0).toString())+ + " set EVENT_NAME=\""+RDEscapeString(q2->value(0).toString())+ + "\""+QString().sprintf(" where ID=%d",q1->value(1).toInt()); + q3=new QSqlQuery(sql); + delete q3; + } + } + } + delete q2; + } + } + else { + printf(" Clock %s is missing the CLK table -- fix (y/N)? ", + (const char *)q->value(0).toString()); + fflush(NULL); + if(UserResponse()) { + sql=RDCreateClockTableSql(RDClock::tableName(q->value(0).toString()), + db_config); + q2=new QSqlQuery(sql); + delete q2; + } + } + delete q1; + } + delete q; +} + + +void MainObject::CheckEvents() const +{ + QString sql; + QSqlQuery *q; + QSqlQuery *q1; + QSqlQuery *q2; + QString eventname; + + sql="select NAME from EVENTS"; + q=new QSqlQuery(sql); + while(q->next()) { + eventname=q->value(0).toString(); + eventname.replace(" ","_"); + + // + // Check the PRE Table + // + sql=QString().sprintf ("select ID from %s_PRE",(const char *)eventname); + q1=new QSqlQuery(sql); + if(q1->size()<0) { + printf(" Event %s is missing the PRE table -- fix (y/N)? ", + (const char *)q->value(0).toString()); + fflush(NULL); + if(UserResponse()) { + sql=RDCreateLogTableSql(eventname+"_PRE",db_config); + q2=new QSqlQuery(sql); + delete q2; + } + } + delete q1; + + // + // Check the POST Table + // + sql=QString().sprintf ("select ID from %s_POST",(const char *)eventname); + q1=new QSqlQuery(sql); + if(!q1->isActive()) { + printf(" Event %s is missing the POST table -- fix (y/N)? ", + (const char *)q->value(0).toString()); + fflush(NULL); + if(UserResponse()) { + sql=RDCreateLogTableSql(eventname+"_POST",db_config); + q2=new QSqlQuery(sql); + delete q2; + } + } + delete q1; + } + delete q; +} + + +void MainObject::CheckOrphanedTables() const +{ + QSqlQuery *table_q; + QSqlQuery *q; + QString sql; + + // + // Generate Table Query + // + sql="show tables"; + table_q=new QSqlQuery(sql); + + // + // Look for orphaned clocks + // + sql="select NAME from CLOCKS"; + q=new QSqlQuery(sql); + CleanTables("CLK",table_q,q); + CleanTables("RULES",table_q,q); + delete q; + + // + // Look for orphaned events + // + sql="select NAME from EVENTS"; + q=new QSqlQuery(sql); + CleanTables("PRE",table_q,q); + CleanTables("POST",table_q,q); + delete q; + + // + // Look for orphaned logs + // + sql="select NAME from LOGS"; + q=new QSqlQuery(sql); + CleanTables("LOG",table_q,q); + CleanTables("STACK",table_q,q); + delete q; + + // + // Look for orphaned services + // + sql="select NAME from SERVICES"; + q=new QSqlQuery(sql); + CleanTables("SRT",table_q,q); + delete q; + + // + // Look for orphaned feeds + // + sql="select KEY_NAME from FEEDS"; + q=new QSqlQuery(sql); + CleanTables("FIELDS",table_q,q); + CleanTables("FLG",table_q,q); + delete q; + + // + // Look for stale and obsolete tables + // + CleanTables("IMP",table_q); + CleanTables("REC",table_q); + + + delete table_q; +} + + +void MainObject::CheckCutCounts() const +{ + QString sql; + QSqlQuery *q; + QSqlQuery *q1; + + sql="select NUMBER,CUT_QUANTITY,TITLE from CART"; + q=new QSqlQuery(sql); + while(q->next()) { + sql=QString().sprintf("select CUT_NAME from CUTS \ + where (CART_NUMBER=%u)&&(LENGTH>0)", + q->value(0).toUInt()); + q1=new QSqlQuery(sql); + if(q1->size()!=q->value(1).toInt()) { + printf(" Cart %u [%s] has invalid cut count, fix (y/N)?", + q->value(0).toUInt(),(const char *)q->value(2).toString()); + if(UserResponse()) { + RDCart *cart=new RDCart(q->value(0).toUInt()); + cart->updateLength(); + cart->resetRotation(); + delete cart; + } + } + delete q1; + } + delete q; +} + + +void MainObject::CheckPendingCarts() const +{ + QString sql; + QSqlQuery *q; + QDateTime now(QDate::currentDate(),QTime::currentTime()); + + sql=QString("select NUMBER from CART where ")+ + "(PENDING_STATION is not null)&&"+ + "(PENDING_DATETIME<\""+now.addDays(-1). + toString("yyyy-MM-dd hh:mm:ss")+"\")"; + q=new QSqlQuery(sql); + while(q->next()) { + printf(" Cart %06u has stale reservation, delete cart(y/N)?", + q->value(0).toUInt()); + if(UserResponse()) { + RDCart::removeCart(q->value(0).toUInt(),NULL,NULL,db_config); + } + } + delete q; +} + + +void MainObject::CheckOrphanedCarts() const +{ + QString sql; + QSqlQuery *q; + QSqlQuery *q1; + + sql="select CART.NUMBER,CART.TITLE from CART left join GROUPS \ + on CART.GROUP_NAME=GROUPS.NAME where GROUPS.NAME is null"; + q=new QSqlQuery(sql); + while(q->next()) { + printf(" Cart %06u [%s] has missing/invalid group.\n", + q->value(0).toUInt(),(const char *)q->value(1).toString()); + if(db_orphan_group_name.isEmpty()) { + printf(" Rerun rddbcheck with the --orphan-group= switch to fix.\n\n"); + } + else { + printf(" Assign to group \"%s\" (y/N)?",(const char *)db_orphan_group_name); + if(UserResponse()) { + sql=QString(). + sprintf("update CART set GROUP_NAME=\"%s\" where NUMBER=%u", + (const char *)RDEscapeString(db_orphan_group_name), + q->value(0).toUInt()); + q1=new QSqlQuery(sql); + delete q1; + } + printf("\n"); + } + } + delete q; +} + + +void MainObject::CheckOrphanedCuts() const +{ + QString sql; + QSqlQuery *q; + QSqlQuery *q1; + QSqlQuery *q2; + QFileInfo *file=NULL; + + sql="select CUTS.CUT_NAME,CUTS.DESCRIPTION from CUTS left join CART \ + on CUTS.CART_NUMBER=CART.NUMBER where CART.NUMBER is null"; + q=new QSqlQuery(sql); + while(q->next()) { + printf(" Cut %s [%s] is orphaned.\n", + (const char *)q->value(0).toString(), + (const char *)q->value(1).toString()); + // + // Try to repair it + // + sql=QString().sprintf("select NUMBER from CART where NUMBER=%d", + q->value(0).toString().left(6).toUInt()); + q1=new QSqlQuery(sql); + if(q1->first()) { + printf(" Repair it (y/N)?"); + if(UserResponse()) { + // + // FIXME: Regen Cart Data + // + sql=QString(). + sprintf("update CUTS set CART_NUMBER=%u where CUT_NAME=\"%s\"", + q1->value(0).toUInt(), + (const char *)q->value(0).toString()); + q2=new QSqlQuery(sql); + delete q2; + delete q1; + printf("\n"); + continue; + } + } + printf("\n"); + delete q1; + + // + // Try to recover audio + // + file=new QFileInfo(RDCut::pathName(q->value(0).toString())); + if(!file->exists()) { + printf(" Clear it (y/N)?"); + if(UserResponse()) { + sql=QString().sprintf("delete from CUTS where CUT_NAME=\"%s\"", + (const char *)q->value(0).toString()); + q1=new QSqlQuery(sql); + delete q1; + } + } + else { + if(db_dump_cuts_dir.isEmpty()) { + printf(" Rerun rddbcheck with the --dump-cuts-dir= switch to fix.\n"); + } + else { + printf(" Clear it (y/N)?"); + if(UserResponse()) { + system("mv "+file->filePath()+" "+db_dump_cuts_dir+"/"); + sql=QString().sprintf("delete from CUTS where CUT_NAME=\"%s\"", + (const char *)q->value(0).toString()); + q1=new QSqlQuery(sql); + delete q1; + printf(" Saved audio in \"%s/%s\"\n",(const char *)db_dump_cuts_dir, + (const char *)file->fileName()); + } + } + } + printf("\n"); + delete file; + file=NULL; + } + delete q; +} + + +void MainObject::CheckOrphanedAudio() const +{ + QDir dir(db_config->audioRoot()); + QStringList list=dir.entryList("??????_???.wav",QDir::Files); + for(unsigned i=0;ifirst()) { + printf(" File \"%s/%s\" is orphaned.\n", + (const char *)db_config->audioRoot(),(const char *)list[i]); + if(db_dump_cuts_dir.isEmpty()) { + printf( + " Rerun rddbcheck with the --dump-cuts-dir= switch to fix.\n\n"); + } + else { + printf(" Move to \"%s\" (y/N)? ",(const char *)db_dump_cuts_dir); + if(UserResponse()) { + system(QString().sprintf("mv %s/%s %s/", + (const char *)db_config->audioRoot(), + (const char *)list[i], + (const char *)db_dump_cuts_dir)); + printf(" Saved audio in \"%s/%s\"\n",(const char *)db_dump_cuts_dir, + (const char *)list[i]); + } + } + } + delete q; + } + } + } +} + + +void MainObject::ValidateAudioLengths() const +{ + QString sql; + QSqlQuery *q; + RDWaveFile *wave=NULL; + + sql="select CUT_NAME,CART_NUMBER,LENGTH from CUTS order by CART_NUMBER"; + q=new QSqlQuery(sql); + while(q->next()) { + if(q->value(2).toInt()>0) { + wave=new RDWaveFile(RDCut::pathName(q->value(0).toString())); + if(wave->openWave()) { + if((int)wave->getExtTimeLength()<(q->value(2).toInt()-100)) { + SetCutLength(q->value(0).toString(),wave->getExtTimeLength()); + } + } + else { + SetCutLength(q->value(0).toString(),0); + } + delete wave; + } + } + delete q; +} + + +void MainObject::Rehash(const QString &arg) const +{ + QString sql; + QSqlQuery *q; + unsigned cartnum; + bool ok=false; + + if(arg.lower()=="all") { + sql=QString("select NUMBER from CART where ")+ + QString().sprintf("TYPE=%d ",RDCart::Audio)+ + "order by NUMBER"; + q=new QSqlQuery(sql); + while(q->next()) { + RehashCart(q->value(0).toUInt()); + } + delete q; + return; + } + cartnum=arg.toUInt(&ok); + if(ok&&(cartnum>0)&&(cartnum<=RD_MAX_CART_NUMBER)) { + RehashCart(cartnum); + return; + } + RDCut *cut=new RDCut(arg); + if(cut->exists()) { + RehashCut(arg); + } + delete cut; +} + + +void MainObject::RehashCart(unsigned cartnum) const +{ + RDCart *cart=new RDCart(cartnum); + if(cart->exists()) { + if(cart->type()==RDCart::Audio) { + QString sql=QString("select CUT_NAME from CUTS where ")+ + QString().sprintf("CART_NUMBER=%u ",cartnum)+ + "order by CUT_NAME"; + QSqlQuery *q=new QSqlQuery(sql); + while(q->next()) { + RehashCut(q->value(0).toString()); + } + delete q; + } + } + else { + printf(" Cart %06u does not exist.\n",cartnum); + } +} + + +void MainObject::RehashCut(const QString &cutnum) const +{ + QString hash=RDSha1Hash(RDCut::pathName(cutnum),true); + if(hash.isEmpty()) { + printf(" Unable to generate hash for \"%s\"\n", + (const char *)RDCut::pathName(cutnum)); + } + else { + RDCut *cut=new RDCut(cutnum); + if(cut->exists()) { + if(cut->sha1Hash().isEmpty()) { + cut->setSha1Hash(hash); + } + else { + if(cut->sha1Hash()!=hash) { + RDCart *cart=new RDCart(RDCut::cartNumber(cutnum)); + printf(" Cut %d [%s] in cart %06u [%s] has inconsistent SHA1 hash. Fix? (y/N) ", + cut->cutNumber(), + (const char *)cut->description(), + cart->number(), + (const char *)cart->title()); + fflush(NULL); + if(UserResponse()) { + cut->setSha1Hash(hash); + } + delete cart; + } + } + } + else { + printf(" Cut \"%s\" does not exist.\n",(const char *)cutnum); + } + delete cut; + } +} + + +void MainObject::SetCutLength(const QString &cutname,int len) const +{ + QString sql; + QSqlQuery *q; + RDCut *cut=new RDCut(cutname); + RDCart *cart=new RDCart(cut->cartNumber()); + + printf(" Cut %d [%s] in cart %06u [%s] has invalid length. Correct? (y/N) ", + cut->cutNumber(), + (const char *)cut->description(), + cart->number(), + (const char *)cart->title()); + fflush(NULL); + if(UserResponse()) { + fflush(NULL); + sql=QString("update CUTS set ")+ + "START_POINT=0,"+ + QString().sprintf("END_POINT=%d,",len)+ + "FADEUP_POINT=-1,"+ + "FADEDOWN_POINT=-1,"+ + "SEGUE_START_POINT=-1,"+ + "SEGUE_END_POINT=-1,"+ + "TALK_START_POINT=-1,"+ + "TALK_END_POINT=-1,"+ + "HOOK_START_POINT=-1,"+ + "HOOK_END_POINT=-1,"+ + "PLAY_GAIN=0,"+ + QString().sprintf("LENGTH=%d where ",len)+ + "CUT_NAME=\""+RDEscapeString(cutname)+"\""; + q=new QSqlQuery(sql); + delete q; + cart->updateLength(); + cart->resetRotation(); + } + delete cart; + delete cut; +} + + +void MainObject::CleanTables(const QString &ext,QSqlQuery *table_q, + QSqlQuery *name_q) const +{ + QString sql; + QSqlQuery *q1; + + table_q->seek(-1); + while(table_q->next()) { + if(!IsTableLinked(name_q,ext,table_q->value(0).toString())) { + printf(" Table %s is orphaned -- delete (y/N)? ", + (const char *)RDEscapeString(table_q->value(0).toString())); + fflush(NULL); + if(UserResponse()) { + sql=QString().sprintf("drop table %s", + (const char *)RDEscapeString(table_q->value(0).toString())); + q1=new QSqlQuery(sql); + delete q1; + } + } + } +} + + +void MainObject::CleanTables(const QString &ext,QSqlQuery *table_q) const +{ + QString sql; + QSqlQuery *q1; + + table_q->seek(-1); + while(table_q->next()) { + if(table_q->value(0).toString().right(ext.length())==ext) { + printf(" Table %s is orphaned -- delete (y/N)? ", + (const char *)table_q->value(0).toString()); + fflush(NULL); + if(UserResponse()) { + sql=QString().sprintf("drop table %s", + (const char *)RDEscapeString(table_q->value(0).toString())); + q1=new QSqlQuery(sql); + delete q1; + } + } + } +} + + +bool MainObject::IsTableLinked(QSqlQuery *name_q,const QString &ext, + const QString &table) const +{ + QString tablename; + + if(table.right(ext.length())!=ext) { + return true; + } + name_q->seek(-1); + while(name_q->next()) { + tablename=name_q->value(0).toString()+"_"+ext; + tablename.replace(" ","_"); + if(tablename==table) { + return true; + } + } + return false; +} + + +bool MainObject::UserResponse() const +{ + char c=0; + + if(db_yes) { + printf("y\n"); + return true; + } + if(db_no) { + printf("n\n"); + return false; + } + while((c!='y')&&(c!='Y')&&(c!='n')&&(c!='N')) { + scanf("%c",&c); + if((c=='y')||(c=='Y')) { + scanf("%c",&c); + return true; + } + if(c=='\n') { + return false; + } + } + scanf("%c",&c); + return false; +} + + +bool MainObject::CopyFile(const QString &destfile,const QString &srcfile) const +{ + int src_fd=-1; + struct stat src_stat; + int dest_fd=-1; + struct stat dest_stat; + int n; + int blksize; + char *data=NULL; + + if((src_fd=open(srcfile,O_RDONLY))<0) { + return false; + } + fstat(src_fd,&src_stat); + mode_t mask=umask(S_IRWXO); + if((dest_fd=open(destfile,O_WRONLY|O_CREAT|O_TRUNC, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP))<0) { + close(src_fd); + return false; + } + umask(mask); + fstat(dest_fd,&dest_stat); + blksize=src_stat.st_blksize; + if(dest_stat.st_blksize #include +#include #include #include #include #include +#include +#include #include "rddbmgr.h" @@ -44,6 +47,11 @@ MainObject::MainObject(QObject *parent) bool generate_audio=false; db_verbose=false; + db_yes=false; + db_no=false; + db_relink_audio=""; + db_relink_audio_move=false; + // // Check that we're 'root' // @@ -174,6 +182,32 @@ MainObject::MainObject(QObject *parent) db_verbose=true; cmd->setProcessed(i,true); } + + // + // --check options + // + if(cmd->key(i)=="--yes") { + db_yes=true; + } + if(cmd->key(i)=="--no") { + db_no=true; + } + if(cmd->key(i)=="--orphan-group") { + db_orphan_group_name=cmd->value(i); + } + if(cmd->key(i)=="--dump-cuts-dir") { + db_dump_cuts_dir=cmd->value(i); + } + if(cmd->key(i)=="--relink-audio") { + db_relink_audio=cmd->value(i); + } + if(cmd->key(i)=="--relink-audio-move") { + db_relink_audio_move=true; + } + if(cmd->key(i)=="--rehash") { + db_rehash=cmd->value(i); + } + if(!cmd->processed(i)) { fprintf(stderr,"rddbmgr: unrecognized option \"%s\"\n", (const char *)cmd->key(i)); @@ -205,8 +239,51 @@ MainObject::MainObject(QObject *parent) } } } + if(db_yes&&db_no) { + fprintf(stderr,"rddbmgr: '--yes' and '--no' are mutually exclusive\n"); + exit(1); + } delete cmd; + // + // Sanity Checks for --check + // + if(db_command==MainObject::CheckCommand) { + // Check for dump cuts directory + if(!db_dump_cuts_dir.isEmpty()) { + QFileInfo file(db_dump_cuts_dir); + if(!file.exists()) { + fprintf(stderr,"rddbmgr: directory \"%s\" does not exist.\n", + (const char *)db_dump_cuts_dir); + exit(1); + } + if(!file.isDir()) { + fprintf(stderr,"rddbmgr: \"%s\" is not a directory.\n", + (const char *)db_dump_cuts_dir); + exit(1); + } + if(!file.isWritable()) { + fprintf(stderr,"rddbmgr: \"%s\" is not writable.\n", + (const char *)db_dump_cuts_dir); + exit(1); + } + } + + // Check that Orphan group exists + if(!db_orphan_group_name.isEmpty()) { + QString sql=QString("select NAME from GROUPS where ")+ + "NAME=\""+RDEscapeString(db_orphan_group_name)+"\""; + RDSqlQuery *q=new RDSqlQuery(sql,false); + if(!q->first()) { + fprintf(stderr,"rddbmgr: invalid group \"%s\"\n", + (const char *)db_orphan_group_name); + delete q; + exit(1); + } + delete q; + } + } + if(db_verbose) { fprintf(stderr,"Using DB Credentials:\n"); fprintf(stderr," Hostname: %s\n",(const char *)db_mysql_hostname); diff --git a/utils/rddbmgr/rddbmgr.h b/utils/rddbmgr/rddbmgr.h index 82fe6fd4..47329334 100644 --- a/utils/rddbmgr/rddbmgr.h +++ b/utils/rddbmgr/rddbmgr.h @@ -24,6 +24,7 @@ #include #include +#include #define RDDBMGR_USAGE "[options]\n" @@ -38,6 +39,29 @@ class MainObject : public QObject // check.cpp // bool Check(QString *err_msg) const; + void RelinkAudio(const QString &srcdir) const; + void CheckOrphanedTracks() const; + void CheckClocks() const; + void CheckEvents() const; + void CheckOrphanedTables() const; + void CheckCutCounts() const; + void CheckPendingCarts() const; + void CheckOrphanedCarts() const; + void CheckOrphanedCuts() const; + void CheckOrphanedAudio() const; + void ValidateAudioLengths() const; + void Rehash(const QString &arg) const; + void RehashCart(unsigned cartnum) const; + void RehashCut(const QString &cutnum) const; + void SetCutLength(const QString &cutname,int len) const; + void CleanTables(const QString &ext,QSqlQuery *table_q, + QSqlQuery *name_q) const; + void CleanTables(const QString &ext,QSqlQuery *table_q) const; + bool IsTableLinked(QSqlQuery *name_q,const QString &ext, + const QString &table) const; + void RemoveCart(unsigned cartnum); + bool CopyFile(const QString &destfile,const QString &srcfile) const; + bool UserResponse() const; // // create.cpp @@ -98,6 +122,13 @@ class MainObject : public QObject QString db_mysql_charset; QString db_mysql_collation; bool db_verbose; + bool db_yes; + bool db_no; + QString db_orphan_group_name; + QString db_dump_cuts_dir; + QString db_rehash; + QString db_relink_audio; + bool db_relink_audio_move; QString db_table_create_postfix; RDConfig *db_config; };