/*
Bacula® - The Network Backup Solution
- Copyright (C) 2009-2009 Free Software Foundation Europe e.V.
+ Copyright (C) 2009-2010 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
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation, which is
listed in the file LICENSE.
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
+ You should have received a copy of the GNU Affero 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.
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"
#define dbglevel 10
#define dbglevel_sql 15
+static int result_handler(void *ctx, int fields, char **row)
+{
+ if (fields == 4) {
+ Pmsg4(0, "%s\t%s\t%s\t%s\n",
+ row[0], row[1], row[2], row[3]);
+ } else if (fields == 5) {
+ Pmsg5(0, "%s\t%s\t%s\t%s\t%s\n",
+ row[0], row[1], row[2], row[3], row[4]);
+ } else if (fields == 6) {
+ Pmsg6(0, "%s\t%s\t%s\t%s\t%s\t%s\n",
+ row[0], row[1], row[2], row[3], row[4], row[5]);
+ } else if (fields == 7) {
+ Pmsg7(0, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
+ row[0], row[1], row[2], row[3], row[4], row[5], row[6]);
+ }
+ return 0;
+}
+
+Bvfs::Bvfs(JCR *j, B_DB *mdb) {
+ jcr = j;
+ jcr->inc_use_count();
+ db = mdb; /* need to inc ref count */
+ jobids = get_pool_memory(PM_NAME);
+ prev_dir = get_pool_memory(PM_NAME);
+ pattern = get_pool_memory(PM_NAME);
+ *jobids = *prev_dir = *pattern = 0;
+ dir_filenameid = pwd_id = offset = 0;
+ see_copies = see_all_versions = false;
+ limit = 1000;
+ 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
hlink *nodes;
int nb_node;
int max_node;
+
+ alist *table_node;
+
htable *cache_ppathid;
public:
max_node = NITEMS;
nodes = (hlink *) malloc(max_node * sizeof (hlink));
nb_node = 0;
+ table_node = New(alist(5, owned_by_alist));
+ table_node->append(nodes);
}
hlink *get_hlink() {
- if (nb_node >= max_node) {
- max_node *= 2;
- nodes = (hlink *)brealloc(nodes, sizeof(hlink) * max_node);
+ if (++nb_node >= max_node) {
+ nb_node = 0;
+ nodes = (hlink *)malloc(max_node * sizeof(hlink));
+ table_node->append(nodes);
}
- return nodes + nb_node++;
+ return nodes + nb_node;
}
bool lookup(char *pathid) {
~pathid_cache() {
cache_ppathid->destroy();
free(cache_ppathid);
- free(nodes);
+ delete table_node;
}
+private:
+ pathid_cache(const pathid_cache &); /* prohibit pass by value */
+ pathid_cache &operator= (const pathid_cache &);/* prohibit class assignment*/
} ;
/* Return the parent_dir with the trailing / (update the given string)
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';
}
return path;
}
-
-
-/* Return the basename of the with the trailing / (update the given string)
+/* Return the basename of the with the trailing /
* TODO: see in the rest of bacula if we don't have
* this function already
*/
while (p > path && !IsPathSeparator(*p)) {
p--;
}
- p = p+1; /* skip first / */
+ if (*p == '/') {
+ p++; /* skip first / */
+ }
}
return p;
}
/* Does the ppathid exist for this ? we use a memory cache... In order to
* avoid the full loop, we consider that if a dir is allready in the
- * brestore_pathhierarchy table, then there is no need to calculate all the
+ * PathHierarchy table, then there is no need to calculate all the
* hierarchy
*/
while (path && *path)
if (!ppathid_cache.lookup(pathid))
{
Mmsg(mdb->cmd,
- "SELECT PPathId FROM brestore_pathhierarchy WHERE PathId = %s",
+ "SELECT PPathId FROM PathHierarchy WHERE PathId = %s",
pathid);
QUERY_DB(jcr, mdb, mdb->cmd);
ppathid_cache.insert(pathid);
Mmsg(mdb->cmd,
- "INSERT INTO brestore_pathhierarchy (PathId, PPathId) "
+ "INSERT INTO PathHierarchy (PathId, PPathId) "
"VALUES (%s,%lld)",
pathid, (uint64_t) parent.PathId);
path = mdb->path; /* already done */
}
} else {
- /* It's allready in the cache. We can leave, no time to waste here,
+ /* It's already in the cache. We can leave, no time to waste here,
* all the parent dirs have allready been done
*/
goto bail_out;
db_lock(mdb);
db_start_transaction(jcr, mdb);
- Mmsg(mdb->cmd, "SELECT 1 FROM brestore_knownjobid WHERE JobId = %s", jobid);
+ Mmsg(mdb->cmd, "SELECT 1 FROM Job WHERE JobId = %s AND HasCache=1", jobid);
if (!QUERY_DB(jcr, mdb, mdb->cmd) || sql_num_rows(mdb) > 0) {
Dmsg1(dbglevel, "already computed %d\n", (uint32_t)JobId );
}
/* Inserting path records for JobId */
- Mmsg(mdb->cmd, "INSERT INTO brestore_pathvisibility (PathId, JobId) "
- "SELECT DISTINCT PathId, JobId FROM File WHERE JobId = %s",
- jobid);
+ Mmsg(mdb->cmd, "INSERT INTO PathVisibility (PathId, JobId) "
+ "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) "
+ "WHERE BaseFiles.JobId = %s) AS B",
+ jobid, jobid);
QUERY_DB(jcr, mdb, mdb->cmd);
-
/* Now we have to do the directory recursion stuff to determine missing
* visibility We try to avoid recursion, to be as fast as possible We also
* only work on not allready hierarchised directories...
*/
Mmsg(mdb->cmd,
- "SELECT brestore_pathvisibility.PathId, Path "
- "FROM brestore_pathvisibility "
- "JOIN Path ON( brestore_pathvisibility.PathId = Path.PathId) "
- "LEFT JOIN brestore_pathhierarchy "
- "ON (brestore_pathvisibility.PathId = brestore_pathhierarchy.PathId) "
- "WHERE brestore_pathvisibility.JobId = %s "
- "AND brestore_pathhierarchy.PathId IS NULL "
+ "SELECT PathVisibility.PathId, Path "
+ "FROM PathVisibility "
+ "JOIN Path ON( PathVisibility.PathId = Path.PathId) "
+ "LEFT JOIN PathHierarchy "
+ "ON (PathVisibility.PathId = PathHierarchy.PathId) "
+ "WHERE PathVisibility.JobId = %s "
+ "AND PathHierarchy.PathId IS NULL "
"ORDER BY Path", jobid);
Dmsg1(dbglevel_sql, "q=%s\n", mdb->cmd);
QUERY_DB(jcr, mdb, mdb->cmd);
}
free(result);
}
-
- Mmsg(mdb->cmd,
- "INSERT INTO brestore_pathvisibility (PathId, JobId) "
+
+ 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 ( "
"SELECT DISTINCT h.PPathId AS PathId "
- "FROM brestore_pathhierarchy AS h "
- "JOIN brestore_pathvisibility AS p ON (h.PathId=p.PathId) "
+ "FROM PathHierarchy AS h "
+ "JOIN PathVisibility AS p ON (h.PathId=p.PathId) "
"WHERE p.JobId=%s) AS a LEFT JOIN "
"(SELECT PathId "
- "FROM brestore_pathvisibility "
+ "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);
} while (sql_affected_rows(mdb) > 0);
- Mmsg(mdb->cmd, "INSERT INTO brestore_knownjobid (JobId) VALUES (%s)", jobid);
- INSERT_DB(jcr, mdb, mdb->cmd);
+ Mmsg(mdb->cmd, "UPDATE Job SET HasCache=1 WHERE JobId=%s", jobid);
+ UPDATE_DB(jcr, mdb, mdb->cmd);
bail_out:
db_end_transaction(jcr, mdb);
db_unlock(mdb);
}
-
/*
* Find an store the filename descriptor for empty directories Filename.Name=''
*/
void bvfs_update_cache(JCR *jcr, B_DB *mdb)
{
- uint32_t nb;
+ uint32_t nb=0;
+ db_list_ctx jobids_list;
+
db_lock(mdb);
- db_start_transaction(jcr, mdb);
- POOLMEM *jobids = get_pool_memory(PM_NAME);
- *jobids = 0;
+#ifdef xxx
+ /* TODO: Remove this code when updating make_bacula_table script */
+ Mmsg(mdb->cmd, "SELECT 1 FROM Job WHERE HasCache<>2 LIMIT 1");
+ if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+ Dmsg0(dbglevel, "Creating cache table\n");
+ Mmsg(mdb->cmd, "ALTER TABLE Job ADD HasCache int DEFAULT 0");
+ QUERY_DB(jcr, mdb, mdb->cmd);
+
+ Mmsg(mdb->cmd,
+ "CREATE TABLE PathHierarchy ( "
+ "PathId integer NOT NULL, "
+ "PPathId integer NOT NULL, "
+ "CONSTRAINT pathhierarchy_pkey "
+ "PRIMARY KEY (PathId))");
+ QUERY_DB(jcr, mdb, mdb->cmd);
+
+ Mmsg(mdb->cmd,
+ "CREATE INDEX pathhierarchy_ppathid "
+ "ON PathHierarchy (PPathId)");
+ QUERY_DB(jcr, mdb, mdb->cmd);
+
+ Mmsg(mdb->cmd,
+ "CREATE TABLE PathVisibility ("
+ "PathId integer NOT NULL, "
+ "JobId integer NOT NULL, "
+ "Size int8 DEFAULT 0, "
+ "Files int4 DEFAULT 0, "
+ "CONSTRAINT pathvisibility_pkey "
+ "PRIMARY KEY (JobId, PathId))");
+ QUERY_DB(jcr, mdb, mdb->cmd);
+
+ Mmsg(mdb->cmd,
+ "CREATE INDEX pathvisibility_jobid "
+ "ON PathVisibility (JobId)");
+ QUERY_DB(jcr, mdb, mdb->cmd);
+
+ }
+#endif
Mmsg(mdb->cmd,
"SELECT JobId from Job "
- "WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid) "
+ "WHERE HasCache = 0 "
"AND Type IN ('B') AND JobStatus IN ('T', 'f', 'A') "
"ORDER BY JobId");
- db_sql_query(mdb, mdb->cmd, db_get_int_handler, jobids);
+ db_sql_query(mdb, mdb->cmd, db_list_handler, &jobids_list);
- bvfs_update_path_hierarchy_cache(jcr, mdb, jobids);
+ 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,
- "DELETE FROM brestore_pathvisibility "
+ "DELETE FROM PathVisibility "
"WHERE NOT EXISTS "
- "(SELECT 1 FROM Job WHERE JobId=brestore_pathvisibility.JobId)");
- nb = DELETE_DB(jcr, mdb, mdb->cmd);
- Dmsg1(dbglevel, "Affected row(s) = %d\n", nb);
-
- Mmsg(mdb->cmd,
- "DELETE FROM brestore_knownjobid "
- "WHERE NOT EXISTS "
- "(SELECT 1 FROM Job WHERE JobId=brestore_knownjobid.JobId)");
+ "(SELECT 1 FROM Job WHERE JobId=PathVisibility.JobId)");
nb = DELETE_DB(jcr, mdb, mdb->cmd);
Dmsg1(dbglevel, "Affected row(s) = %d\n", nb);
db_end_transaction(jcr, mdb);
+ db_unlock(mdb);
}
/*
if (stat == 0) {
break;
}
-
+ Dmsg1(dbglevel, "Updating cache for %lld\n", (uint64_t)JobId);
update_path_hierarchy_cache(jcr, mdb, ppathid_cache, JobId);
}
}
bvfs_update_path_hierarchy_cache(jcr, db, jobids);
}
-static int result_handler(void *ctx, int fields, char **row)
-{
- if (fields == 4) {
- Dmsg4(0, "%s\t%s\t%s\t%s\n",
- row[0], row[1], row[2], row[3]);
- } else if (fields == 5) {
- Dmsg5(0, "%s\t%s\t%s\t%s\t%s\n",
- row[0], row[1], row[2], row[3], row[4]);
- } else if (fields == 6) {
- Dmsg6(0, "%s\t%s\t%s\t%s\t%s\t%s\n",
- row[0], row[1], row[2], row[3], row[4], row[5]);
- } else if (fields == 7) {
- Dmsg7(0, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
- row[0], row[1], row[2], row[3], row[4], row[5], row[6]);
- }
- return 0;
-}
-
-static int result_path_handler(void *ctx, int fields, char **row)
-{
- if (fields == 4) {
- Dmsg4(0, "%s\t%s\t%s\t%s\n",
- row[0], bvfs_basename_dir(row[1]), row[2], row[3]);
- }
- return 0;
-}
-
/* Change the current directory, returns true if the path exists */
-bool Bvfs::ch_dir(char *path)
+bool Bvfs::ch_dir(const char *path)
{
pm_strcpy(db->path, path);
db->pnl = strlen(db->path);
- pwd_id = db_get_path_record(jcr, db);
+ ch_dir(db_get_path_record(jcr, db));
return pwd_id != 0;
}
/*
* 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, char *client)
+void Bvfs::get_all_file_versions(DBId_t pathid, DBId_t fnid, const char *client)
{
Dmsg3(dbglevel, "get_all_file_versions(%lld, %lld, %s)\n", (uint64_t)pathid,
(uint64_t)fnid, client);
POOL_MEM query;
- Mmsg(query,
-"SELECT File.JobId, File.FileId, File.LStat, "
- "File.Md5, Media.VolumeName, Media.InChanger "
+ 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 "
"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 Job.ClientId = Client.ClientId "
"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);
-
- db_sql_query(db, query.c_str(), result_handler, this);
+ Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
+ db_sql_query(db, query.c_str(), list_entries, user_data);
}
DBId_t Bvfs::get_root()
return db_get_path_record(jcr, db);
}
+static int path_handler(void *ctx, int fields, char **row)
+{
+ Bvfs *fs = (Bvfs *) ctx;
+ return fs->_handle_path(ctx, fields, row);
+}
+
+int Bvfs::_handle_path(void *ctx, int fields, char **row)
+{
+ if (bvfs_is_dir(row)) {
+ /* can have the same path 2 times */
+ if (strcmp(row[BVFS_Name], prev_dir)) {
+ pm_strcpy(prev_dir, row[BVFS_Name]);
+ return list_entries(user_data, fields, row);
+ }
+ }
+ return 0;
+}
+
/*
* Retrieve . and .. information
*/
{
Dmsg1(dbglevel, "ls_special_dirs(%lld)\n", (uint64_t)pwd_id);
char ed1[50], ed2[50];
- if (!*jobids) {
+ if (*jobids == 0) {
return;
}
if (!dir_filenameid) {
get_dir_filenameid();
}
+ /* Will fetch directories */
+ *prev_dir = 0;
+
POOL_MEM query;
Mmsg(query,
-"((SELECT PPathId AS PathId, '..' AS Path "
- "FROM brestore_pathhierarchy "
- "WHERE PathId = %s) "
+"(SELECT PPathId AS PathId, '..' AS Path "
+ "FROM PathHierarchy "
+ "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;
- Mmsg(query2,
-"SELECT tmp.PathId, tmp.Path, LStat, JobId "
+ Mmsg(query2,// 1 2 3 4 5 6
+"SELECT 'D', tmp.PathId, 0, tmp.Path, JobId, LStat, FileId "
"FROM %s AS tmp LEFT JOIN ( " // get attributes if any
"SELECT File1.PathId AS PathId, File1.JobId AS JobId, "
- "File1.LStat AS LStat FROM File AS File1 "
+ "File1.LStat AS LStat, File1.FileId AS FileId FROM File AS File1 "
"WHERE File1.FilenameId = %s "
"AND File1.JobId IN (%s)) AS listfile1 "
"ON (tmp.PathId = listfile1.PathId) "
"ORDER BY tmp.Path, JobId DESC ",
query.c_str(), edit_uint64(dir_filenameid, ed2), jobids);
- Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
- db_sql_query(db, query2.c_str(), result_handler, this);
+ Dmsg1(dbglevel_sql, "q=%s\n", query2.c_str());
+ db_sql_query(db, query2.c_str(), path_handler, this);
}
-void Bvfs::ls_dirs()
+/* Returns true if we have dirs to read */
+bool Bvfs::ls_dirs()
{
Dmsg1(dbglevel, "ls_dirs(%lld)\n", (uint64_t)pwd_id);
char ed1[50], ed2[50];
- if (!*jobids) {
- return;
+ if (*jobids == 0) {
+ 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) {
get_dir_filenameid();
}
+ /* the sql query displays same directory multiple time, take the first one */
+ *prev_dir = 0;
+
/* Let's retrieve the list of the visible dirs in this dir ...
* First, I need the empty filenameid to locate efficiently
* the dirs in the file table
* my $dir_filenameid = $self->get_dir_filenameid();
*/
/* Then we get all the dir entries from File ... */
- POOL_MEM query;
- Mmsg(query,
-"SELECT PathId, Path, JobId, LStat FROM ( "
+ Mmsg(query,
+// 0 1 2 3 4 5 6
+"SELECT 'D', PathId, 0, Path, JobId, LStat, FileId FROM ( "
"SELECT Path1.PathId AS PathId, Path1.Path AS Path, "
"lower(Path1.Path) AS lpath, "
- "listfile1.JobId AS JobId, listfile1.LStat AS LStat "
+ "listfile1.JobId AS JobId, listfile1.LStat AS LStat, "
+ "listfile1.FileId AS FileId "
"FROM ( "
- "SELECT DISTINCT brestore_pathhierarchy1.PathId AS PathId "
- "FROM brestore_pathhierarchy AS brestore_pathhierarchy1 "
+ "SELECT DISTINCT PathHierarchy1.PathId AS PathId "
+ "FROM PathHierarchy AS PathHierarchy1 "
"JOIN Path AS Path2 "
- "ON (brestore_pathhierarchy1.PathId = Path2.PathId) "
- "JOIN brestore_pathvisibility AS brestore_pathvisibility1 "
- "ON (brestore_pathhierarchy1.PathId = brestore_pathvisibility1.PathId) "
- "WHERE brestore_pathhierarchy1.PPathId = %s "
- "AND brestore_pathvisibility1.jobid IN (%s) "
+ "ON (PathHierarchy1.PathId = Path2.PathId) "
+ "JOIN PathVisibility AS PathVisibility1 "
+ "ON (PathHierarchy1.PathId = PathVisibility1.PathId) "
+ "WHERE PathHierarchy1.PPathId = %s "
+ "AND PathVisibility1.JobId IN (%s) "
"%s "
") AS listpath1 "
"JOIN Path AS Path1 ON (listpath1.PathId = Path1.PathId) "
"LEFT JOIN ( " /* get attributes if any */
"SELECT File1.PathId AS PathId, File1.JobId AS JobId, "
- "File1.LStat AS LStat FROM File AS File1 "
+ "File1.LStat AS LStat, File1.FileId AS FileId FROM File AS File1 "
"WHERE File1.FilenameId = %s "
"AND File1.JobId IN (%s)) AS listfile1 "
"ON (listpath1.PathId = listfile1.PathId) "
limit, offset);
Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
- db_sql_query(db, query.c_str(), result_path_handler, this);
+
+ db_lock(db);
+ db_sql_query(db, query.c_str(), path_handler, this);
+ 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);
+ }
}
-void Bvfs::ls_files()
+/* 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) {
- return ;
+ if (*jobids == 0) {
+ return false;
}
if (!pwd_id) {
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);
}
+ 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 = sql_num_rows(db);
+ db_unlock(db);
+
+ return nb_record == limit;
+}
+
+
+/*
+ * Return next Id from comma separated list
+ *
+ * Returns:
+ * 1 if next Id returned
+ * 0 if no more Ids are in list
+ * -1 there is an error
+ * TODO: merge with get_next_jobid_from_list() and get_next_dbid_from_list()
+ */
+static int get_next_id_from_list(char **p, int64_t *Id)
+{
+ const int maxlen = 30;
+ char id[maxlen+1];
+ char *q = *p;
+
+ id[0] = 0;
+ for (int i=0; i<maxlen; i++) {
+ if (*q == 0) {
+ break;
+ } else if (*q == ',') {
+ q++;
+ break;
+ }
+ id[i] = *q++;
+ id[i+1] = 0;
+ }
+ if (id[0] == 0) {
+ return 0;
+ } else if (!is_a_number(id)) {
+ return -1; /* error */
+ }
+ *p = q;
+ *Id = str_to_int64(id);
+ return 1;
+}
+
+static int get_path_handler(void *ctx, int fields, char **row)
+{
+ POOL_MEM *buf = (POOL_MEM *) ctx;
+ pm_strcpy(*buf, row[0]);
+ return 0;
+}
+
+static bool check_temp(char *output_table)
+{
+ if (output_table[0] == 'b' &&
+ output_table[1] == '2' &&
+ is_an_integer(output_table + 2))
+ {
+ return true;
+ }
+ 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;
- Mmsg(query, // 0 1 2 3 4
-"SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId "
-"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);
+ if (check_temp(output_table)) {
+ Mmsg(query, "DROP TABLE %s", output_table);
+ db_sql_query(db, query.c_str(), NULL, NULL);
+ return true;
+ }
+ return false;
+}
+
+bool Bvfs::compute_restore_list(char *fileid, char *dirid, char *hardlink,
+ char *output_table)
+{
+ POOL_MEM query;
+ POOL_MEM tmp, tmp2;
+ int64_t id, jobid, prev_jobid;
+ bool init=false;
+ bool ret=false;
+ /* check args */
+ if ((*fileid && !is_a_number_list(fileid)) ||
+ (*dirid && !is_a_number_list(dirid)) ||
+ (*hardlink && !is_a_number_list(hardlink))||
+ (!*hardlink && !*fileid && !*dirid && !*hardlink))
+ {
+ return false;
+ }
+ if (!check_temp(output_table)) {
+ return false;
+ }
+
+ db_lock(db);
+
+ /* Cleanup old tables first */
+ Mmsg(query, "DROP TABLE btemp%s", output_table);
+ db_sql_query(db, query.c_str());
+
+ Mmsg(query, "DROP TABLE %s", output_table);
+ db_sql_query(db, query.c_str());
+
+ Mmsg(query, "CREATE TABLE btemp%s AS ", output_table);
+
+ if (*fileid) { /* Select files with their direct id */
+ init=true;
+ 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 */
+ goto bail_out;
+ }
+ if (!strcmp(tmp2.c_str(), "")) { /* path not found */
+ Dmsg3(dbglevel, "Path not found %lld q=%s s=%s\n",
+ id, tmp.c_str(), tmp2.c_str());
+ break;
+ }
+ /* escape % and _ for LIKE search */
+ tmp.check_size((strlen(tmp2.c_str())+1) * 2);
+ char *p = tmp.c_str();
+ for (char *s = tmp2.c_str(); *s ; s++) {
+ if (*s == '%' || *s == '_' || *s == '\\') {
+ *p = '\\';
+ p++;
+ }
+ *p = *s;
+ p++;
+ }
+ *p = '\0';
+ tmp.strcat("%");
+
+ size_t len = strlen(tmp.c_str());
+ tmp2.check_size((len+1) * 2);
+ db_escape_string(jcr, db, tmp2.c_str(), tmp.c_str(), len);
+
+ if (init) {
+ query.strcat(" UNION ");
+ }
+
+ Mmsg(tmp, "SELECT JobId, JobTDate, File.FileIndex, File.FilenameId, "
+ "File.PathId, FileId "
+ "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 */
+ 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");
+ goto bail_out;
+ }
+ if (jobid != prev_jobid) { /* new job */
+ if (prev_jobid == 0) { /* first jobid */
+ if (init) {
+ query.strcat(" UNION ");
+ }
+ } else { /* end last job, start new one */
+ tmp.strcat(") UNION ");
+ query.strcat(tmp.c_str());
+ }
+ 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;
+
+ } else { /* same job, add new findex */
+ Mmsg(tmp2, ", %lld", id);
+ tmp.strcat(tmp2.c_str());
+ }
+ }
+
+ if (prev_jobid != 0) { /* end last job */
+ tmp.strcat(") ");
+ query.strcat(tmp.c_str());
+ init = true;
+ }
+
+ 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;
+ }
+
+ /* TODO: handle basejob and SQLite3 */
+ Mmsg(query, sql_bvfs_select[db_get_type_index(db)], output_table, output_table);
+
+ /* TODO: handle jobid filter */
Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
- db_sql_query(db, query.c_str(), result_handler, this);
+ 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 %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:
+ Mmsg(query, "DROP TABLE btemp%s", output_table);
+ db_sql_query(db, query.c_str(), NULL, NULL);
+ db_unlock(db);
+ return ret;
}
+
+#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */