--- /dev/null
+Index: src/dird/autoprune.c
+===================================================================
+--- src/dird/autoprune.c (révision 7380)
++++ src/dird/autoprune.c (copie de travail)
+@@ -96,7 +96,7 @@
+ POOL_MEM query(PM_MESSAGE);
+ UAContext *ua;
+ bool ok = false;
+- char ed1[50], ed2[100], ed3[50];
++ char ed1[50], ed2[100];
+ POOL_DBR spr;
+
+ Dmsg1(050, "Prune volumes PoolId=%d\n", jcr->jr.PoolId);
+@@ -141,15 +141,9 @@
+ "(PoolId=%s OR RecyclePoolId IN (%s)) AND MediaType='%s' %s"
+ "ORDER BY LastWritten ASC,MediaId";
+
+- if (InChanger) {
+- char changer[100];
+- /* Ensure it is in this autochanger */
+- bsnprintf(changer, sizeof(changer), "AND InChanger=1 AND StorageId=%s ",
+- edit_int64(mr->StorageId, ed3));
+- Mmsg(query, select, ed1, ed2, mr->MediaType, changer);
+- } else {
+- Mmsg(query, select, ed1, ed2, mr->MediaType, "");
+- }
++ POOL_MEM changer(PM_MESSAGE);
++ db_select_autochanger(ua->jcr, ua->db, InChanger, mr->StorageId, changer);
++ Mmsg(query, select, ed1, ed2, mr->MediaType, changer.c_str());
+
+ Dmsg1(050, "query=%s\n", query.c_str());
+ if (!db_get_query_dbids(ua->jcr, ua->db, query, ids)) {
+Index: src/dird/dird.c
+===================================================================
+--- src/dird/dird.c (révision 7380)
++++ src/dird/dird.c (copie de travail)
+@@ -933,6 +933,7 @@
+ }
+ bstrncpy(sr.Name, store->name(), sizeof(sr.Name));
+ sr.AutoChanger = store->autochanger;
++ sr.AutoChangerId = 0; /* don't know if it's in big autochanger */
+ db_create_storage_record(NULL, db, &sr);
+ store->StorageId = sr.StorageId; /* set storage Id */
+ if (!sr.created) { /* if not created, update it */
+@@ -974,7 +975,21 @@
+ }
+ }
+ }
+-
++ /* Update Autochanger properties */
++ db_reset_storage_autochangerid(NULL, db); /* reset AutoChangerId for each storage */
++ foreach_res(store, R_STORAGE) {
++ if (store->storage_member) { /* This storage owns other storages */
++ STORE *elt;
++ foreach_alist(elt, store->storage_member) {
++ STORAGE_DBR sr;
++ bstrncpy(sr.Name, elt->name(), sizeof(sr.Name));
++ sr.StorageId = elt->StorageId;
++ sr.AutoChanger = elt->autochanger;
++ sr.AutoChangerId = store->StorageId;
++ db_update_storage_record(NULL, db, &sr);
++ }
++ }
++ }
+ /* Loop over all counters, defining them in each database */
+ /* Set default value in all counters */
+ COUNTER *counter;
+Index: src/dird/dird_conf.c
+===================================================================
+--- src/dird/dird_conf.c (révision 7380)
++++ src/dird/dird_conf.c (copie de travail)
+@@ -218,6 +218,7 @@
+ {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
+ {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
+ {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
++ {"contains", store_alist_res,ITEM(res_store.storage_member), R_STORAGE, 0, 0},
+ {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
+ {"maximumconcurrentjobs", store_pint32, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
+ {"sddport", store_pint32, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
+@@ -1161,6 +1162,9 @@
+ if (res->res_store.device) {
+ delete res->res_store.device;
+ }
++ if (res->res_store.storage_member) {
++ delete res->res_store.storage_member;
++ }
+ if (res->res_store.tls_ctx) {
+ free_tls_context(res->res_store.tls_ctx);
+ }
+@@ -1395,6 +1399,7 @@
+ }
+ /* we must explicitly copy the device alist pointer */
+ res->res_store.device = res_all.res_store.device;
++ res->res_store.storage_member = res_all.res_store.storage_member;
+ break;
+ case R_JOB:
+ case R_JOBDEFS:
+Index: src/dird/dird_conf.h
+===================================================================
+--- src/dird/dird_conf.h (révision 7380)
++++ src/dird/dird_conf.h (copie de travail)
+@@ -290,6 +290,7 @@
+ char *password;
+ char *media_type;
+ alist *device; /* Alternate devices for this Storage */
++ alist *storage_member;
+ uint32_t MaxConcurrentJobs; /* Maximume concurrent jobs */
+ uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
+ uint32_t NumConcurrentReadJobs; /* number of jobs reading */
+Index: src/cats/sql_update.c
+===================================================================
+--- src/cats/sql_update.c (révision 7380)
++++ src/cats/sql_update.c (copie de travail)
+@@ -288,14 +288,27 @@
+ }
+
+ bool
++db_reset_storage_autochangerid(JCR *jcr, B_DB *mdb)
++{
++ int stat;
++ Dmsg0(50, "reset storage autochangerid\n");
++ db_lock(mdb);
++ Mmsg(mdb->cmd, "UPDATE Storage SET AutoChangerId=0 "); /* Change to Autochanger */
++ stat = UPDATE_DB(jcr, mdb, mdb->cmd);
++ db_unlock(mdb);
++ return stat;
++}
++
++bool
+ db_update_storage_record(JCR *jcr, B_DB *mdb, STORAGE_DBR *sr)
+ {
+ int stat;
+- char ed1[50];
++ char ed1[50], ed2[50];
+
+ db_lock(mdb);
+- Mmsg(mdb->cmd, "UPDATE Storage SET AutoChanger=%d WHERE StorageId=%s",
+- sr->AutoChanger, edit_int64(sr->StorageId, ed1));
++ /* TODO: if autochangerid is set, update AutoChanger table */
++ Mmsg(mdb->cmd, "UPDATE Storage SET AutoChanger=%d, AutoChangerId=%s WHERE StorageId=%s",
++ sr->AutoChanger, edit_int64(sr->AutoChangerId, ed2), edit_int64(sr->StorageId, ed1));
+
+ stat = UPDATE_DB(jcr, mdb, mdb->cmd);
+ db_unlock(mdb);
+Index: src/cats/update_postgresql_tables.in
+===================================================================
+--- src/cats/update_postgresql_tables.in (révision 7380)
++++ src/cats/update_postgresql_tables.in (copie de travail)
+@@ -14,6 +14,9 @@
+ -- Create a table like Job for long term statistics
+ CREATE TABLE jobstat (LIKE job);
+
++ALTER TABLE Storage ADD COLUMN AutoChangerId integer;
++ALTER TABLE Storage ALTER COLUMN AutoChangerId SET DEFAULT 0;
++
+ UPDATE version SET versionid=11;
+
+ vacuum analyse;
+Index: src/cats/make_sqlite3_tables.in
+===================================================================
+--- src/cats/make_sqlite3_tables.in (révision 7380)
++++ src/cats/make_sqlite3_tables.in (copie de travail)
+@@ -218,6 +218,7 @@
+ StorageId INTEGER,
+ Name VARCHAR(128) NOT NULL,
+ AutoChanger TINYINT DEFAULT 0,
++ AutoChangerId INTEGER DEFAULT 0,
+ PRIMARY KEY(StorageId)
+ );
+
+Index: src/cats/cats.h
+===================================================================
+--- src/cats/cats.h (révision 7380)
++++ src/cats/cats.h (copie de travail)
+@@ -939,7 +939,7 @@
+ DBId_t StorageId;
+ char Name[MAX_NAME_LENGTH]; /* Device name */
+ int AutoChanger; /* Set if autochanger */
+-
++ int AutoChangerId; /* Group devices in autochanger */
+ /* Not in database */
+ bool created; /* set if created by db_create ... */
+ };
+Index: src/cats/make_postgresql_tables.in
+===================================================================
+--- src/cats/make_postgresql_tables.in (révision 7380)
++++ src/cats/make_postgresql_tables.in (copie de travail)
+@@ -185,6 +185,7 @@
+ StorageId SERIAL,
+ Name TEXT NOT NULL,
+ AutoChanger INTEGER DEFAULT 0,
++ AutoChangerId INTEGER DEFAULT 0,
+ PRIMARY KEY(StorageId)
+ );
+
+Index: src/cats/update_mysql_tables.in
+===================================================================
+--- src/cats/update_mysql_tables.in (révision 7380)
++++ src/cats/update_mysql_tables.in (copie de travail)
+@@ -15,6 +15,9 @@
+ -- Create a table like Job for long term statistics
+ CREATE TABLE JobStat (LIKE Job);
+
++ALTER TABLE Storage ADD COLUMN AutoChangerId integer;
++ALTER TABLE Storage ALTER COLUMN AutoChangerId SET DEFAULT 0;
++
+ DELETE FROM Version;
+ INSERT INTO Version (VersionId) VALUES (11);
+
+Index: src/cats/protos.h
+===================================================================
+--- src/cats/protos.h (révision 7380)
++++ src/cats/protos.h (copie de travail)
+@@ -123,10 +123,12 @@
+ void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
+
+ /* sql_update.c */
++void db_select_autochanger(JCR *jcr, B_DB *mdb, bool InChanger, DBId_t StorageId, POOL_MEM &result);
+ 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, 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_reset_storage_autochangerid(JCR *jcr, B_DB *db);
+ bool db_update_storage_record(JCR *jcr, B_DB *mdb, STORAGE_DBR *sr);
+ int db_update_media_record(JCR *jcr, B_DB *db, MEDIA_DBR *mr);
+ int db_update_media_defaults(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
+Index: src/cats/sql_find.c
+===================================================================
+--- src/cats/sql_find.c (révision 7380)
++++ src/cats/sql_find.c (copie de travail)
+@@ -305,6 +305,26 @@
+ return true;
+ }
+
++void db_select_autochanger(JCR *jcr, B_DB *mdb, bool InChanger, DBId_t StorageId, POOL_MEM &result)
++{
++ char ed1[50];
++
++ if (InChanger) {
++ edit_int64(StorageId, ed1);
++ Mmsg(result,
++ "AND InChanger=1 "
++ "AND ( StorageId IN "
++ "( SELECT StorageId "
++ "FROM Storage "
++ "WHERE AutoChangerId = %s "
++ ") "
++ "OR StorageId=%s)",
++ ed1, ed1);
++ } else {
++ Mmsg(result, "");
++ }
++}
++
+ /*
+ * Find Available Media (Volume) for Pool
+ *
+@@ -338,12 +358,9 @@
+ edit_int64(mr->PoolId, ed1), mr->MediaType);
+ item = 1;
+ } else {
+- POOL_MEM changer(PM_FNAME);
+- /* Find next available volume */
+- if (InChanger) {
+- Mmsg(changer, "AND InChanger=1 AND StorageId=%s",
+- edit_int64(mr->StorageId, ed1));
+- }
++ POOL_MEM changer(PM_MESSAGE);
++ db_select_autochanger(jcr, mdb, InChanger, mr->StorageId, changer);
++
+ if (strcmp(mr->VolStatus, "Recycle") == 0 ||
+ strcmp(mr->VolStatus, "Purged") == 0) {
+ order = "AND Recycle=1 ORDER BY LastWritten ASC,MediaId"; /* take oldest that can be recycled */
+Index: src/cats/make_sqlite_tables.in
+===================================================================
+--- src/cats/make_sqlite_tables.in (révision 7380)
++++ src/cats/make_sqlite_tables.in (copie de travail)
+@@ -218,6 +218,7 @@
+ StorageId INTEGER,
+ Name VARCHAR(128) NOT NULL,
+ AutoChanger TINYINT DEFAULT 0,
++ AutoChangerId INTEGER DEFAULT 0,
+ PRIMARY KEY(StorageId)
+ );
+
+Index: src/cats/sql_create.c
+===================================================================
+--- src/cats/sql_create.c (révision 7380)
++++ src/cats/sql_create.c (copie de travail)
+@@ -279,7 +279,7 @@
+ bool ok;
+
+ db_lock(mdb);
+- Mmsg(mdb->cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'", sr->Name);
++ Mmsg(mdb->cmd, "SELECT StorageId,AutoChanger,AutoChangerId FROM Storage WHERE Name='%s'", sr->Name);
+
+ sr->StorageId = 0;
+ sr->created = false;
+@@ -300,6 +300,7 @@
+ }
+ sr->StorageId = str_to_int64(row[0]);
+ sr->AutoChanger = atoi(row[1]); /* bool */
++ sr->AutoChangerId = str_to_int64(row[2]);
+ sql_free_result(mdb);
+ db_unlock(mdb);
+ return true;
+@@ -308,8 +309,8 @@
+ }
+
+ /* Must create it */
+- Mmsg(mdb->cmd, "INSERT INTO Storage (Name,AutoChanger)"
+- " VALUES ('%s',%d)", sr->Name, sr->AutoChanger);
++ Mmsg(mdb->cmd, "INSERT INTO Storage (Name,AutoChanger,AutoChangerId)"
++ " VALUES ('%s',%d,%d)", sr->Name, sr->AutoChanger, sr->AutoChangerId);
+
+ if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
+ Mmsg2(&mdb->errmsg, _("Create DB Storage record %s failed. ERR=%s\n"),
+Index: src/cats/make_mysql_tables.in
+===================================================================
+--- src/cats/make_mysql_tables.in (révision 7380)
++++ src/cats/make_mysql_tables.in (copie de travail)
+@@ -64,6 +64,7 @@
+ StorageId INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ Name TINYBLOB NOT NULL,
+ AutoChanger TINYINT DEFAULT 0,
++ AutoChangerId INTEGER DEFAULT 0,
+ PRIMARY KEY(StorageId)
+ );
+
+Index: src/stored/stored_conf.h
+===================================================================
+--- src/stored/stored_conf.h (révision 7380)
++++ src/stored/stored_conf.h (copie de travail)
+@@ -99,7 +99,7 @@
+ char *tls_keyfile; /* TLS Server Key File */
+ char *tls_dhfile; /* TLS Diffie-Hellman Parameters */
+ alist *tls_allowed_cns; /* TLS Allowed Clients */
+-
++ alist *storage_member; /* Storage in the same Autochanger */
+ TLS_CONTEXT *tls_ctx; /* Shared TLS Context */
+ };
+ typedef class s_res_store STORES;