RdbmsCatalogue.cpp 227 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/RdbmsCatalogue.hpp"
21
#include "catalogue/RdbmsCatalogueGetArchiveFilesItor.hpp"
22
#include "catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp"
23
#include "catalogue/retryOnLostConnection.hpp"
24
#include "catalogue/SqliteCatalogueSchema.hpp"
25
26
#include "catalogue/UserSpecifiedANonEmptyTape.hpp"
#include "catalogue/UserSpecifiedANonExistentTape.hpp"
27
#include "catalogue/UserSpecifiedAnEmptyStringComment.hpp"
28
#include "catalogue/UserSpecifiedAnEmptyStringDiskInstanceName.hpp"
29
#include "catalogue/UserSpecifiedAnEmptyStringLogicalLibraryName.hpp"
30
#include "catalogue/UserSpecifiedAnEmptyStringMediaType.hpp"
31
#include "catalogue/UserSpecifiedAnEmptyStringStorageClassName.hpp"
32
#include "catalogue/UserSpecifiedAnEmptyStringSupply.hpp"
33
#include "catalogue/UserSpecifiedAnEmptyStringTapePoolName.hpp"
34
#include "catalogue/UserSpecifiedAnEmptyStringUsername.hpp"
35
#include "catalogue/UserSpecifiedAnEmptyStringVendor.hpp"
36
#include "catalogue/UserSpecifiedAnEmptyStringVid.hpp"
37
#include "catalogue/UserSpecifiedAnEmptyStringVo.hpp"
38
#include "catalogue/UserSpecifiedAZeroCapacity.hpp"
39
#include "catalogue/UserSpecifiedAZeroCopyNb.hpp"
40
#include "common/dataStructures/TapeFile.hpp"
41
#include "common/exception/Exception.hpp"
42
#include "common/exception/UserError.hpp"
43
#include "common/make_unique.hpp"
44
#include "common/threading/MutexLocker.hpp"
45
#include "common/Timer.hpp"
46
#include "common/utils/utils.hpp"
47
#include "rdbms/AutoRollback.hpp"
48

49
#include <ctype.h>
50
51
#include <memory>
#include <time.h>
52
#include <common/exception/LostDatabaseConnection.hpp>
53

54
55
56
namespace cta {
namespace catalogue {

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

//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
78
RdbmsCatalogue::~RdbmsCatalogue() {
79
80
81
82
83
}

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

    if(comment.empty()) {
94
      throw UserSpecifiedAnEmptyStringComment("Cannot create admin user because the comment is an empty string");
95
96
    }

97
    auto conn = m_connPool.getConn();
98
    if (adminUserExists(conn, username)) {
99
      throw exception::UserError(std::string("Cannot create admin user " + username +
100
101
        " because an admin user with the same name already exists"));
    }
102
    const uint64_t now = time(nullptr);
Steven Murray's avatar
Steven Murray committed
103
104
105
106
107
108
109
110
111
112
113
114
115
    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)"
116
      "VALUES("
Steven Murray's avatar
Steven Murray committed
117
118
119
120
121
122
123
124
        ":ADMIN_USER_NAME,"

        ":USER_COMMENT,"

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

125
126
127
        ":LAST_UPDATE_USER_NAME,"
        ":LAST_UPDATE_HOST_NAME,"
        ":LAST_UPDATE_TIME)";
128
    auto stmt = conn.createStmt(sql);
Steven Murray's avatar
Steven Murray committed
129

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

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

134
135
136
    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
137

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

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

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

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

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

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

Steven Murray's avatar
Steven Murray committed
207
        "USER_COMMENT AS USER_COMMENT,"
208

209
210
211
        "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,"
212

213
214
215
        "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 "
216
      "FROM "
217
218
219
        "ADMIN_USER "
      "ORDER BY "
        "ADMIN_USER_NAME";
220
    auto conn = m_connPool.getConn();
221
    auto stmt = conn.createStmt(sql);
222
    auto rset = stmt.executeQuery();
223
    while (rset.next()) {
Steven Murray's avatar
Steven Murray committed
224
      common::dataStructures::AdminUser admin;
225

226
227
228
229
230
231
232
233
      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");
234

Steven Murray's avatar
Steven Murray committed
235
236
      admins.push_back(admin);
    }
237

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

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

    if(comment.empty()) {
258
      throw UserSpecifiedAnEmptyStringComment("Cannot modify admin user because the comment is an empty string");
259
260
    }

261
262
263
264
265
266
267
    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 "
268
      "WHERE "
269
270
        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
    auto conn = m_connPool.getConn();
271
    auto stmt = conn.createStmt(sql);
272
273
274
275
276
    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);
277
    stmt.executeNonQuery();
278
279

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

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

    if(storageClass.name.empty()) {
303
304
      throw UserSpecifiedAnEmptyStringStorageClassName("Cannot create storage class because the storage class name is"
        " an empty string");
305
306
307
    }

    if(storageClass.comment.empty()) {
308
      throw UserSpecifiedAnEmptyStringComment("Cannot create storage class because the comment is an empty string");
309
310
    }

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);
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
    auto stmt = conn.createStmt(sql);
391
392
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
393
    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);
416

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

420
    stmt.executeNonQuery();
421
    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
    auto stmt = conn.createStmt(sql);
460
    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
    auto stmt = conn.createStmt(sql);
505
506
507
508
509
510
    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);
511
    stmt.executeNonQuery();
512
513

    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
    auto stmt = conn.createStmt(sql);
543
544
545
546
547
548
    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);
549
    stmt.executeNonQuery();
550
551

    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
  const uint64_t nbPartialTapes,
  const bool encryptionValue,
572
  const cta::optional<std::string> &supply,
573
  const std::string &comment) {
Steven Murray's avatar
Steven Murray committed
574
  try {
575
576
577
578
579
    if(name.empty()) {
      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create tape pool because the tape pool name is an empty string");
    }

    if(vo.empty()) {
580
      throw UserSpecifiedAnEmptyStringVo("Cannot create tape pool because the VO is an empty string");
581
582
583
    }

    if(comment.empty()) {
584
      throw UserSpecifiedAnEmptyStringComment("Cannot create tape pool because the comment is an empty string");
585
586
    }

587
    auto conn = m_connPool.getConn();
588

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

        "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
        ":NB_PARTIAL_TAPES,"
        ":IS_ENCRYPTED,"
616
        ":SUPPLY,"
Steven Murray's avatar
Steven Murray committed
617
618
619
620
621
622
623

        ":USER_COMMENT,"

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

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

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

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

637
638
639
    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
640

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

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

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

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

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

717
718
719
//------------------------------------------------------------------------------
// diskFileIdExists
//------------------------------------------------------------------------------
720
bool RdbmsCatalogue::diskFileIdExists(rdbms::Conn &conn, const std::string &diskInstanceName,
721
722
723
724
725
726
727
728
729
730
731
  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";
732
    auto stmt = conn.createStmt(sql);
733
734
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_ID", diskFileId);
735
    auto rset = stmt.executeQuery();
736
    return rset.next();
737
738
  } catch(exception::UserError &) {
    throw;
739
740
741
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
742
743
744
  }
}

745
746
747
//------------------------------------------------------------------------------
// diskFilePathExists
//------------------------------------------------------------------------------
748
bool RdbmsCatalogue::diskFilePathExists(rdbms::Conn &conn, const std::string &diskInstanceName,
749
750
751
752
753
754
755
756
757
758
759
  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";
760
    auto stmt = conn.createStmt(sql);
761
762
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_PATH", diskFilePath);
763
    auto rset = stmt.executeQuery();
764
    return rset.next();
765
766
  } catch(exception::UserError &) {
    throw;
767
768
769
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
770
771
772
  }
}

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

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

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

861
862
863
//------------------------------------------------------------------------------
// deleteTapePool
//------------------------------------------------------------------------------
864
void RdbmsCatalogue::deleteTapePool(const std::string &name) {
865
  try {
866
    auto conn = m_connPool.getConn();
867
    const uint64_t nbTapesInPool = getNbTapesInPool(conn, name);
868

869
870
871
872
873
874
875
876
877
878
879
    if(0 == nbTapesInPool) {
      const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME";
      auto stmt = conn.createStmt(sql);
      stmt.bindString(":TAPE_POOL_NAME", name);
      stmt.executeNonQuery();

      if(0 == stmt.getNbAffectedRows()) {
        throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist");
      }
    } else {
      throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it is not empty");
880
    }
881
  } catch(exception::UserError &) {
882
    throw;
883
884
885
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
886
  }
Steven Murray's avatar
Steven Murray committed
887
}
888
889
890
891

//------------------------------------------------------------------------------
// getTapePools
//------------------------------------------------------------------------------
892
std::list<TapePool> RdbmsCatalogue::getTapePools() const {
Steven Murray's avatar
Steven Murray committed
893
  try {
894
    std::list<TapePool> pools;
Steven Murray's avatar
Steven Murray committed
895
896
    const char *const sql =
      "SELECT "
897
        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
898
        "COALESCE(TAPE_POOL.VO, 'NONE') AS VO," // TBD Remove COALESCE
899
900
        "TAPE_POOL.NB_PARTIAL_TAPES AS NB_PARTIAL_TAPES,"
        "TAPE_POOL.IS_ENCRYPTED AS IS_ENCRYPTED,"
901
        "TAPE_POOL.SUPPLY AS SUPPLY,"
Steven Murray's avatar
Steven Murray committed
902

903
        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
904
905
906
        "COALESCE(SUM(TAPE.CAPACITY_IN_BYTES), 0) AS CAPACITY_IN_BYTES,"
        "COALESCE(SUM(TAPE.DATA_IN_BYTES), 0) AS DATA_IN_BYTES,"
        "COALESCE(SUM(TAPE.LAST_FSEQ), 0) AS NB_PHYSICAL_FILES,"
Steven Murray's avatar
Steven Murray committed
907

908
        "TAPE_POOL.USER_COMMENT AS USER_COMMENT,"
Steven Murray's avatar
Steven Murray committed
909