--- /dev/null
+Index: src/dird/job.c
+===================================================================
+--- src/dird/job.c (révision 6151)
++++ src/dird/job.c (copie de travail)
+@@ -734,7 +734,7 @@
+ jcr->jr.VolSessionId = jcr->VolSessionId;
+ jcr->jr.VolSessionTime = jcr->VolSessionTime;
+ jcr->jr.JobErrors = jcr->Errors;
+- if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
++ if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr, jcr->job->stats_enabled)) {
+ Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
+ db_strerror(jcr->db));
+ }
+Index: src/dird/ua_prune.c
+===================================================================
+--- src/dird/ua_prune.c (révision 6151)
++++ src/dird/ua_prune.c (copie de travail)
+@@ -107,6 +107,7 @@
+ * prune files (from) client=xxx
+ * prune jobs (from) client=xxx
+ * prune volume=xxx
++ * prune stats
+ */
+ int prunecmd(UAContext *ua, const char *cmd)
+ {
+@@ -119,6 +120,7 @@
+ NT_("Files"),
+ NT_("Jobs"),
+ NT_("Volume"),
++ NT_("Stats"),
+ NULL};
+
+ if (!open_client_db(ua)) {
+@@ -127,7 +129,7 @@
+
+ /* First search args */
+ kw = find_arg_keyword(ua, keywords);
+- if (kw < 0 || kw > 2) {
++ if (kw < 0 || kw > 3) {
+ /* no args, so ask user */
+ kw = do_keyword_prompt(ua, _("Choose item to prune"), keywords);
+ }
+@@ -162,6 +164,9 @@
+ }
+ prune_volume(ua, &mr);
+ return true;
++ case 3: /* prune stats */
++ /* TODO: prune JobStat table */
++ return true;
+ default:
+ break;
+ }
+Index: src/dird/dird_conf.c
+===================================================================
+--- src/dird/dird_conf.c (révision 6151)
++++ src/dird/dird_conf.c (copie de travail)
+@@ -133,6 +133,7 @@
+ {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
+ {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
+ {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
++ {"statisticsretention", store_time, ITEM(res_dir.stats_retention), 0, ITEM_DEFAULT, 60*60*24*31*12*5},
+ {NULL, NULL, {0}, 0, 0, 0}
+ };
+
+@@ -318,6 +319,7 @@
+ {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
+ {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
+ {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
++ {"usestatistics", store_bool, ITEM(res_job.stats_enabled), 0, 0, 0},
+ {NULL, NULL, {0}, 0, 0, 0}
+ };
+
+@@ -637,6 +639,9 @@
+ if (res->res_job.RegexWhere) {
+ sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
+ }
++ if (res->res_job.stats_enabled) {
++ sendit(sock, _(" --> StatsEnabled=%d\n"), res->res_job.stats_enabled);
++ }
+ if (res->res_job.RestoreBootstrap) {
+ sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
+ }
+Index: src/dird/dird_conf.h
+===================================================================
+--- src/dird/dird_conf.h (révision 6151)
++++ src/dird/dird_conf.h (copie de travail)
+@@ -129,6 +129,7 @@
+ bool tls_enable; /* Enable TLS */
+ bool tls_require; /* Require TLS */
+ bool tls_verify_peer; /* TLS Verify Client Certificate */
++ utime_t stats_retention; /* Stats retention period in seconds */
+
+ /* Methods */
+ char *name() const;
+@@ -399,6 +400,7 @@
+ bool write_part_after_job; /* Set to write part after job in SD */
+ bool enabled; /* Set if job enabled */
+ bool OptimizeJobScheduling; /* Set if we should optimize Job scheduling */
++ bool stats_enabled; /* Keep job records in a table for long term statistics */
+
+ MSGS *messages; /* How and where to send messages */
+ SCHED *schedule; /* When -- Automatic schedule */
+Index: src/cats/sql_update.c
+===================================================================
+--- src/cats/sql_update.c (révision 6151)
++++ src/cats/sql_update.c (copie de travail)
+@@ -142,7 +142,7 @@
+ * 1 on success
+ */
+ int
+-db_update_job_end_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
++db_update_job_end_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, bool stats_enabled)
+ {
+ char dt[MAX_TIME_LENGTH];
+ char rdt[MAX_TIME_LENGTH];
+@@ -192,11 +192,17 @@
+ edit_int64(jr->JobId, ed3));
+
+ stat = UPDATE_DB(jcr, mdb, mdb->cmd);
++
++ if (stat && stats_enabled) {
++ Mmsg(mdb->cmd,
++ "INSERT INTO JobStat (SELECT * FROM Job WHERE JobId=%s)",
++ edit_int64(jr->JobId, ed3));
++ INSERT_DB(jcr, mdb, mdb->cmd); /* TODO: get a message ? */
++ }
+ db_unlock(mdb);
+ return stat;
+ }
+
+-
+ /*
+ * Update Client record
+ * Returns: 0 on failure
+Index: src/cats/make_sqlite3_tables.in
+===================================================================
+--- src/cats/make_sqlite3_tables.in (révision 6151)
++++ src/cats/make_sqlite3_tables.in (copie de travail)
+@@ -75,6 +75,9 @@
+ );
+ CREATE INDEX inx6 ON Job (Name);
+
++-- Create a table like Job for long term statistics
++CREATE TABLE JobStat (LIKE Job);
++
+ CREATE TABLE Location (
+ LocationId INTEGER,
+ Location TEXT NOT NULL,
+Index: src/cats/make_postgresql_tables.in
+===================================================================
+--- src/cats/make_postgresql_tables.in (révision 6151)
++++ src/cats/make_postgresql_tables.in (copie de travail)
+@@ -79,6 +79,9 @@
+
+ CREATE INDEX job_name_idx on job (name);
+
++-- Create a table like Job for long term statistics
++CREATE TABLE jobstat (LIKE job);
++
+ CREATE TABLE Location (
+ LocationId serial not null,
+ Location text not null,
+Index: src/cats/protos.h
+===================================================================
+--- src/cats/protos.h (révision 6151)
++++ src/cats/protos.h (copie de travail)
+@@ -117,7 +117,7 @@
+
+ /* sql_update.c */
+ bool db_update_job_start_record(JCR *jcr, B_DB *db, JOB_DBR *jr);
+-int db_update_job_end_record(JCR *jcr, B_DB *db, JOB_DBR *jr);
++int db_update_job_end_record(JCR *jcr, B_DB *db, JOB_DBR *jr, bool stats_enabled);
+ int db_update_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
+ int db_update_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pr);
+ bool db_update_storage_record(JCR *jcr, B_DB *mdb, STORAGE_DBR *sr);
+Index: src/cats/make_sqlite_tables.in
+===================================================================
+--- src/cats/make_sqlite_tables.in (révision 6151)
++++ src/cats/make_sqlite_tables.in (copie de travail)
+@@ -75,6 +75,9 @@
+ );
+ CREATE INDEX inx6 ON Job (Name);
+
++-- Create a table like Job for long term statistics
++CREATE TABLE JobStat (LIKE Job);
++
+ CREATE TABLE Location (
+ LocationId INTEGER,
+ Location TEXT NOT NULL,
+Index: src/cats/sql_create.c
+===================================================================
+--- src/cats/sql_create.c (révision 6151)
++++ src/cats/sql_create.c (copie de travail)
+@@ -112,7 +112,7 @@
+ bool
+ db_create_jobmedia_record(JCR *jcr, B_DB *mdb, JOBMEDIA_DBR *jm)
+ {
+- bool ok = true;;
++ bool ok = true;
+ int count;
+ char ed1[50], ed2[50];
+
+@@ -162,8 +162,6 @@
+ return ok;
+ }
+
+-
+-
+ /* Create Unique Pool record
+ * Returns: false on failure
+ * true on success
+Index: src/cats/make_mysql_tables.in
+===================================================================
+--- src/cats/make_mysql_tables.in (révision 6151)
++++ src/cats/make_mysql_tables.in (copie de travail)
+@@ -115,6 +115,8 @@
+ INDEX (Name(128))
+ );
+
++-- Create a table like Job for long term statistics
++CREATE TABLE JobStat (LIKE Job);
+
+ CREATE TABLE Location (
+ LocationId INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+Index: src/stored/bscan.c
+===================================================================
+--- src/stored/bscan.c (révision 6151)
++++ src/stored/bscan.c (copie de travail)
+@@ -622,7 +622,7 @@
+ jr.VolSessionTime = mjcr->VolSessionTime;
+ jr.JobTDate = (utime_t)mjcr->start_time;
+ jr.ClientId = mjcr->ClientId;
+- if (!db_update_job_end_record(bjcr, db, &jr)) {
++ if (!db_update_job_end_record(bjcr, db, &jr, false)) {
+ Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
+ }
+ mjcr->read_dcr = NULL;
+@@ -1112,7 +1112,7 @@
+ return 1;
+ }
+
+- if (!db_update_job_end_record(bjcr, db, jr)) {
++ if (!db_update_job_end_record(bjcr, db, jr, false)) {
+ Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId, db_strerror(db));
+ free_jcr(mjcr);
+ return 0;
+Index: src/jcr.h
+===================================================================
+--- src/jcr.h (révision 6156)
++++ src/jcr.h (copie de travail)
+@@ -260,6 +260,7 @@
+ bool unlink_bsr; /* Unlink bsr file created */
+ bool VSS; /* VSS used by FD */
+ bool Encrypt; /* Encryption used by FD */
++ bool stats_enabled; /* Keep all job records in a table for long term statistics */
+ #endif /* DIRECTOR_DAEMON */
+
+