RdbmsCatalogue.cpp 214 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/UserSpecifiedAnEmptyStringTapePoolName.hpp"
33
#include "catalogue/UserSpecifiedAnEmptyStringUsername.hpp"
34
#include "catalogue/UserSpecifiedAnEmptyStringVendor.hpp"
35
#include "catalogue/UserSpecifiedAnEmptyStringVid.hpp"
36
#include "catalogue/UserSpecifiedAnEmptyStringVo.hpp"
37
#include "catalogue/UserSpecifiedAZeroCapacity.hpp"
38
#include "catalogue/UserSpecifiedAZeroCopyNb.hpp"
39
#include "common/dataStructures/TapeFile.hpp"
40
#include "common/exception/Exception.hpp"
41
#include "common/exception/UserError.hpp"
42
#include "common/make_unique.hpp"
43
#include "common/threading/MutexLocker.hpp"
44
#include "common/Timer.hpp"
45
#include "common/utils/utils.hpp"
46
#include "rdbms/AutoRollback.hpp"
47

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

53
54
55
namespace cta {
namespace catalogue {

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

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

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

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

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

        ":USER_COMMENT,"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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
    auto stmt = conn.createStmt(sql);
271
272
273
274
275
    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);
276
    stmt.executeNonQuery();
277
278

    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
    if(storageClass.diskInstance.empty()) {
297
298
      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot create storage class because the disk instance name is"
        " an empty string");
299
300
301
    }

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

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

310
    auto conn = m_connPool.getConn();
311
    if(storageClassExists(conn, storageClass.diskInstance, storageClass.name)) {
312
313
      throw exception::UserError(std::string("Cannot create storage class ") + storageClass.diskInstance + ":" +
        storageClass.name + " because it already exists");
314
    }
315
    const uint64_t storageClassId = getNextStorageClassId(conn);
316
    const time_t now = time(nullptr);
Steven Murray's avatar
Steven Murray committed
317
318
    const char *const sql =
      "INSERT INTO STORAGE_CLASS("
319
        "STORAGE_CLASS_ID,"
320
        "DISK_INSTANCE_NAME,"
Steven Murray's avatar
Steven Murray committed
321
322
323
324
325
326
327
328
329
330
331
332
        "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)"
333
      "VALUES("
334
        ":STORAGE_CLASS_ID,"
335
        ":DISK_INSTANCE_NAME,"
Steven Murray's avatar
Steven Murray committed
336
337
338
339
340
341
342
343
344
        ":STORAGE_CLASS_NAME,"
        ":NB_COPIES,"

        ":USER_COMMENT,"

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

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

350
    stmt.bindUint64(":STORAGE_CLASS_ID", storageClassId);
351
352
353
    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
354

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

357
358
359
    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
360

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

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

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

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

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

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

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

        "USER_COMMENT AS USER_COMMENT,"

446
447
448
        "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
449

450
451
452
        "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 "
453
      "FROM "
454
455
456
        "STORAGE_CLASS "
      "ORDER BY "
        "DISK_INSTANCE_NAME, STORAGE_CLASS_NAME";
457
    auto conn = m_connPool.getConn();
458
    auto stmt = conn.createStmt(sql);
459
    auto rset = stmt.executeQuery();
460
    while (rset.next()) {
Steven Murray's avatar
Steven Murray committed
461
462
      common::dataStructures::StorageClass storageClass;

463
464
465
466
467
468
469
470
471
472
      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
473
474

      storageClasses.push_back(storageClass);
475
476
    }

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

//------------------------------------------------------------------------------
// modifyStorageClassNbCopies
//------------------------------------------------------------------------------
Steven Murray's avatar
Steven Murray committed
489
void RdbmsCatalogue::modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
490
491
492
493
494
495
496
497
498
  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 "
499
      "WHERE "
500
501
502
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
    auto conn = m_connPool.getConn();
503
    auto stmt = conn.createStmt(sql);
504
505
506
507
508
509
    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);
510
    stmt.executeNonQuery();
511
512

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

//------------------------------------------------------------------------------
// modifyStorageClassComment
//------------------------------------------------------------------------------
Steven Murray's avatar
Steven Murray committed
527
void RdbmsCatalogue::modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
528
529
530
531
532
533
534
535
536
  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 "
537
      "WHERE "
538
539
540
        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
    auto conn = m_connPool.getConn();
541
    auto stmt = conn.createStmt(sql);
542
543
544
545
546
547
    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);
548
    stmt.executeNonQuery();
549
550

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

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

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

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

586
    auto conn = m_connPool.getConn();
587

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

        "USER_COMMENT,"

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

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

        ":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);
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);
632
    stmt.bindOptionalString(":SUPPLY", supply);
Steven Murray's avatar
Steven Murray committed
633

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

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

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

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

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

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

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

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

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

772
773
774
//------------------------------------------------------------------------------
// diskFileUserExists
//------------------------------------------------------------------------------
775
bool RdbmsCatalogue::diskFileUserExists(rdbms::Conn &conn, const std::string &diskInstanceName,
776
777
778
779
780
781
782
783
784
785
786
  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";
787
    auto stmt = conn.createStmt(sql);
788
789
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_USER", diskFileUser);
790
    auto rset = stmt.executeQuery();
791
    return rset.next();
792
793
  } catch(exception::UserError &) {
    throw;
794
795
796
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
797
798
799
  }
}

800
801
802
//------------------------------------------------------------------------------
// diskFileGroupExists
//------------------------------------------------------------------------------
803
bool RdbmsCatalogue::diskFileGroupExists(rdbms::Conn &conn, const std::string &diskInstanceName,
804
805
806
807
808
809
810
811
812
813
814
  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";
815
    auto stmt = conn.createStmt(sql);
816
817
    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
    stmt.bindString(":DISK_FILE_GROUP", diskFileGroup);
818
    auto rset = stmt.executeQuery();
819
    return rset.next();
820
821
  } catch(exception::UserError &) {
    throw;
822
823
824
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
825
826
827
  }
}

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

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

868
869
870
871
872
873
874
875
876
877
878
    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");
879
    }
880
  } catch(exception::UserError &) {
881
    throw;
882
883
884
  } catch(exception::Exception &ex) {
    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
    throw;
885
  }
Steven Murray's avatar
Steven Murray committed
886
}
887
888
889
890

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

902
        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
903
904
905
        "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
906

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

909
910
911
912
913
914
915
        "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 "
916
      "FROM "
917
        "TAPE_POOL "
918
919
920
921
      "LEFT OUTER JOIN TAPE ON "
        "TAPE_POOL.TAPE_POOL_NAME = TAPE.TAPE_POOL_NAME "
      "GROUP BY "
        "TAPE_POOL.TAPE_POOL_NAME,"
922
        "TAPE_POOL.VO,"
923
924
        "TAPE_POOL.NB_PARTIAL_TAPES,"
        "TAPE_POOL.IS_ENCRYPTED,"
925
        "TAPE_POOL.SUPPLY,"
926
927
928
929
930
931
932
        "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 "