]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/cats/bvfs.c
Add %D option to edit_job_code, simplify callbacks on director side
[bacula/bacula] / bacula / src / cats / bvfs.c
index 98603b2ec6c202098a63aa1ad0a95183039bd6e5..9131a00baf5c79a8bb1112c7070d49bb9e767d4d 100644 (file)
    Switzerland, email:ftf@fsfeurope.org.
 */
 
-#define __SQL_C                       /* indicate that this is sql.c */
-
 #include "bacula.h"
-#include "cats/cats.h"
+
+#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
+
+#include "cats.h"
+#include "bdb_priv.h"
+#include "sql_glue.h"
 #include "lib/htable.h"
 #include "bvfs.h"
 
@@ -68,16 +71,59 @@ Bvfs::Bvfs(JCR *j, B_DB *mdb) {
    attr = new_attr(jcr);
    list_entries = result_handler;
    user_data = this;
+   username = NULL;
 }
 
 Bvfs::~Bvfs() {
    free_pool_memory(jobids);
    free_pool_memory(pattern);
    free_pool_memory(prev_dir);
+   if (username) {
+      free(username);
+   }
    free_attr(attr);
    jcr->dec_use_count();
 }
 
+void Bvfs::filter_jobid()
+{
+   if (!username) {
+      return;
+   }
+
+   /* Query used by Bweb to filter clients, activated when using
+    * set_username() 
+    */
+   POOL_MEM query;
+   Mmsg(query,
+      "SELECT DISTINCT JobId FROM Job JOIN Client USING (ClientId) "
+        "JOIN (SELECT ClientId FROM client_group_member "
+        "JOIN client_group USING (client_group_id) "
+        "JOIN bweb_client_group_acl USING (client_group_id) "
+        "JOIN bweb_user USING (userid) "
+       "WHERE bweb_user.username = '%s' "
+      ") AS filter USING (ClientId) "
+        " WHERE JobId IN (%s)", 
+        username, jobids);
+
+   db_list_ctx ctx;
+   Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
+   db_sql_query(db, query.c_str(), db_list_handler, &ctx);
+   pm_strcpy(jobids, ctx.list);
+}
+
+void Bvfs::set_jobid(JobId_t id)
+{
+   Mmsg(jobids, "%lld", (uint64_t)id);
+   filter_jobid();
+}
+
+void Bvfs::set_jobids(char *ids)
+{
+   pm_strcpy(jobids, ids);
+   filter_jobid();
+}
+
 /* 
  * TODO: Find a way to let the user choose how he wants to display
  * files and directories
@@ -153,6 +199,15 @@ char *bvfs_parent_dir(char *path)
    char *p = path;
    int len = strlen(path) - 1;
 
+   /* windows directory / */
+   if (len == 2 && B_ISALPHA(path[0]) 
+                && path[1] == ':' 
+                && path[2] == '/')
+   {
+      len = 0;
+      path[0] = '\0';
+   }
+
    if (len >= 0 && path[len] == '/') {      /* if directory, skip last / */
       path[len] = '\0';
    }
@@ -285,7 +340,8 @@ static void update_path_hierarchy_cache(JCR *jcr,
                    "SELECT DISTINCT PathId, JobId "
                      "FROM (SELECT PathId, JobId FROM File WHERE JobId = %s "
                            "UNION "
-                           "SELECT PathId, BaseFiles.JobId FROM BaseFiles JOIN File AS F USING (FileId) "
+                           "SELECT PathId, BaseFiles.JobId "
+                             "FROM BaseFiles JOIN File AS F USING (FileId) "
                             "WHERE BaseFiles.JobId = %s) AS B",
         jobid, jobid);
    QUERY_DB(jcr, mdb, mdb->cmd);
@@ -331,7 +387,17 @@ static void update_path_hierarchy_cache(JCR *jcr,
       free(result);
    }
 
-   Mmsg(mdb->cmd, 
+   if (mdb->db_get_type_index() == SQL_TYPE_SQLITE3) {
+      Mmsg(mdb->cmd, 
+ "INSERT INTO PathVisibility (PathId, JobId) "
+   "SELECT DISTINCT h.PPathId AS PathId, %s "
+     "FROM PathHierarchy AS h "
+    "WHERE h.PathId IN (SELECT PathId FROM PathVisibility WHERE JobId=%s) "
+      "AND h.PPathId NOT IN (SELECT PathId FROM PathVisibility WHERE JobId=%s)",
+           jobid, jobid, jobid );
+
+   } else {
+      Mmsg(mdb->cmd, 
   "INSERT INTO PathVisibility (PathId, JobId)  "
    "SELECT a.PathId,%s "
    "FROM ( "
@@ -343,6 +409,7 @@ static void update_path_hierarchy_cache(JCR *jcr,
           "FROM PathVisibility "
          "WHERE JobId=%s) AS b ON (a.PathId = b.PathId) "
    "WHERE b.PathId IS NULL",  jobid, jobid, jobid);
+   }
 
    do {
       QUERY_DB(jcr, mdb, mdb->cmd);
@@ -378,7 +445,6 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb)
    db_list_ctx jobids_list;
 
    db_lock(mdb);
-   db_start_transaction(jcr, mdb);
 
 #ifdef xxx
    /* TODO: Remove this code when updating make_bacula_table script */
@@ -429,7 +495,6 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb)
 
    bvfs_update_path_hierarchy_cache(jcr, mdb, jobids_list.list);
 
-   db_end_transaction(jcr, mdb);
    db_start_transaction(jcr, mdb);
    Dmsg0(dbglevel, "Cleaning pathvisibility\n");
    Mmsg(mdb->cmd, 
@@ -485,6 +550,7 @@ bool Bvfs::ch_dir(const char *path)
 
 /* 
  * Get all file versions for a specified client
+ * TODO: Handle basejobs using different client
  */
 void Bvfs::get_all_file_versions(DBId_t pathid, DBId_t fnid, const char *client)
 {
@@ -510,36 +576,11 @@ void Bvfs::get_all_file_versions(DBId_t pathid, DBId_t fnid, const char *client)
 "WHERE File.FilenameId = %s "
   "AND File.PathId=%s "
   "AND File.JobId = Job.JobId "
-  "AND Job.ClientId = Client.ClientId "
   "AND Job.JobId = JobMedia.JobId "
   "AND File.FileIndex >= JobMedia.FirstIndex "
   "AND File.FileIndex <= JobMedia.LastIndex "
   "AND JobMedia.MediaId = Media.MediaId "
-  "AND Client.Name = '%s' "
-  "%s ORDER BY FileId LIMIT %d OFFSET %d"
-        ,edit_uint64(fnid, ed1), edit_uint64(pathid, ed2), client, q.c_str(),
-        limit, offset);
-   Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
-   db_sql_query(db, query.c_str(), list_entries, user_data);
-
-   /* Handle BaseJobs version */
-   Mmsg(query,//    1           2              3       
-"SELECT 'V', File.PathId, File.FilenameId,  File.Md5, "
-//         4          5           6
-        "File.JobId, File.LStat, File.FileId, "
-//         7                    8
-       "Media.VolumeName, Media.InChanger "
-"FROM File, Job, Client, JobMedia, Media, BaseFiles "
-"WHERE File.FilenameId = %s "
-  "AND File.PathId = %s "
-  "AND Job.JobId = BaseFiles.JobId "
-  "AND File.JobId = BaseFiles.BaseJobId "
-  "AND File.FileId = BaseFiles.FileId "
   "AND Job.ClientId = Client.ClientId "
-  "AND Job.JobId = JobMedia.JobId "
-  "AND File.FileIndex >= JobMedia.FirstIndex "
-  "AND File.FileIndex <= JobMedia.LastIndex "
-  "AND JobMedia.MediaId = Media.MediaId "
   "AND Client.Name = '%s' "
   "%s ORDER BY FileId LIMIT %d OFFSET %d"
         ,edit_uint64(fnid, ed1), edit_uint64(pathid, ed2), client, q.c_str(),
@@ -591,11 +632,11 @@ void Bvfs::ls_special_dirs()
 
    POOL_MEM query;
    Mmsg(query, 
-"((SELECT PPathId AS PathId, '..' AS Path "
+"(SELECT PPathId AS PathId, '..' AS Path "
     "FROM  PathHierarchy "
-   "WHERE  PathId = %s) "
+   "WHERE  PathId = %s "
 "UNION "
- "(SELECT %s AS PathId, '.' AS Path))",
+ "SELECT %s AS PathId, '.' AS Path)",
         edit_uint64(pwd_id, ed1), ed1);
 
    POOL_MEM query2;
@@ -623,9 +664,11 @@ bool Bvfs::ls_dirs()
       return false;
    }
 
+   POOL_MEM query;
    POOL_MEM filter;
    if (*pattern) {
-      Mmsg(filter, " AND Path2.Path %s '%s' ", SQL_MATCH, pattern);
+      Mmsg(filter, " AND Path2.Path %s '%s' ", 
+           match_query[db_get_type_index(db)], pattern);
    }
 
    if (!dir_filenameid) {
@@ -641,7 +684,6 @@ bool Bvfs::ls_dirs()
     * my $dir_filenameid = $self->get_dir_filenameid();
     */
    /* Then we get all the dir entries from File ... */
-   POOL_MEM query;
    Mmsg(query,
 //       0     1     2   3      4     5       6
 "SELECT 'D', PathId, 0, Path, JobId, LStat, FileId FROM ( "
@@ -657,7 +699,7 @@ bool Bvfs::ls_dirs()
       "JOIN PathVisibility AS PathVisibility1 "
         "ON (PathHierarchy1.PathId = PathVisibility1.PathId) "
       "WHERE PathHierarchy1.PPathId = %s "
-      "AND PathVisibility1.jobid IN (%s) "
+      "AND PathVisibility1.JobId IN (%s) "
            "%s "
      ") AS listpath1 "
    "JOIN Path AS Path1 ON (listpath1.PathId = Path1.PathId) "
@@ -680,17 +722,35 @@ bool Bvfs::ls_dirs()
 
    db_lock(db);
    db_sql_query(db, query.c_str(), path_handler, this);
-   nb_record = db->num_rows;
+   nb_record = sql_num_rows(db);
    db_unlock(db);
 
    return nb_record == limit;
 }
 
+void build_ls_files_query(B_DB *db, POOL_MEM &query, 
+                          const char *JobId, const char *PathId,  
+                          const char *filter, int64_t limit, int64_t offset)
+{
+   if (db_get_type_index(db) == SQL_TYPE_POSTGRESQL) {
+      Mmsg(query, sql_bvfs_list_files[db_get_type_index(db)], 
+           JobId, PathId, JobId, PathId, 
+           filter, limit, offset);
+   } else {
+      Mmsg(query, sql_bvfs_list_files[db_get_type_index(db)], 
+           JobId, PathId, JobId, PathId, 
+           limit, offset, filter, JobId, JobId);
+   }
+}
+
 /* Returns true if we have files to read */
 bool Bvfs::ls_files()
 {
+   POOL_MEM query;
+   POOL_MEM filter;
+   char pathid[50];
+
    Dmsg1(dbglevel, "ls_files(%lld)\n", (uint64_t)pwd_id);
-   char ed1[50];
    if (*jobids == 0) {
       return false;
    }
@@ -699,37 +759,21 @@ bool Bvfs::ls_files()
       ch_dir(get_root());
    }
 
-   POOL_MEM filter;
+   edit_uint64(pwd_id, pathid);
    if (*pattern) {
-      Mmsg(filter, " AND Filename.Name %s '%s' ", SQL_MATCH, pattern);
+      Mmsg(filter, " AND Filename.Name %s '%s' ", 
+           match_query[db_get_type_index(db)], pattern);
    }
-   /* TODO: Use JobTDate instead of FileId to determine the latest version */
-   POOL_MEM query;
-   Mmsg(query, //    1              2             3          4
-"SELECT 'F', File.PathId, File.FilenameId, listfiles.Name, File.JobId, "
-        "File.LStat, listfiles.id "
-"FROM File, ( "
-       "SELECT Filename.Name as Name, max(File.FileId) as id "
-         "FROM File, Filename "
-        "WHERE File.FilenameId = Filename.FilenameId "
-          "AND Filename.Name != '' "
-          "AND File.PathId = %s "
-          "AND File.JobId IN (%s) "
-          "%s "
-        "GROUP BY Filename.Name "
-        "ORDER BY Filename.Name LIMIT %d OFFSET %d "
-     ") AS listfiles "
-"WHERE File.FileId = listfiles.id",
-        edit_uint64(pwd_id, ed1),
-        jobids,
-        filter.c_str(),
-        limit,
-        offset);
+
+   build_ls_files_query(db, query, 
+                        jobids, pathid, filter.c_str(),
+                        limit, offset);
+
    Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
 
    db_lock(db);
    db_sql_query(db, query.c_str(), list_entries, user_data);
-   nb_record = db->num_rows;
+   nb_record = sql_num_rows(db);
    db_unlock(db);
 
    return nb_record == limit;
@@ -790,6 +834,15 @@ static bool check_temp(char *output_table)
    return false;
 }
 
+void Bvfs::clear_cache()
+{
+   db_sql_query(db, "BEGIN",                     NULL, NULL);
+   db_sql_query(db, "UPDATE Job SET HasCache=0", NULL, NULL);
+   db_sql_query(db, "TRUNCATE PathHierarchy",    NULL, NULL);
+   db_sql_query(db, "TRUNCATE PathVisibility",   NULL, NULL);
+   db_sql_query(db, "COMMIT",                    NULL, NULL);
+}
+
 bool Bvfs::drop_restore_list(char *output_table)
 {
    POOL_MEM query;
@@ -821,24 +874,27 @@ bool Bvfs::compute_restore_list(char *fileid, char *dirid, char *hardlink,
       return false;
    }
 
-   Mmsg(query, "CREATE TEMPORARY TABLE btemp%s AS ", output_table);
+   Mmsg(query, "CREATE TABLE btemp%s AS ", output_table);
 
-   if (*fileid) {
+   if (*fileid) {               /* Select files with their direct id */
       init=true;
-      Mmsg(tmp, "(SELECT JobId, FileIndex, FilenameId, PathId, FileId "
-                   "FROM File WHERE FileId IN (%s))", fileid);
+      Mmsg(tmp,"SELECT JobId, JobTDate, FileIndex, FilenameId, PathId, FileId "
+                  "FROM File JOIN Job USING (JobId) WHERE FileId IN (%s)",
+           fileid);
       pm_strcat(query, tmp.c_str());
    }
 
+   /* Add a directory content */
    while (get_next_id_from_list(&dirid, &id) == 1) {
       Mmsg(tmp, "SELECT Path FROM Path WHERE PathId=%lld", id);
       
       if (!db_sql_query(db, tmp.c_str(), get_path_handler, (void *)&tmp2)) {
+         Dmsg0(dbglevel, "Can't search for path\n");
          /* print error */
          return false;
       }
       if (!strcmp(tmp2.c_str(), "")) { /* path not found */
-         Dmsg3(0, "Path not found %lld q=%s s=%s\n",
+         Dmsg3(dbglevel, "Path not found %lld q=%s s=%s\n",
                id, tmp.c_str(), tmp2.c_str());
          break;
       }
@@ -863,19 +919,34 @@ bool Bvfs::compute_restore_list(char *fileid, char *dirid, char *hardlink,
       if (init) {
          query.strcat(" UNION ");
       }
-      Mmsg(tmp, "(SELECT File.JobId, File.FileIndex, File.FilenameId, "
+
+      Mmsg(tmp, "SELECT JobId, JobTDate, File.FileIndex, File.FilenameId, "
                         "File.PathId, FileId "
-                   "FROM Path JOIN File USING (PathId) "
-                  "WHERE Path.Path LIKE '%s' AND File.JobId IN (%s)) ", 
+                   "FROM Path JOIN File USING (PathId) JOIN Job USING (JobId) "
+                  "WHERE Path.Path LIKE '%s' AND File.JobId IN (%s) ", 
            tmp2.c_str(), jobids); 
       query.strcat(tmp.c_str());
       init = true;
+
+      query.strcat(" UNION ");
+
+      /* A directory can have files from a BaseJob */
+      Mmsg(tmp, "SELECT File.JobId, JobTDate, BaseFiles.FileIndex, "
+                        "File.FilenameId, File.PathId, BaseFiles.FileId "
+                   "FROM BaseFiles "
+                        "JOIN File USING (FileId) "
+                        "JOIN Job ON (BaseFiles.JobId = Job.JobId) "
+                        "JOIN Path USING (PathId) "
+                  "WHERE Path.Path LIKE '%s' AND BaseFiles.JobId IN (%s) ", 
+           tmp2.c_str(), jobids); 
+      query.strcat(tmp.c_str());
    }
 
    /* expect jobid,fileindex */
    int64_t prev_jobid=0;
    while (get_next_id_from_list(&hardlink, &jobid) == 1) {
       if (get_next_id_from_list(&hardlink, &id) != 1) {
+         Dmsg0(dbglevel, "hardlink should be two by two\n");
          return false;
       }
       if (jobid != prev_jobid) { /* new job */
@@ -884,11 +955,12 @@ bool Bvfs::compute_restore_list(char *fileid, char *dirid, char *hardlink,
                query.strcat(" UNION ");
             }
          } else {               /* end last job, start new one */
-            tmp.strcat(")) UNION ");
+            tmp.strcat(") UNION ");
             query.strcat(tmp.c_str());
          }
-         Mmsg(tmp, "(SELECT JobId, FileIndex, FilenameId, PathId, FileId "
-                       "FROM File WHERE JobId = %lld " 
+         Mmsg(tmp,   "SELECT JobId, JobTDate, FileIndex, FilenameId, "
+                            "PathId, FileId "
+                       "FROM File JOIN Job USING (JobId) WHERE JobId = %lld " 
                         "AND FileIndex IN (%lld", jobid, id);
          prev_jobid = jobid;
 
@@ -899,30 +971,39 @@ bool Bvfs::compute_restore_list(char *fileid, char *dirid, char *hardlink,
    }
 
    if (prev_jobid != 0) {       /* end last job */
-      tmp.strcat(")) ");
+      tmp.strcat(") ");
       query.strcat(tmp.c_str());
       init = true;
    }
 
-   Dmsg1(0, "q=%s\n", query.c_str());
+   Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
 
    if (!db_sql_query(db, query.c_str(), NULL, NULL)) {
+      Dmsg0(dbglevel, "Can't execute q\n");
       goto bail_out;
    }
 
-   Mmsg(query, "CREATE TABLE %s AS ( "
-        "SELECT JobId, FileIndex, FileId "
-          "FROM ( "
-     "SELECT DISTINCT ON (PathId, FilenameId) JobId, FileIndex, FileId "
-       "FROM btemp%s "
-      "ORDER BY PathId, FilenameId, JobId DESC "
-          ") AS T "
-          "WHERE FileIndex > 0)", output_table, output_table);
+   /* TODO: handle basejob and SQLite3 */
+   Mmsg(query, sql_bvfs_select[db_get_type_index(db)], output_table, output_table);
 
-   Dmsg1(0, "q=%s\n", query.c_str());
+   /* TODO: handle jobid filter */
+   Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
    if (!db_sql_query(db, query.c_str(), NULL, NULL)) {
+      Dmsg0(dbglevel, "Can't execute q\n");
       goto bail_out;
    }
+
+   /* MySQL need it */
+   if (db_get_type_index(db) == SQL_TYPE_MYSQL) {
+      Mmsg(query, "CREATE INDEX idx_%s ON b2%s (JobId)", 
+           output_table, output_table);
+      Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
+      if (!db_sql_query(db, query.c_str(), NULL, NULL)) {
+         Dmsg0(dbglevel, "Can't execute q\n");
+         goto bail_out;
+      }
+   }
+
    ret = true;
 
 bail_out:
@@ -930,3 +1011,5 @@ bail_out:
    db_sql_query(db, query.c_str(), NULL, NULL);
    return ret;
 }
+
+#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */