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

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

52
53
54
namespace cta {
namespace catalogue {

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

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

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

    if(comment.empty()) {
92
      throw UserSpecifiedAnEmptyStringComment("Cannot create admin user 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);
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
    auto stmt = conn.createStmt(sql);
162
    stmt.bindString(":ADMIN_USER_NAME", adminUsername);
163
    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
    auto stmt = conn.createStmt(sql);
181
    stmt.bindString(":ADMIN_USER_NAME", username);
182
    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
    auto stmt = conn.createStmt(sql);
220
    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
    if(username.empty()) {
      throw UserSpecifiedAnEmptyStringUsername("Cannot modify admin user because the username is an empty string");
    }

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

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

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

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

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

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

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

        ":USER_COMMENT,"

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

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

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

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

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

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

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

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

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

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

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

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

        "USER_COMMENT AS USER_COMMENT,"

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

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

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

      storageClasses.push_back(storageClass);
474
475
    }

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

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

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

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

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

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

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

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

584
    auto conn = m_connPool.getConn();
585

586
    if(tapePoolExists(conn, name)) {
587
      throw exception::UserError(std::string("Cannot create tape pool ") + name +
588
589
        " because a tape pool with the same name already exists");
    }
590
    const time_t now = time(nullptr);
Steven Murray's avatar
Steven Murray committed
591
592
593
    const char *const sql =
      "INSERT INTO TAPE_POOL("
        "TAPE_POOL_NAME,"
594
        "VO,"
Steven Murray's avatar
Steven Murray committed
595
596
597
598
599
600
601
602
603
604
605
606
        "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)"
607
      "VALUES("
Steven Murray's avatar
Steven Murray committed
608
        ":TAPE_POOL_NAME,"
609
        ":VO,"
Steven Murray's avatar
Steven Murray committed
610
611
612
613
614
615
616
617
618
        ":NB_PARTIAL_TAPES,"
        ":IS_ENCRYPTED,"

        ":USER_COMMENT,"

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

619
620
621
        ":LAST_UPDATE_USER_NAME,"
        ":LAST_UPDATE_HOST_NAME,"
        ":LAST_UPDATE_TIME)";
622
    auto stmt = conn.createStmt(sql);
Steven Murray's avatar
Steven Murray committed
623

624
    stmt.bindString(":TAPE_POOL_NAME", name);
625
    stmt.bindString(":VO", vo);
626
627
    stmt.bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes);
    stmt.bindBool(":IS_ENCRYPTED", encryptionValue);
Steven Murray's avatar
Steven Murray committed
628

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

631
632
633
    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
634

635
636
637
    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
    stmt.bindUint64(":LAST_UPDATE_TIME", now);
638

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

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

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

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

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

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

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

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

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

855
856
857
//------------------------------------------------------------------------------
// deleteTapePool
//------------------------------------------------------------------------------
858
void RdbmsCatalogue::deleteTapePool(const std::string &name) {
859
  try {
860
    const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME";
861
    auto conn = m_connPool.getConn();
862
    auto stmt = conn.createStmt(sql);
863
    stmt.bindString(":TAPE_POOL_NAME", name);
864
    stmt.executeNonQuery();
865

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

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

890
        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
891
892
893
        "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
894

895
        "TAPE_POOL.USER_COMMENT AS USER_COMMENT,"
Steven Murray's avatar
Steven Murray committed
896

897
898
899
900
901
902
903
        "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 "
904
      "FROM "
905
        "TAPE_POOL "
906
907
908
909
      "LEFT OUTER JOIN TAPE ON "
        "TAPE_POOL.TAPE_POOL_NAME = TAPE.TAPE_POOL_NAME "
      "GROUP BY "
        "TAPE_POOL.TAPE_POOL_NAME,"
910
        "TAPE_POOL.VO,"
911
912
913
914
915
916
917
918
919
        "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 "
920
921
      "ORDER BY "
        "TAPE_POOL_NAME";
922

923
    auto conn = m_connPool.getConn();
924
    auto stmt = conn.createStmt(sql);
925
    auto rset = stmt.executeQuery();
926
    while (rset.next()) {
927
      TapePool pool;
Steven Murray's avatar
Steven Murray committed
928