X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fcats%2Fsql_get.c;h=6da66a75bdfc184e81b91290029e62cc285daca3;hb=c6b2ff4c5c9a0d52923129bdd74a96c40e9cf072;hp=6d2e1155fa9e3ed370942ffbf4036bef5bc62228;hpb=16415680c8b2c30cddd68400f906d410b4d344f0;p=bacula%2Fbacula diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index 6d2e1155fa..6da66a75bd 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -1,3 +1,30 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2000-2009 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version two of the GNU General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ /* * Bacula Catalog Database Get record interface routines * Note, these routines generally get a record by id or @@ -6,22 +33,7 @@ * * Kern Sibbald, March 2000 * - * Version $Id$ - */ - -/* - Copyright (C) 2000-2005 Kern Sibbald - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - version 2 as amended with additional clauses defined in the - file LICENSE in the main source directory. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - the file LICENSE for additional details. - + * Version $Id: sql_get.c 8918 2009-06-23 11:56:35Z ricozz $ */ @@ -33,7 +45,7 @@ #include "bacula.h" #include "cats.h" -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI /* ----------------------------------------------------------------------- * @@ -45,14 +57,6 @@ /* Forward referenced functions */ static int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr); static int db_get_filename_record(JCR *jcr, B_DB *mdb); -static int db_get_path_record(JCR *jcr, B_DB *mdb); - - -/* Imported subroutines */ -extern void print_result(B_DB *mdb); -extern int QueryDB(const char *file, int line, JCR *jcr, B_DB *db, char *select_cmd); -extern void split_path_and_file(JCR *jcr, B_DB *mdb, const char *fname); - /* @@ -92,6 +96,18 @@ int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, * Note in this routine, we do not use Jmsg because it may be * called to get attributes of a non-existent file, which is * "normal" if a new file is found during Verify. + * + * The following is a bit of a kludge: because we always backup a + * directory entry, we can end up with two copies of the directory + * in the backup. One is when we encounter the directory and find + * we cannot recurse into it, and the other is when we find an + * explicit mention of the directory. This can also happen if the + * use includes the directory twice. In this case, Verify + * VolumeToCatalog fails because we have two copies in the catalog, + * and only the first one is marked (twice). So, when calling from Verify, + * jr is not NULL and we know jr->FileIndex is the fileindex + * of the version of the directory/file we actually want and do + * a more explicit SQL search. */ static int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr) @@ -100,16 +116,25 @@ int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr) int stat = 0; char ed1[50], ed2[50], ed3[50]; - if (jcr->JobLevel == L_VERIFY_DISK_TO_CATALOG) { - Mmsg(mdb->cmd, + if (jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) { + Mmsg(mdb->cmd, "SELECT FileId, LStat, MD5 FROM File,Job WHERE " "File.JobId=Job.JobId AND File.PathId=%s AND " -"File.FilenameId=%s AND Job.Type='B' AND Job.JobSTATUS='T' AND " +"File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND " "ClientId=%s ORDER BY StartTime DESC LIMIT 1", edit_int64(fdbr->PathId, ed1), edit_int64(fdbr->FilenameId, ed2), edit_int64(jr->ClientId,ed3)); + } else if (jr != NULL) { + /* Called from Verify so jr->FileIndex is valid */ + Mmsg(mdb->cmd, +"SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND " +"File.FilenameId=%s AND FileIndex=%u", + edit_int64(fdbr->JobId, ed1), + edit_int64(fdbr->PathId, ed2), + edit_int64(fdbr->FilenameId,ed3), + jr->FileIndex); } else { Mmsg(mdb->cmd, "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND " @@ -118,7 +143,7 @@ int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr) edit_int64(fdbr->PathId, ed2), edit_int64(fdbr->FilenameId,ed3)); } - Dmsg3(050, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n", + Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n", fdbr->JobId, fdbr->FilenameId, fdbr->PathId); Dmsg1(100, "Query=%s\n", mdb->cmd); @@ -129,6 +154,7 @@ int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr) if (mdb->num_rows > 1) { Mmsg1(mdb->errmsg, _("get_file_record want 1 got rows=%d\n"), mdb->num_rows); + Dmsg1(000, "=== Problem! %s", mdb->errmsg); } if (mdb->num_rows >= 1) { if ((row = sql_fetch_row(mdb)) == NULL) { @@ -136,7 +162,7 @@ int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr) } else { fdbr->FileId = (FileId_t)str_to_int64(row[0]); bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat)); - bstrncpy(fdbr->SIG, row[2], sizeof(fdbr->SIG)); + bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest)); stat = 1; } } else { @@ -164,7 +190,7 @@ static int db_get_filename_record(JCR *jcr, B_DB *mdb) int FilenameId = 0; mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2); - db_escape_string(mdb->esc_name, mdb->fname, mdb->fnl); + db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl); Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name); if (QUERY_DB(jcr, mdb, mdb->cmd)) { @@ -202,13 +228,13 @@ static int db_get_filename_record(JCR *jcr, B_DB *mdb) * * DO NOT use Jmsg in this routine (see notes for get_file_record) */ -static int db_get_path_record(JCR *jcr, B_DB *mdb) +int db_get_path_record(JCR *jcr, B_DB *mdb) { SQL_ROW row; uint32_t PathId = 0; mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2); - db_escape_string(mdb->esc_name, mdb->path, mdb->pnl); + db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl); if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl && strcmp(mdb->cached_path, mdb->path) == 0) { @@ -257,10 +283,10 @@ static int db_get_path_record(JCR *jcr, B_DB *mdb) /* * Get Job record for given JobId or Job name - * Returns: 0 on failure - * 1 on success + * Returns: false on failure + * true on success */ -int db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr) +bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr) { SQL_ROW row; char ed1[50]; @@ -269,25 +295,27 @@ int db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr) if (jr->JobId == 0) { Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime," "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus," -"Type,Level,ClientId " +"Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId," +"SchedTime,RealEndTime,ReadBytes,HasBase " "FROM Job WHERE Job='%s'", jr->Job); } else { Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime," "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus," -"Type,Level,ClientId " +"Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId," +"SchedTime,RealEndTime,ReadBytes,HasBase " "FROM Job WHERE JobId=%s", edit_int64(jr->JobId, ed1)); } if (!QUERY_DB(jcr, mdb, mdb->cmd)) { db_unlock(mdb); - return 0; /* failed */ + return false; /* failed */ } if ((row = sql_fetch_row(mdb)) == NULL) { Mmsg1(mdb->errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1)); sql_free_result(mdb); db_unlock(mdb); - return 0; /* failed */ + return false; /* failed */ } jr->VolSessionId = str_to_uint64(row[0]); @@ -299,14 +327,29 @@ int db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr) jr->JobBytes = str_to_int64(row[6]); jr->JobTDate = str_to_int64(row[7]); bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job)); - jr->JobStatus = (int)*row[9]; - jr->JobType = (int)*row[10]; - jr->JobLevel = (int)*row[11]; + jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError; + jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP; + jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE; jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)""); + bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name)); + jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)""); + bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime)); + if (jr->JobId == 0) { + jr->JobId = str_to_int64(row[16]); + } + jr->FileSetId = str_to_int64(row[17]); + bstrncpy(jr->cSchedTime, row[3]!=NULL?row[18]:"", sizeof(jr->cSchedTime)); + bstrncpy(jr->cRealEndTime, row[3]!=NULL?row[19]:"", sizeof(jr->cRealEndTime)); + jr->ReadBytes = str_to_int64(row[20]); + jr->StartTime = str_to_utime(jr->cStartTime); + jr->SchedTime = str_to_utime(jr->cSchedTime); + jr->EndTime = str_to_utime(jr->cEndTime); + jr->RealEndTime = str_to_utime(jr->cRealEndTime); + jr->HasBase = str_to_int64(row[21]); sql_free_result(mdb); db_unlock(mdb); - return 1; + return true; } /* @@ -385,7 +428,8 @@ int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS db_lock(mdb); Mmsg(mdb->cmd, "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile," -"JobMedia.EndFile,StartBlock,JobMedia.EndBlock,Copy,Stripe" +"JobMedia.EndFile,StartBlock,JobMedia.EndBlock,Copy," +"Slot,StorageId,InChanger" " FROM JobMedia,Media WHERE JobMedia.JobId=%s" " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId", edit_int64(JobId, ed1)); @@ -393,14 +437,16 @@ int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS Dmsg1(130, "VolNam=%s\n", mdb->cmd); if (QUERY_DB(jcr, mdb, mdb->cmd)) { mdb->num_rows = sql_num_rows(mdb); - Dmsg1(130, "Num rows=%d\n", mdb->num_rows); + Dmsg1(200, "Num rows=%d\n", mdb->num_rows); if (mdb->num_rows <= 0) { Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId); stat = 0; } else { stat = mdb->num_rows; + DBId_t *SId = NULL; if (stat > 0) { *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS)); + SId = (DBId_t *)malloc(stat * sizeof(DBId_t)); } for (i=0; i < stat; i++) { if ((row = sql_fetch_row(mdb)) == NULL) { @@ -409,18 +455,40 @@ int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS stat = 0; break; } else { + DBId_t StorageId; + uint32_t StartBlock, EndBlock, StartFile, EndFile; bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH); bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH); Vols[i].FirstIndex = str_to_uint64(row[2]); Vols[i].LastIndex = str_to_uint64(row[3]); - Vols[i].StartFile = str_to_uint64(row[4]); - Vols[i].EndFile = str_to_uint64(row[5]); - Vols[i].StartBlock = str_to_uint64(row[6]); - Vols[i].EndBlock = str_to_uint64(row[7]); + StartFile = str_to_uint64(row[4]); + EndFile = str_to_uint64(row[5]); + StartBlock = str_to_uint64(row[6]); + EndBlock = str_to_uint64(row[7]); + Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock; + Vols[i].EndAddr = (((uint64_t)EndFile)<<32) | EndBlock; // Vols[i].Copy = str_to_uint64(row[8]); -// Vols[i].Stripe = str_to_uint64(row[9]); + Vols[i].Slot = str_to_uint64(row[9]); + StorageId = str_to_uint64(row[10]); + Vols[i].InChanger = str_to_uint64(row[11]); + Vols[i].Storage[0] = 0; + SId[i] = StorageId; } } + for (i=0; i < stat; i++) { + if (SId[i] != 0) { + Mmsg(mdb->cmd, "SELECT Name from Storage WHERE StorageId=%s", + edit_int64(SId[i], ed1)); + if (QUERY_DB(jcr, mdb, mdb->cmd)) { + if ((row = sql_fetch_row(mdb)) && row[0]) { + bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH); + } + } + } + } + if (SId) { + free(SId); + } } sql_free_result(mdb); } @@ -541,16 +609,15 @@ bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr) Mmsg(mdb->cmd, "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume," "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles," -"MaxVolBytes,PoolType,LabelType,LabelFormat FROM Pool WHERE Pool.PoolId=%s", +"MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId FROM Pool WHERE Pool.PoolId=%s", edit_int64(pdbr->PoolId, ed1)); } else { /* find by name */ Mmsg(mdb->cmd, "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume," "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles," -"MaxVolBytes,PoolType,LabelType,LabelFormat FROM Pool WHERE Pool.Name='%s'", +"MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId FROM Pool WHERE Pool.Name='%s'", pdbr->Name); } - if (QUERY_DB(jcr, mdb, mdb->cmd)) { mdb->num_rows = sql_num_rows(mdb); if (mdb->num_rows > 1) { @@ -580,6 +647,8 @@ bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr) bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType)); pdbr->LabelType = str_to_int64(row[15]); bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat)); + pdbr->RecyclePoolId = str_to_int64(row[17]); + pdbr->ScratchPoolId = str_to_int64(row[18]); ok = true; } } @@ -779,13 +848,13 @@ int db_get_num_media_records(JCR *jcr, B_DB *mdb) /* * This function returns a list of all the Media record ids for - * the current Pool. + * the current Pool with the correct Media Type. * The caller must free ids if non-NULL. * * Returns false: on failure * true: on success */ -bool db_get_media_ids(JCR *jcr, B_DB *mdb, uint32_t PoolId, int *num_ids, uint32_t *ids[]) +bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[]) { SQL_ROW row; int i = 0; @@ -795,8 +864,9 @@ bool db_get_media_ids(JCR *jcr, B_DB *mdb, uint32_t PoolId, int *num_ids, uint32 db_lock(mdb); *ids = NULL; - Mmsg(mdb->cmd, "SELECT MediaId FROM Media WHERE PoolId=%s", - edit_int64(PoolId, ed1)); + Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE PoolId=%s " + " AND MediaType='%s'", + edit_int64(mr->PoolId, ed1), mr->MediaType); if (QUERY_DB(jcr, mdb, mdb->cmd)) { *num_ids = sql_num_rows(mdb); if (*num_ids > 0) { @@ -818,6 +888,43 @@ bool db_get_media_ids(JCR *jcr, B_DB *mdb, uint32_t PoolId, int *num_ids, uint32 } +/* + * This function returns a list of all the DBIds that are returned + * for the query. + * + * Returns false: on failure + * true: on success + */ +bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids) +{ + SQL_ROW row; + int i = 0; + bool ok = false; + + db_lock(mdb); + ids.num_ids = 0; + if (QUERY_DB(jcr, mdb, query.c_str())) { + ids.num_ids = sql_num_rows(mdb); + if (ids.num_ids > 0) { + if (ids.max_ids < ids.num_ids) { + free(ids.DBId); + ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t)); + } + while ((row = sql_fetch_row(mdb)) != NULL) { + ids.DBId[i++] = str_to_uint64(row[0]); + } + } + sql_free_result(mdb); + ok = true; + } else { + Mmsg(mdb->errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror(mdb)); + Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg); + ok = false; + } + db_unlock(mdb); + return ok; +} + /* Get Media Record * * Returns: false: on failure @@ -841,7 +948,9 @@ bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs," "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," - "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId " + "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId," + "Enabled,LocationId,RecycleCount,InitialWrite," + "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime " "FROM Media WHERE MediaId=%s", edit_int64(mr->MediaId, ed1)); } else { /* find by name */ @@ -849,7 +958,9 @@ bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs," "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," - "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId " + "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId," + "Enabled,LocationId,RecycleCount,InitialWrite," + "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime " "FROM Media WHERE VolumeName='%s'", mr->VolumeName); } @@ -898,6 +1009,16 @@ bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate)); mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate); mr->StorageId = str_to_int64(row[28]); + mr->Enabled = str_to_int64(row[29]); + mr->LocationId = str_to_int64(row[30]); + mr->RecycleCount = str_to_int64(row[31]); + bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite)); + mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite); + mr->ScratchPoolId = str_to_int64(row[33]); + mr->RecyclePoolId = str_to_int64(row[34]); + mr->VolReadTime = str_to_int64(row[35]); + mr->VolWriteTime = str_to_int64(row[36]); + ok = true; } } else { @@ -922,5 +1043,236 @@ bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) return ok; } +/* + * Find the last "accurate" backup state (that can take deleted files in + * account) + * 1) Get all files with jobid in list (F subquery) + * Get all files in BaseFiles with jobid in list + * 2) Take only the last version of each file (Temp subquery) => accurate list + * is ok + * 3) Join the result to file table to get fileindex, jobid and lstat information + * + * TODO: See if we can do the SORT only if needed (as an argument) + */ +bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, + DB_RESULT_HANDLER *result_handler, void *ctx) +{ + if (!*jobids) { + db_lock(mdb); + Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n")); + db_unlock(mdb); + return false; + } + POOL_MEM buf(PM_MESSAGE); + +#define new_db_get_file_list +#ifdef new_db_get_file_list + POOL_MEM buf2(PM_MESSAGE); + Mmsg(buf2, select_recent_version_with_basejob[db_type], + jobids, jobids, jobids); + Mmsg(buf, + "SELECT Path.Path, Filename.Name, FileIndex, JobId, " + "LStat, MD5 " + "FROM ( %s ) AS Temp " + "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) " + "JOIN Path ON (Path.PathId = Temp.PathId) " +"WHERE FileIndex > 0 ORDER BY JobId, FileIndex ASC",/* Return sorted by JobId, */ + /* FileIndex for restore code */ + buf2.c_str()); + Dmsg1(0, "sql=%s\n", buf.c_str()); +#else + /* + * I am not sure that this works the same as the code in ua_restore.c but it + * is very similar. The accurate-test fails in a restore. Bad file count. + */ + Mmsg(buf, uar_sel_files, jobids); +#endif + + return db_sql_query(mdb, buf.c_str(), result_handler, ctx); +} + +/* + * This procedure gets the base jobid list used by jobids, + */ +bool db_get_used_base_jobids(JCR *jcr, B_DB *mdb, + POOLMEM *jobids, db_list_ctx *result) +{ + POOL_MEM buf; + Mmsg(buf, + "SELECT DISTINCT BaseJobId " + " FROM Job JOIN BaseFiles USING (JobId) " + " WHERE Job.HasBase = 1 " + " AND JobId IN (%s) ", jobids); + return db_sql_query(mdb, buf.c_str(), db_list_handler, result); +} + +/* The decision do change an incr/diff was done before + * Full : do nothing + * Differential : get the last full id + * Incremental : get the last full + last diff + last incr(s) ids + * + * If you specify jr->StartTime, it will be used to limit the search + * in the time. (usually now) + * + * TODO: look and merge from ua_restore.c + */ +bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, + JOB_DBR *jr, db_list_ctx *jobids) +{ + bool ret=false; + char clientid[50], jobid[50], filesetid[50]; + char date[MAX_TIME_LENGTH]; + POOL_MEM query(PM_FNAME); + + /* Take the current time as upper limit if nothing else specified */ + utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL); + + bstrutime(date, sizeof(date), StartTime + 1); + jobids->list[0] = 0; + jobids->count = 0; + + /* First, find the last good Full backup for this job/client/fileset */ + Mmsg(query, +"CREATE TABLE btemp3%s AS " + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles " + "FROM Job JOIN FileSet USING (FileSetId) " + "WHERE ClientId = %s " + "AND Level='F' AND JobStatus IN ('T','W') AND Type='B' " + "AND StartTime<'%s' " + "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) " + "ORDER BY Job.JobTDate DESC LIMIT 1", + edit_uint64(jcr->JobId, jobid), + edit_uint64(jr->ClientId, clientid), + date, + edit_uint64(jr->FileSetId, filesetid)); + + if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) { + goto bail_out; + } + + if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) { + /* Now, find the last differential backup after the last full */ + Mmsg(query, +"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) " + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles " + "FROM Job JOIN FileSet USING (FileSetId) " + "WHERE ClientId = %s " + "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' " + "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) " + "AND StartTime < '%s' " + "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) " + "ORDER BY Job.JobTDate DESC LIMIT 1 ", + jobid, + clientid, + jobid, + date, + filesetid); + + if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) { + goto bail_out; + } + + /* We just have to take all incremental after the last Full/Diff */ + Mmsg(query, +"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) " + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles " + "FROM Job JOIN FileSet USING (FileSetId) " + "WHERE ClientId = %s " + "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' " + "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) " + "AND StartTime < '%s' " + "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) " + "ORDER BY Job.JobTDate DESC ", + jobid, + clientid, + jobid, + date, + filesetid); + if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) { + goto bail_out; + } + } + + /* build a jobid list ie: 1,2,3,4 */ + Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid); + db_sql_query(mdb, query.c_str(), db_list_handler, jobids); + Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list); + ret = true; + +bail_out: + Mmsg(query, "DROP TABLE btemp3%s", jobid); + db_sql_query(mdb, query.c_str(), NULL, NULL); + + return ret; +} + +bool db_get_base_file_list(JCR *jcr, B_DB *mdb, + DB_RESULT_HANDLER *result_handler, void *ctx) +{ + POOL_MEM buf(PM_MESSAGE); + + Mmsg(buf, + "SELECT Path, Name, FileIndex, JobId, LStat, MD5 " + "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC", + (uint64_t) jcr->JobId); + + return db_sql_query(mdb, buf.c_str(), result_handler, ctx); +} + +bool db_get_base_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr, JobId_t *jobid) +{ + char date[MAX_TIME_LENGTH]; + int64_t id = *jobid = 0; + POOL_MEM query(PM_FNAME); + +// char clientid[50], filesetid[50]; + + utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL); + bstrutime(date, sizeof(date), StartTime + 1); + + /* we can take also client name, fileset, etc... */ + + Mmsg(query, + "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles " + "FROM Job " +// "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) " + "WHERE Job.Name = '%s' " + "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' " +// "AND FileSet.FileSet= '%s' " +// "AND Client.Name = '%s' " + "AND StartTime<'%s' " + "ORDER BY Job.JobTDate DESC LIMIT 1", + jr->Name, +// edit_uint64(jr->ClientId, clientid), +// edit_uint64(jr->FileSetId, filesetid)); + date); + + Dmsg1(10, "db_get_base_jobid q=%s\n", query.c_str()); + if (!db_sql_query(mdb, query.c_str(), db_int64_handler, &id)) { + goto bail_out; + } + *jobid = (JobId_t) id; + + Dmsg1(10, "db_get_base_jobid=%lld\n", id); + return true; + +bail_out: + return false; +} + +/* + * Use to build a string of int list from a query. "10,20,30" + */ +int db_get_int_handler(void *ctx, int num_fields, char **row) +{ + POOLMEM *ret = (POOLMEM *)ctx; + if (num_fields == 1) { + if (ret[0]) { + pm_strcat(ret, ","); + } + pm_strcat(ret, row[0]); + } + return 0; +} -#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/ +#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */