RdbmsCatalogue.cpp 193 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * The CERN Tape Archive (CTA) project
 * Copyright (C) 2015  CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

19
#include "catalogue/ArchiveFileRow.hpp"
20
#include "catalogue/RdbmsArchiveFileItorImpl.hpp"
21
#include "catalogue/RdbmsCatalogue.hpp"
22
#include "catalogue/retryOnLostConnection.hpp"
23
#include "catalogue/SqliteCatalogueSchema.hpp"
24
25
#include "catalogue/UserSpecifiedANonEmptyTape.hpp"
#include "catalogue/UserSpecifiedANonExistentTape.hpp"
26
#include "catalogue/UserSpecifiedAnEmptyStringComment.hpp"
27
#include "catalogue/UserSpecifiedAnEmptyStringDiskInstanceName.hpp"
28
#include "catalogue/UserSpecifiedAnEmptyStringLogicalLibraryName.hpp"
29
#include "catalogue/UserSpecifiedAnEmptyStringStorageClassName.hpp"
30
#include "catalogue/UserSpecifiedAnEmptyStringTapePoolName.hpp"
31
#include "catalogue/UserSpecifiedAnEmptyStringUsername.hpp"
32
#include "catalogue/UserSpecifiedAnEmptyStringVid.hpp"
33
#include "catalogue/UserSpecifiedAnEmptyStringVo.hpp"
34
#include "catalogue/UserSpecifiedAZeroCapacity.hpp"
35
#include "common/dataStructures/TapeFile.hpp"
36
#include "common/exception/Exception.hpp"
37
#include "common/exception/UserError.hpp"
38
#include "common/make_unique.hpp"
39
#include "common/threading/MutexLocker.hpp"
40
#include "common/Timer.hpp"
41
#include "common/utils/utils.hpp"
42
#include "rdbms/AutoRollback.hpp"
43

44
#include <ctype.h>
45
46
#include <memory>
#include <time.h>
47
#include <common/exception/LostDatabaseConnection.hpp>
48

49
50
51
namespace cta {
namespace catalogue {

52
53
54
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
55
RdbmsCatalogue::RdbmsCatalogue(
56
  log::Logger &log,
57
  const rdbms::Login &login,
58
  const uint64_t nbConns,
59
60
  const uint64_t nbArchiveFileListingConns,
  const uint32_t maxTriesToConnect):
61
  m_log(log),
62
  m_connPool(login, nbConns),
63
  m_archiveFileListingConnPool(login, nbArchiveFileListingConns),
Steven Murray's avatar
Steven Murray committed
64
65
66
67
  m_maxTriesToConnect(maxTriesToConnect),
  m_tapeCopyToPoolCache(10),
  m_groupMountPolicyCache(10),
  m_userMountPolicyCache(10),
68
69
  m_expectedNbArchiveRoutesCache(10),
  m_isAdminCache(10) {
70
71
72
73
74
}

//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
75
RdbmsCatalogue::~RdbmsCatalogue() {
76
77
78
79
80
}

//------------------------------------------------------------------------------
// createAdminUser
//------------------------------------------------------------------------------
81
void RdbmsCatalogue::createAdminUser(
Steven Murray's avatar
Steven Murray committed
82
  const common::dataStructures::SecurityIdentity &admin,
83
  const std::string &username,
84
  const std::string &comment) {
Steven Murray's avatar
Steven Murray committed
85
  try {
86
87
88
89
90
91
    if(username.empty()) {
      throw UserSpecifiedAnEmptyStringUsername("Cannot create admin user because the username is an empty string");
    }

    if(comment.empty()) {
      throw UserSpecifiedAnEmptyStringComment(std::string("Cannot create admin user ") + username +
92
        " because the comment is an empty string");
93
94
    }

95
    auto conn = m_connPool.getConn();
96
    if (adminUserExists(conn, username)) {
97
      throw exception::UserError(std::string("Cannot create admin user " + username +
98
99
        " because an admin user with the same name already exists"));
    }
100
    const uint64_t now = time(nullptr);
Steven Murray's avatar
Steven Murray committed
101
102
103
104
105
106
107
108
109
110
111
112
113
    const char *const sql =
      "INSERT INTO ADMIN_USER("
        "ADMIN_USER_NAME,"

        "USER_COMMENT,"

        "CREATION_LOG_USER_NAME,"
        "CREATION_LOG_HOST_NAME,"
        "CREATION_LOG_TIME,"

        "LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME)"
114
      "VALUES("
Steven Murray's avatar
Steven Murray committed
115
116
117
118
119
120
121
122
        ":ADMIN_USER_NAME,"

        ":USER_COMMENT,"

        ":CREATION_LOG_USER_NAME,"
        ":CREATION_LOG_HOST_NAME,"
        ":CREATION_LOG_TIME,"

123
124
125
        ":LAST_UPDATE_USER_NAME,"
        ":LAST_UPDATE_HOST_NAME,"
        ":LAST_UPDATE_TIME)";
126
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
Steven Murray's avatar
Steven Murray committed
127

128
    stmt.bindString(":ADMIN_USER_NAME", username);
Steven Murray's avatar
Steven Murray committed
129

130
    stmt.bindString(":USER_COMMENT", comment);
Steven Murray's avatar
Steven Murray committed
131

132
133
134
    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
    stmt.bindUint64(":CREATION_LOG_TIME", now);
Steven Murray's avatar
Steven Murray committed
135

136
137
138
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
139

140
    stmt.executeNonQuery();
141
142
143
144
  } catch(exception::UserError &) {
    throw;
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
145
146
147
148
149
150
151
    throw;
  }
}

//------------------------------------------------------------------------------
// adminUserExists
//------------------------------------------------------------------------------
152
bool RdbmsCatalogue::adminUserExists(rdbms::Conn &conn, const std::string adminUsername) const {
153
154
155
156
157
158
159
  try {
    const char *const sql =
      "SELECT "
        "ADMIN_USER_NAME AS ADMIN_USER_NAME "
      "FROM "
        "ADMIN_USER "
      "WHERE "
160
        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
161
162
163
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":ADMIN_USER_NAME", adminUsername);
    auto rset = stmt.executeQuery();
164
    return rset.next();
165
166
  } catch(exception::UserError &) {
    throw;
167
168
169
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
Steven Murray's avatar
Steven Murray committed
170
  }
171
172
173
174
175
}

//------------------------------------------------------------------------------
// deleteAdminUser
//------------------------------------------------------------------------------
176
void RdbmsCatalogue::deleteAdminUser(const std::string &username) {
177
  try {
178
    const char *const sql = "DELETE FROM ADMIN_USER WHERE ADMIN_USER_NAME = :ADMIN_USER_NAME";
179
    auto conn = m_connPool.getConn();
180
181
182
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
    stmt.bindString(":ADMIN_USER_NAME", username);
    stmt.executeNonQuery();
183

184
    if(0 == stmt.getNbAffectedRows()) {
185
      throw exception::UserError(std::string("Cannot delete admin-user ") + username + " because they do not exist");
186
    }
187
188
189
190
  } catch(exception::UserError &) {
    throw;
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
191
192
    throw;
  }
Steven Murray's avatar
Steven Murray committed
193
}
194
195
196
197

//------------------------------------------------------------------------------
// getAdminUsers
//------------------------------------------------------------------------------
198
std::list<common::dataStructures::AdminUser> RdbmsCatalogue::getAdminUsers() const {
Steven Murray's avatar
Steven Murray committed
199
200
201
202
203
  try {
    std::list<common::dataStructures::AdminUser> admins;
    const char *const sql =
      "SELECT "
        "ADMIN_USER_NAME AS ADMIN_USER_NAME,"
204

Steven Murray's avatar
Steven Murray committed
205
        "USER_COMMENT AS USER_COMMENT,"
206

207
208
209
        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
210

211
212
213
        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
214
      "FROM "
215
216
217
        "ADMIN_USER "
      "ORDER BY "
        "ADMIN_USER_NAME";
218
    auto conn = m_connPool.getConn();
219
220
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    auto rset = stmt.executeQuery();
221
    while (rset.next()) {
Steven Murray's avatar
Steven Murray committed
222
      common::dataStructures::AdminUser admin;
223

224
225
226
227
228
229
230
231
      admin.name = rset.columnString("ADMIN_USER_NAME");
      admin.comment = rset.columnString("USER_COMMENT");
      admin.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
      admin.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
      admin.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
      admin.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
      admin.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
      admin.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
232

Steven Murray's avatar
Steven Murray committed
233
234
      admins.push_back(admin);
    }
235

Steven Murray's avatar
Steven Murray committed
236
    return admins;
237
238
  } catch(exception::UserError &) {
    throw;
239
240
241
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
Steven Murray's avatar
Steven Murray committed
242
  }
243
244
245
246
247
}

//------------------------------------------------------------------------------
// modifyAdminUserComment
//------------------------------------------------------------------------------
Steven Murray's avatar
Steven Murray committed
248
void RdbmsCatalogue::modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
249
250
  const std::string &username, const std::string &comment) {
  try {
251
252
253
254
255
256
257
258
259
    if(username.empty()) {
      throw UserSpecifiedAnEmptyStringUsername("Cannot modify admin user because the username is an empty string");
    }

    if(comment.empty()) {
      throw UserSpecifiedAnEmptyStringComment(std::string("Cannot modify admin user ") + username +
        " because the comment is an empty string");
    }

260
261
262
263
264
265
266
    const time_t now = time(nullptr);
    const char *const sql =
      "UPDATE ADMIN_USER SET "
        "USER_COMMENT = :USER_COMMENT,"
        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
267
      "WHERE "
268
269
        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
    auto conn = m_connPool.getConn();
270
271
272
273
274
275
276
277
278
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
    stmt.bindString(":USER_COMMENT", comment);
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
    stmt.bindString(":ADMIN_USER_NAME", username);
    stmt.executeNonQuery();

    if(0 == stmt.getNbAffectedRows()) {
279
280
281
282
      throw exception::UserError(std::string("Cannot modify admin user ") + username + " because they do not exist");
    }
  } catch(exception::UserError &) {
    throw;
283
284
285
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
286
  }
Steven Murray's avatar
Steven Murray committed
287
}
288
289
290
291

//------------------------------------------------------------------------------
// createStorageClass
//------------------------------------------------------------------------------
292
void RdbmsCatalogue::createStorageClass(
Steven Murray's avatar
Steven Murray committed
293
  const common::dataStructures::SecurityIdentity &admin,
294
  const common::dataStructures::StorageClass &storageClass) {
Steven Murray's avatar
Steven Murray committed
295
  try {
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
    if(storageClass.diskInstance.empty()) {
      throw UserSpecifiedAnEmptyStringDiskInstanceName(std::string("Cannot create storage class ") +
         storageClass.diskInstance + ":" + storageClass.name + " because the disk instance name is an empty string");
    }

    if(storageClass.name.empty()) {
      throw UserSpecifiedAnEmptyStringStorageClassName(std::string("Cannot create storage class ") +
        storageClass.diskInstance + ":" + storageClass.name + " because the storage class name is an empty string");
    }

    if(storageClass.comment.empty()) {
      throw UserSpecifiedAnEmptyStringComment(std::string("Cannot create storage class ") +
        storageClass.diskInstance + ":" + storageClass.name + " because the comment is an empty string");
    }

311
    auto conn = m_connPool.getConn();
312
    if(storageClassExists(conn, storageClass.diskInstance, storageClass.name)) {
313
314
      throw exception::UserError(std::string("Cannot create storage class ") + storageClass.diskInstance + ":" +
        storageClass.name + " because it already exists");
315
    }
316
    const uint64_t storageClassId = getNextStorageClassId(conn);
317
    const time_t now = time(nullptr);
Steven Murray's avatar
Steven Murray committed
318
319
    const char *const sql =
      "INSERT INTO STORAGE_CLASS("
320
        "STORAGE_CLASS_ID,"
321
        "DISK_INSTANCE_NAME,"
Steven Murray's avatar
Steven Murray committed
322
323
324
325
326
327
328
329
330
331
332
333
        "STORAGE_CLASS_NAME,"
        "NB_COPIES,"

        "USER_COMMENT,"

        "CREATION_LOG_USER_NAME,"
        "CREATION_LOG_HOST_NAME,"
        "CREATION_LOG_TIME,"

        "LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME)"
334
      "VALUES("
335
        ":STORAGE_CLASS_ID,"
336
        ":DISK_INSTANCE_NAME,"
Steven Murray's avatar
Steven Murray committed
337
338
339
340
341
342
343
344
345
        ":STORAGE_CLASS_NAME,"
        ":NB_COPIES,"

        ":USER_COMMENT,"

        ":CREATION_LOG_USER_NAME,"
        ":CREATION_LOG_HOST_NAME,"
        ":CREATION_LOG_TIME,"

346
347
348
        ":LAST_UPDATE_USER_NAME,"
        ":LAST_UPDATE_HOST_NAME,"
        ":LAST_UPDATE_TIME)";
349
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
Steven Murray's avatar
Steven Murray committed
350

351
    stmt.bindUint64(":STORAGE_CLASS_ID", storageClassId);
352
353
354
    stmt.bindString(":DISK_INSTANCE_NAME", storageClass.diskInstance);
    stmt.bindString(":STORAGE_CLASS_NAME", storageClass.name);
    stmt.bindUint64(":NB_COPIES", storageClass.nbCopies);
Steven Murray's avatar
Steven Murray committed
355

356
    stmt.bindString(":USER_COMMENT", storageClass.comment);
Steven Murray's avatar
Steven Murray committed
357

358
359
360
    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
    stmt.bindUint64(":CREATION_LOG_TIME", now);
Steven Murray's avatar
Steven Murray committed
361

362
363
364
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
365

366
    stmt.executeNonQuery();
367
  } catch(exception::UserError &) {
368
    throw;
369
370
371
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
372
373
374
375
376
377
  }
}

//------------------------------------------------------------------------------
// storageClassExists
//------------------------------------------------------------------------------
378
bool RdbmsCatalogue::storageClassExists(rdbms::Conn &conn, const std::string &diskInstanceName,
379
  const std::string &storageClassName) const {
380
381
382
  try {
    const char *const sql =
      "SELECT "
383
        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
384
385
386
387
        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
      "FROM "
        "STORAGE_CLASS "
      "WHERE "
388
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
389
        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
390
391
392
393
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
    auto rset = stmt.executeQuery();
394
    return rset.next();
395
396
  } catch(exception::UserError &) {
    throw;
397
398
399
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
Steven Murray's avatar
Steven Murray committed
400
  }
401
402
403
404
405
}

//------------------------------------------------------------------------------
// deleteStorageClass
//------------------------------------------------------------------------------
406
void RdbmsCatalogue::deleteStorageClass(const std::string &diskInstanceName, const std::string &storageClassName) {
407
408
409
410
411
  try {
    const char *const sql =
      "DELETE FROM "
        "STORAGE_CLASS "
      "WHERE "
412
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
413
        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
414
    auto conn = m_connPool.getConn();
415
    auto stmt = conn.createStmt(sql,rdbms::AutocommitMode::ON);
416

417
418
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
419

420
421
    stmt.executeNonQuery();
    if(0 == stmt.getNbAffectedRows()) {
422
423
      throw exception::UserError(std::string("Cannot delete storage-class ") + diskInstanceName + ":" +
        storageClassName + " because it does not exist");
424
    }
425
  } catch(exception::UserError &) {
426
    throw;
427
428
429
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
430
  }
Steven Murray's avatar
Steven Murray committed
431
}
432
433
434
435

//------------------------------------------------------------------------------
// getStorageClasses
//------------------------------------------------------------------------------
436
std::list<common::dataStructures::StorageClass> RdbmsCatalogue::getStorageClasses() const {
Steven Murray's avatar
Steven Murray committed
437
438
439
440
  try {
    std::list<common::dataStructures::StorageClass> storageClasses;
    const char *const sql =
      "SELECT "
441
        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
Steven Murray's avatar
Steven Murray committed
442
        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
443
        "NB_COPIES AS NB_COPIES,"
Steven Murray's avatar
Steven Murray committed
444
445
446

        "USER_COMMENT AS USER_COMMENT,"

447
448
449
        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
Steven Murray's avatar
Steven Murray committed
450

451
452
453
        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
454
      "FROM "
455
456
457
        "STORAGE_CLASS "
      "ORDER BY "
        "DISK_INSTANCE_NAME, STORAGE_CLASS_NAME";
458
    auto conn = m_connPool.getConn();
459
460
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    auto rset = stmt.executeQuery();
461
    while (rset.next()) {
Steven Murray's avatar
Steven Murray committed
462
463
      common::dataStructures::StorageClass storageClass;

464
465
466
467
468
469
470
471
472
473
      storageClass.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
      storageClass.name = rset.columnString("STORAGE_CLASS_NAME");
      storageClass.nbCopies = rset.columnUint64("NB_COPIES");
      storageClass.comment = rset.columnString("USER_COMMENT");
      storageClass.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
      storageClass.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
      storageClass.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
      storageClass.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
      storageClass.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
      storageClass.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
Steven Murray's avatar
Steven Murray committed
474
475

      storageClasses.push_back(storageClass);
476
477
    }

Steven Murray's avatar
Steven Murray committed
478
    return storageClasses;
479
480
  } catch(exception::UserError &) {
    throw;
481
482
483
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
484
485
486
487
488
489
  }
}

//------------------------------------------------------------------------------
// modifyStorageClassNbCopies
//------------------------------------------------------------------------------
Steven Murray's avatar
Steven Murray committed
490
void RdbmsCatalogue::modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
491
492
493
494
495
496
497
498
499
  const std::string &instanceName, const std::string &name, const uint64_t nbCopies) {
  try {
    const time_t now = time(nullptr);
    const char *const sql =
      "UPDATE STORAGE_CLASS SET "
        "NB_COPIES = :NB_COPIES,"
        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
500
      "WHERE "
501
502
503
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
    auto conn = m_connPool.getConn();
504
505
506
507
508
509
510
511
512
513
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
    stmt.bindUint64(":NB_COPIES", nbCopies);
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
    stmt.bindString(":STORAGE_CLASS_NAME", name);
    stmt.executeNonQuery();

    if(0 == stmt.getNbAffectedRows()) {
514
515
516
517
518
      throw exception::UserError(std::string("Cannot modify storage class ") + instanceName + ":" + name +
        " because it does not exist");
    }
  } catch(exception::UserError &) {
    throw;
519
520
521
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
522
  }
Steven Murray's avatar
Steven Murray committed
523
}
524
525
526
527

//------------------------------------------------------------------------------
// modifyStorageClassComment
//------------------------------------------------------------------------------
Steven Murray's avatar
Steven Murray committed
528
void RdbmsCatalogue::modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
529
530
531
532
533
534
535
536
537
  const std::string &instanceName, const std::string &name, const std::string &comment) {
  try {
    const time_t now = time(nullptr);
    const char *const sql =
      "UPDATE STORAGE_CLASS SET "
        "USER_COMMENT = :USER_COMMENT,"
        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
538
      "WHERE "
539
540
541
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
    auto conn = m_connPool.getConn();
542
543
544
545
546
547
548
549
550
551
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
    stmt.bindString(":USER_COMMENT", comment);
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
    stmt.bindString(":STORAGE_CLASS_NAME", name);
    stmt.executeNonQuery();

    if(0 == stmt.getNbAffectedRows()) {
552
553
554
555
556
      throw exception::UserError(std::string("Cannot modify storage class ") + instanceName + ":" + name +
        " because it does not exist");
    }
  } catch(exception::UserError &) {
    throw;
557
558
559
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
560
  }
Steven Murray's avatar
Steven Murray committed
561
}
562
563
564
565

//------------------------------------------------------------------------------
// createTapePool
//------------------------------------------------------------------------------
566
void RdbmsCatalogue::createTapePool(
Steven Murray's avatar
Steven Murray committed
567
  const common::dataStructures::SecurityIdentity &admin,
568
  const std::string &name,
569
  const std::string &vo,
570
571
572
  const uint64_t nbPartialTapes,
  const bool encryptionValue,
  const std::string &comment) {
Steven Murray's avatar
Steven Murray committed
573
  try {
574
575
576
577
578
579
580
581
582
583
584
585
586
587
    if(name.empty()) {
      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create tape pool because the tape pool name is an empty string");
    }

    if(vo.empty()) {
      throw UserSpecifiedAnEmptyStringVo(std::string("Cannot create tape pool ") + name +
        " because the VO is an empty string");
    }

    if(comment.empty()) {
      throw UserSpecifiedAnEmptyStringComment(std::string("Cannot create tape pool ") + name +
        " because the comment is an empty string");
    }

588
    auto conn = m_connPool.getConn();
589

590
    if(tapePoolExists(conn, name)) {
591
      throw exception::UserError(std::string("Cannot create tape pool ") + name +
592
593
        " because a tape pool with the same name already exists");
    }
594
    const time_t now = time(nullptr);
Steven Murray's avatar
Steven Murray committed
595
596
597
    const char *const sql =
      "INSERT INTO TAPE_POOL("
        "TAPE_POOL_NAME,"
598
        "VO,"
Steven Murray's avatar
Steven Murray committed
599
600
601
602
603
604
605
606
607
608
609
610
        "NB_PARTIAL_TAPES,"
        "IS_ENCRYPTED,"

        "USER_COMMENT,"

        "CREATION_LOG_USER_NAME,"
        "CREATION_LOG_HOST_NAME,"
        "CREATION_LOG_TIME,"

        "LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME)"
611
      "VALUES("
Steven Murray's avatar
Steven Murray committed
612
        ":TAPE_POOL_NAME,"
613
        ":VO,"
Steven Murray's avatar
Steven Murray committed
614
615
616
617
618
619
620
621
622
        ":NB_PARTIAL_TAPES,"
        ":IS_ENCRYPTED,"

        ":USER_COMMENT,"

        ":CREATION_LOG_USER_NAME,"
        ":CREATION_LOG_HOST_NAME,"
        ":CREATION_LOG_TIME,"

623
624
625
        ":LAST_UPDATE_USER_NAME,"
        ":LAST_UPDATE_HOST_NAME,"
        ":LAST_UPDATE_TIME)";
626
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
Steven Murray's avatar
Steven Murray committed
627

628
    stmt.bindString(":TAPE_POOL_NAME", name);
629
    stmt.bindString(":VO", vo);
630
631
    stmt.bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes);
    stmt.bindBool(":IS_ENCRYPTED", encryptionValue);
Steven Murray's avatar
Steven Murray committed
632

633
    stmt.bindString(":USER_COMMENT", comment);
Steven Murray's avatar
Steven Murray committed
634

635
636
637
    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
    stmt.bindUint64(":CREATION_LOG_TIME", now);
Steven Murray's avatar
Steven Murray committed
638

639
640
641
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
642

643
    stmt.executeNonQuery();
644
  } catch(exception::UserError &) {
645
    throw;
646
647
648
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
649
650
651
652
653
654
655
656
657
658
  }
}

//------------------------------------------------------------------------------
// tapePoolExists
//------------------------------------------------------------------------------
bool RdbmsCatalogue::tapePoolExists(const std::string &tapePoolName) const {
  try {
    auto conn = m_connPool.getConn();
    return tapePoolExists(conn, tapePoolName);
659
660
  } catch(exception::UserError &) {
    throw;
661
662
663
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
Steven Murray's avatar
Steven Murray committed
664
  }
665
666
}

667
668
669
//------------------------------------------------------------------------------
// tapePoolExists
//------------------------------------------------------------------------------
670
bool RdbmsCatalogue::tapePoolExists(rdbms::Conn &conn, const std::string &tapePoolName) const {
671
672
673
674
675
676
677
  try {
    const char *const sql =
      "SELECT "
        "TAPE_POOL_NAME AS TAPE_POOL_NAME "
      "FROM "
        "TAPE_POOL "
      "WHERE "
678
        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
679
680
681
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
    auto rset = stmt.executeQuery();
682
    return rset.next();
683
684
  } catch(exception::UserError &) {
    throw;
685
686
687
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
688
689
690
  }
}

691
692
693
//------------------------------------------------------------------------------
// archiveFileExists
//------------------------------------------------------------------------------
694
bool RdbmsCatalogue::archiveFileIdExists(rdbms::Conn &conn, const uint64_t archiveFileId) const {
695
696
697
698
699
700
701
702
  try {
    const char *const sql =
      "SELECT "
        "ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
      "FROM "
        "ARCHIVE_FILE "
      "WHERE "
        "ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
703
704
705
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
    auto rset = stmt.executeQuery();
706
    return rset.next();
707
708
  } catch(exception::UserError &) {
    throw;
709
710
711
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
712
713
714
  }
}

715
716
717
//------------------------------------------------------------------------------
// diskFileIdExists
//------------------------------------------------------------------------------
718
bool RdbmsCatalogue::diskFileIdExists(rdbms::Conn &conn, const std::string &diskInstanceName,
719
720
721
722
723
724
725
726
727
728
729
  const std::string &diskFileId) const {
  try {
    const char *const sql =
      "SELECT "
        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
        "DISK_FILE_ID AS DISK_FILE_ID "
      "FROM "
        "ARCHIVE_FILE "
      "WHERE "
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "DISK_FILE_ID = :DISK_FILE_ID";
730
731
732
733
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_ID", diskFileId);
    auto rset = stmt.executeQuery();
734
    return rset.next();
735
736
  } catch(exception::UserError &) {
    throw;
737
738
739
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
740
741
742
  }
}

743
744
745
//------------------------------------------------------------------------------
// diskFilePathExists
//------------------------------------------------------------------------------
746
bool RdbmsCatalogue::diskFilePathExists(rdbms::Conn &conn, const std::string &diskInstanceName,
747
748
749
750
751
752
753
754
755
756
757
  const std::string &diskFilePath) const {
  try {
    const char *const sql =
      "SELECT "
        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
        "DISK_FILE_PATH AS DISK_FILE_PATH "
      "FROM "
        "ARCHIVE_FILE "
      "WHERE "
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "DISK_FILE_PATH = :DISK_FILE_PATH";
758
759
760
761
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_PATH", diskFilePath);
    auto rset = stmt.executeQuery();
762
    return rset.next();
763
764
  } catch(exception::UserError &) {
    throw;
765
766
767
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
768
769
770
  }
}

771
772
773
//------------------------------------------------------------------------------
// diskFileUserExists
//------------------------------------------------------------------------------
774
bool RdbmsCatalogue::diskFileUserExists(rdbms::Conn &conn, const std::string &diskInstanceName,
775
776
777
778
779
780
781
782
783
784
785
  const std::string &diskFileUser) const {
  try {
    const char *const sql =
      "SELECT "
        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
        "DISK_FILE_USER AS DISK_FILE_USER "
      "FROM "
        "ARCHIVE_FILE "
      "WHERE "
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "DISK_FILE_USER = :DISK_FILE_USER";
786
787
788
789
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_USER", diskFileUser);
    auto rset = stmt.executeQuery();
790
    return rset.next();
791
792
  } catch(exception::UserError &) {
    throw;
793
794
795
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
796
797
798
  }
}

799
800
801
//------------------------------------------------------------------------------
// diskFileGroupExists
//------------------------------------------------------------------------------
802
bool RdbmsCatalogue::diskFileGroupExists(rdbms::Conn &conn, const std::string &diskInstanceName,
803
804
805
806
807
808
809
810
811
812
813
  const std::string &diskFileGroup) const {
  try {
    const char *const sql =
      "SELECT "
        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
        "DISK_FILE_GROUP AS DISK_FILE_GROUP "
      "FROM "
        "ARCHIVE_FILE "
      "WHERE "
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "DISK_FILE_GROUP = :DISK_FILE_GROUP";
814
815
816
817
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_GROUP", diskFileGroup);
    auto rset = stmt.executeQuery();
818
    return rset.next();
819
820
  } catch(exception::UserError &) {
    throw;
821
822
823
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
824
825
826
  }
}

827
828
829
//------------------------------------------------------------------------------
// archiveRouteExists
//------------------------------------------------------------------------------
830
bool RdbmsCatalogue::archiveRouteExists(rdbms::Conn &conn, const std::string &diskInstanceName,
831
832
833
834
  const std::string &storageClassName, const uint64_t copyNb) const {
  try {
    const char *const sql =
      "SELECT "
835
836
        "ARCHIVE_ROUTE.STORAGE_CLASS_ID AS STORAGE_CLASS_ID,"
        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB "
837
838
      "FROM "
        "ARCHIVE_ROUTE "
839
840
      "INNER JOIN STORAGE_CLASS ON "
        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
841
      "WHERE "
842
843
844
        "STORAGE_CLASS.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND "
        "ARCHIVE_ROUTE.COPY_NB = :COPY_NB";
845
846
847
848
849
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
    stmt.bindUint64(":COPY_NB", copyNb);
    auto rset = stmt.executeQuery();
850
    return rset.next();
851
852
  } catch(exception::UserError &) {
    throw;
853
854
855
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
856
857
858
  }
}

859
860
861
//------------------------------------------------------------------------------
// deleteTapePool
//------------------------------------------------------------------------------
862
void RdbmsCatalogue::deleteTapePool(const std::string &name) {
863
  try {
864
    const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME";
865
    auto conn = m_connPool.getConn();
866
867
868
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
    stmt.bindString(":TAPE_POOL_NAME", name);
    stmt.executeNonQuery();
869

870
    if(0 == stmt.getNbAffectedRows()) {
871
      throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist");
872
    }
873
  } catch(exception::UserError &) {
874
    throw;
875
876
877
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
878
  }
Steven Murray's avatar
Steven Murray committed
879
}
880
881
882
883

//------------------------------------------------------------------------------
// getTapePools
//------------------------------------------------------------------------------
884
std::list<TapePool> RdbmsCatalogue::getTapePools() const {
Steven Murray's avatar
Steven Murray committed
885
  try {
886
    std::list<TapePool> pools;
Steven Murray's avatar
Steven Murray committed
887
888
    const char *const sql =
      "SELECT "
889
        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
890
        "COALESCE(TAPE_POOL.VO, 'NONE') AS VO," // TBD Remove COALESCE
891
892
        "TAPE_POOL.NB_PARTIAL_TAPES AS NB_PARTIAL_TAPES,"
        "TAPE_POOL.IS_ENCRYPTED AS IS_ENCRYPTED,"
Steven Murray's avatar
Steven Murray committed
893

894
        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
895
896
        "COALESCE(SUM(CAPACITY_IN_BYTES), 0) AS CAPACITY_IN_BYTES,"
        "COALESCE(SUM(DATA_IN_BYTES), 0) AS DATA_IN_BYTES,"
Steven Murray's avatar
Steven Murray committed
897

898
        "TAPE_POOL.USER_COMMENT AS USER_COMMENT,"
Steven Murray's avatar
Steven Murray committed
899

900
901
902
903
904
905
906
        "TAPE_POOL.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
        "TAPE_POOL.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
        "TAPE_POOL.CREATION_LOG_TIME AS CREATION_LOG_TIME,"

        "TAPE_POOL.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
        "TAPE_POOL.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
        "TAPE_POOL.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
907
      "FROM "
908
        "TAPE_POOL "
909
910
911
912
      "LEFT OUTER JOIN TAPE ON "
        "TAPE_POOL.TAPE_POOL_NAME = TAPE.TAPE_POOL_NAME "
      "GROUP BY "
        "TAPE_POOL.TAPE_POOL_NAME,"
913
        "TAPE_POOL.VO,"
914
915
916
917
918
919
920
921
922
        "TAPE_POOL.NB_PARTIAL_TAPES,"
        "TAPE_POOL.IS_ENCRYPTED,"
        "TAPE_POOL.USER_COMMENT,"
        "TAPE_POOL.CREATION_LOG_USER_NAME,"
        "TAPE_POOL.CREATION_LOG_HOST_NAME,"
        "TAPE_POOL.CREATION_LOG_TIME,"
        "TAPE_POOL.LAST_UPDATE_USER_NAME,"
        "TAPE_POOL.LAST_UPDATE_HOST_NAME,"
        "TAPE_POOL.LAST_UPDATE_TIME "
923
924
      "ORDER BY "
        "TAPE_POOL_NAME";
925

926
    auto conn = m_connPool.getConn();
927
928
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::OFF);
    auto rset = stmt.executeQuery();
929
    while (rset.next()) {
930
      TapePool pool;
Steven Murray's avatar
Steven Murray committed
931

932
      pool.name = rset.columnString("TAPE_POOL_NAME");
933
      pool.vo = rset.columnString("VO");
934
935
      pool.nbPartialTapes = rset.columnUint64("NB_PARTIAL_TAPES");
      pool.encryption = rset.columnBool("IS_ENCRYPTED");
936
      pool.nbTapes = rset.columnUint64("NB_TAPES");
937
938
      pool.capacityBytes = rset.columnUint64("CAPACITY_IN_BYTES");
      pool.dataBytes = rset.columnUint64("DATA_IN_BYTES");
939
940
941
942
943
944
945
      pool.comment = rset.columnString("USER_COMMENT");
      pool.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
      pool.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
      pool.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
      pool.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
      pool.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
      pool.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
Steven Murray's avatar
Steven Murray committed
946
947

      pools.push_back(pool);
948
949
    }

Steven Murray's avatar
Steven Murray committed
950
    return pools;
951
952
  } catch(exception::UserError &) {
    throw;
953
954
955
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
956
957
958
  }
}

959
960
961
962
963
964
//------------------------------------------------------------------------------
// modifyTapePoolVO
//------------------------------------------------------------------------------
void RdbmsCatalogue::modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin,
  const std::string &name, const std::string &vo) {
  try {
965
966
967
968
969
    if(name.empty()) {
      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
        " string");
    }

970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
    if(vo.empty()) {
      throw UserSpecifiedAnEmptyStringVo(std::string("Cannot modify tape pool ") + name +
        " because the new VO is an empty string");
    }

    const time_t now = time(nullptr);
    const char *const sql =
      "UPDATE TAPE_POOL SET "
        "VO = :VO,"
        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
      "WHERE "
        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
    auto conn = m_connPool.getConn();
    auto stmt = conn.createStmt(sql, rdbms::AutocommitMode::ON);
    stmt.bindString(":VO", vo);
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
    stmt.bindString(":TAPE_POOL_NAME", name);
    stmt.executeNonQuery();

    if(0 == stmt.getNbAffectedRows()) {
      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
    }
  } catch(exception::UserError &) {
    throw;
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
For faster browsing, not all history is shown. View entire blame