1
/* Copyright (C) 2009 PrimeBase Technologies GmbH, Germany
3
* PrimeBase Media Stream for MySQL
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
* System backup info table for repository backups.
27
#include <drizzled/common.h>
28
#include <drizzled/session.h>
29
#include <drizzled/sql_lex.h>
32
#include "cslib/CSConfig.h"
35
#include <sys/types.h>
40
//#include "mysql_priv.h"
41
#include "cslib/CSGlobal.h"
42
#include "cslib/CSStrUtil.h"
43
#include "cslib/CSLog.h"
44
#include "cslib/CSPath.h"
45
#include "cslib/CSDirectory.h"
51
#include "database_ms.h"
52
#include "open_table_ms.h"
53
#include "discover_ms.h"
54
#include "systab_util_ms.h"
55
#include "backup_ms.h"
57
#include "systab_backup_ms.h"
60
DT_FIELD_INFO pbms_backup_info[]=
62
{"Id", NOVAL, NULL, MYSQL_TYPE_LONG, NULL, NOT_NULL_FLAG, "The backup reference ID"},
63
{"Database_Name", 64, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, NOT_NULL_FLAG, "The database name"},
64
{"Database_Id", NOVAL, NULL, MYSQL_TYPE_LONG, NULL, 0, "The database ID"},
65
{"Started", 32, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "The start time"},
66
{"Completed", 32, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "The completion time"},
67
{"IsRunning", 3, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "Is the backup still running"},
68
{"IsDump", 3, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "Is the backup the result of a dump"},
69
{"Location", 1024, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "The backup location"},
70
{"Cloud_Ref", NOVAL, NULL, MYSQL_TYPE_LONG, NULL, 0, "The S3 cloud reference number refering to the pbms.pbms_cloud table."},
71
{"Cloud_Backup_No", NOVAL, NULL, MYSQL_TYPE_LONG, NULL, 0, "The cloud backup number"},
72
{NULL,NOVAL, NULL, MYSQL_TYPE_STRING,NULL, 0, NULL}
75
DT_KEY_INFO pbms_backup_keys[]=
77
{"pbms_backup_pk", PRI_KEY_FLAG, {"Id", NULL}},
81
#define MIN_BACKUP_TABLE_FILE_SIZE 4
84
//----------------------------
85
void MSBackupTable::startUp()
87
MSBackupInfo::startUp();
90
//----------------------------
91
void MSBackupTable::shutDown()
93
MSBackupInfo::shutDown();
96
//----------------------------
97
void MSBackupTable::loadTable(MSDatabase *db)
103
lock_(MSBackupInfo::gBackupInfo);
105
if (MSBackupInfo::gMaxInfoRef == 0) {
107
path = getSysFile(getPBMSPath(RETAIN(db->myDatabasePath)), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);
110
if (path->exists()) {
112
SysTabRec *backupData;
113
const char *name, *location;
114
uint32_t info_id, db_id, start, end, cloud_ref, cloud_backup_no;
119
new_(backupData, SysTabRec("pbms", BACKUP_TABLE_NAME".dat", BACKUP_TABLE_NAME));
122
file = path->openFile(CSFile::READONLY);
124
size = file->getEOF();
125
backupData->setLength(size);
126
file->read(backupData->getBuffer(0), 0, size, size);
129
backupData->firstRecord();
130
MSBackupInfo::gMaxInfoRef = backupData->getInt4Field();
132
if (! backupData->isValidRecord())
133
MSBackupInfo::gMaxInfoRef = 1;
135
while (backupData->nextRecord()) {
136
info_id = backupData->getInt4Field();
137
name = backupData->getStringField();
138
db_id = backupData->getInt4Field();
139
start = backupData->getInt4Field();
140
end = backupData->getInt4Field();
141
isDump = backupData->getInt1Field();
142
location = backupData->getStringField();
143
cloud_ref = backupData->getInt4Field();
144
cloud_backup_no = backupData->getInt4Field();
146
if (backupData->isValidRecord()) {
147
if (info_id > MSBackupInfo::gMaxInfoRef) {
149
snprintf(msg, 80, "backup info id (%"PRIu32") larger than expected (%"PRIu32")\n", info_id, MSBackupInfo::gMaxInfoRef);
150
CSL.log(self, CSLog::Warning, "pbms "BACKUP_TABLE_NAME".dat :possible damaged file or record. ");
151
CSL.log(self, CSLog::Warning, msg);
152
MSBackupInfo::gMaxInfoRef = info_id +1;
154
if ( MSBackupInfo::gBackupInfo->get(info_id)) {
156
snprintf(msg, 80, "Duplicate Backup info id (%"PRIu32") being ignored\n", info_id);
157
CSL.log(self, CSLog::Warning, "pbms "BACKUP_TABLE_NAME".dat :possible damaged file or record. ");
158
CSL.log(self, CSLog::Warning, msg);
160
new_(info, MSBackupInfo(info_id, name, db_id, start, end, isDump, location, cloud_ref, cloud_backup_no));
161
MSBackupInfo::gBackupInfo->set(info_id, info);
165
release_(backupData); backupData = NULL;
168
MSBackupInfo::gMaxInfoRef = 1;
173
unlock_(MSBackupInfo::gBackupInfo);
180
void MSBackupTable::saveTable(MSDatabase *db)
182
SysTabRec *backupData;
188
new_(backupData, SysTabRec("pbms", BACKUP_TABLE_NAME".dat", BACKUP_TABLE_NAME));
191
// Build the table records
193
lock_(MSBackupInfo::gBackupInfo);
195
backupData->beginRecord();
196
backupData->setInt4Field(MSBackupInfo::gMaxInfoRef);
197
backupData->endRecord();
198
for (int i = 0;(info = (MSBackupInfo*) MSBackupInfo::gBackupInfo->itemAt(i)); i++) { // info is not referenced.
200
backupData->beginRecord();
201
backupData->setInt4Field(info->getBackupRefId());
203
backupData->setStringField(info->getName());
204
backupData->setInt4Field(info->getDatabaseId());
205
backupData->setInt4Field(info->getStart());
206
backupData->setInt4Field(info->getEnd());
207
backupData->setInt1Field(info->isDump());
208
backupData->setStringField(info->getLocation());
209
backupData->setInt4Field(info->getcloudRef());
210
backupData->setInt4Field(info->getcloudBackupNo());
211
backupData->endRecord();
213
unlock_(MSBackupInfo::gBackupInfo);
215
restoreTable(RETAIN(db), backupData->getBuffer(0), backupData->length(), false);
217
release_(backupData);
223
MSBackupTable::MSBackupTable(MSSystemTableShare *share, TABLE *table):
224
MSOpenSystemTable(share, table),
229
MSBackupTable::~MSBackupTable()
234
void MSBackupTable::use()
236
MSBackupInfo::gBackupInfo->lock();
239
void MSBackupTable::unuse()
241
MSBackupInfo::gBackupInfo->unlock();
246
void MSBackupTable::seqScanInit()
251
bool MSBackupTable::seqScanNext(char *buf)
253
TABLE *table = mySQLTable;
256
MY_BITMAP *save_write_set;
263
info = (MSBackupInfo *) MSBackupInfo::gBackupInfo->itemAt(iBackupIndex++); // Object is not referenced.
267
save_write_set = table->write_set;
268
table->write_set = NULL;
270
new_(timeVal, CSTime());
273
memset(buf, 0xFF, table->getNullBytes());
275
memset(buf, 0xFF, table->s->null_bytes);
277
for (Field **field=GET_TABLE_FIELDS(table) ; *field ; field++) {
279
save = curr_field->ptr;
280
#if MYSQL_VERSION_ID < 50114
281
curr_field->ptr = (byte *) buf + curr_field->offset();
284
curr_field->ptr = (byte *) buf + curr_field->offset(curr_field->getTable()->getInsertRecord());
286
curr_field->ptr = (byte *) buf + curr_field->offset(curr_field->table->record[0]);
289
switch (curr_field->field_name[0]) {
291
if (curr_field->field_name[1] == 'd') {
292
ASSERT(strcmp(curr_field->field_name, "Id") == 0);
293
curr_field->store(info->getBackupRefId(), true);
294
setNotNullInRecord(curr_field, buf);
295
} else if (curr_field->field_name[2] == 'D') {
296
ASSERT(strcmp(curr_field->field_name, "IsDump") == 0);
297
val = (info->isDump())? "Yes": "No";
298
curr_field->store(val, strlen(val), &UTF8_CHARSET);
299
setNotNullInRecord(curr_field, buf);
301
ASSERT(strcmp(curr_field->field_name, "IsRunning") == 0);
302
val = (info->isBackupRunning())? "Yes": "No";
303
curr_field->store(val, strlen(val), &UTF8_CHARSET);
304
setNotNullInRecord(curr_field, buf);
309
if (curr_field->field_name[9] == 'I') {
310
ASSERT(strcmp(curr_field->field_name, "Database_Id") == 0);
311
curr_field->store(info->getDatabaseId(), true);
312
setNotNullInRecord(curr_field, buf);
314
ASSERT(strcmp(curr_field->field_name, "Database_Name") == 0);
315
val = info->getName();
316
curr_field->store(val, strlen(val), &UTF8_CHARSET);
317
setNotNullInRecord(curr_field, buf);
323
ASSERT(strcmp(curr_field->field_name, "Started") == 0);
324
if (info->getStart()) {
325
timeVal->setUTC1970(info->getStart(), 0);
326
val = timeVal->getCString();
327
curr_field->store(val, strlen(val), &UTF8_CHARSET);
328
setNotNullInRecord(curr_field, buf);
333
ASSERT(strcmp(curr_field->field_name, "Location") == 0);
334
val = info->getLocation();
336
curr_field->store(val, strlen(val), &UTF8_CHARSET);
337
setNotNullInRecord(curr_field, buf);
342
if (curr_field->field_name[1] == 'o') {
343
ASSERT(strcmp(curr_field->field_name, "Completed") == 0);
344
if (info->getEnd()) {
345
timeVal->setUTC1970(info->getEnd(), 0);
346
val = timeVal->getCString();
347
curr_field->store(val, strlen(val), &UTF8_CHARSET);
348
setNotNullInRecord(curr_field, buf);
350
} else if (curr_field->field_name[6] == 'R') {
351
ASSERT(strcmp(curr_field->field_name, "Cloud_Ref") == 0);
352
curr_field->store(info->getcloudRef(), true);
353
setNotNullInRecord(curr_field, buf);
354
} else if (curr_field->field_name[6] == 'B') {
355
ASSERT(strcmp(curr_field->field_name, "Cloud_Backup_No") == 0);
356
curr_field->store(info->getcloudBackupNo(), true);
357
setNotNullInRecord(curr_field, buf);
367
curr_field->ptr = save;
371
table->write_set = save_write_set;
376
void MSBackupTable::seqScanPos(unsigned char *pos)
378
int32_t index = iBackupIndex -1;
380
index = 0; // This is probably an error condition.
382
mi_int4store(pos, index);
385
void MSBackupTable::seqScanRead(unsigned char *pos, char *buf)
387
iBackupIndex = mi_uint4korr(pos);
391
void MSBackupTable::updateRow(char *old_data, char *new_data)
393
uint32_t n_id, db_id, cloud_ref, cloud_backup_no, n_indx;
394
uint32_t o_id, o_db_id, o_cloud_ref, o_cloud_backup_no, o_indx;
395
String name, start, end, isRunning, isDump, location;
396
String o_name, o_start, o_end, o_isRunning, o_isDump, o_location;
397
MSBackupInfo *info, *old_info;
401
getFieldValue(new_data, 0, &n_id);
402
getFieldValue(new_data, 1, &name);
403
getFieldValue(new_data, 2, &db_id);
404
getFieldValue(new_data, 3, &start);
405
getFieldValue(new_data, 4, &end);
406
getFieldValue(new_data, 5, &isRunning);
407
getFieldValue(new_data, 6, &isDump);
408
getFieldValue(new_data, 7, &location);
409
getFieldValue(new_data, 8, &cloud_ref);
410
getFieldValue(new_data, 9, &cloud_backup_no);
412
getFieldValue(old_data, 0, &o_id);
413
getFieldValue(old_data, 1, &o_name);
414
getFieldValue(old_data, 2, &o_db_id);
415
getFieldValue(old_data, 3, &o_start);
416
getFieldValue(old_data, 4, &o_end);
417
getFieldValue(old_data, 5, &o_isRunning);
418
getFieldValue(old_data, 6, &o_isDump);
419
getFieldValue(old_data, 7, &o_location);
420
getFieldValue(old_data, 8, &o_cloud_ref);
421
getFieldValue(old_data, 9, &o_cloud_backup_no);
423
// The only fields that are allowed to be updated are 'Location' and 'Cloud_Ref'.
424
// It makes no scence to update any of the other fields.
426
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Id) in the "BACKUP_TABLE_NAME" table.");
428
if (strcmp(name.c_ptr(), o_name.c_ptr()) == 0 )
429
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Database_Name) in the "BACKUP_TABLE_NAME" table.");
431
if (db_id != o_db_id )
432
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Database_Id) in the "BACKUP_TABLE_NAME" table.");
434
if (strcmp(start.c_ptr(), o_start.c_ptr()) == 0 )
435
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Started) in the "BACKUP_TABLE_NAME" table.");
437
if (strcmp(end.c_ptr(), o_end.c_ptr()) == 0 )
438
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Completed) in the "BACKUP_TABLE_NAME" table.");
440
if (strcmp(isRunning.c_ptr(), o_isRunning.c_ptr()) == 0 )
441
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (isRunning) in the "BACKUP_TABLE_NAME" table.");
443
if (strcmp(isDump.c_ptr(), o_isDump.c_ptr()) == 0 )
444
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (IsDump) in the "BACKUP_TABLE_NAME" table.");
446
if (cloud_backup_no != o_cloud_backup_no )
447
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Cloud_Backup_No) in the "BACKUP_TABLE_NAME" table.");
449
old_info = (MSBackupInfo*) MSBackupInfo::gBackupInfo->get(o_id); // A non referenced object.
451
new_(info, MSBackupInfo(n_id, old_info->getName(), db_id, old_info->getStart(), old_info->getEnd(), old_info->isDump(), location.c_ptr(), cloud_ref, cloud_backup_no));
454
o_indx = MSBackupInfo::gBackupInfo->getIndex(o_id);
456
MSBackupInfo::gBackupInfo->remove(o_id);
458
MSBackupInfo::gBackupInfo->set(n_id, info);
460
// Adjust the current position in the array if required.
461
n_indx = MSBackupInfo::gBackupInfo->getIndex(n_id);
462
if (o_indx < n_indx )
465
saveTable(RETAIN(myShare->mySysDatabase));
470
class InsertRowCleanUp : public CSRefObject {
478
InsertRowCleanUp(CSThread *self): CSRefObject(),
479
do_cleanup(true), myself(self){}
484
myself->logException();
486
MSBackupInfo::gBackupInfo->remove(ref_id);
491
void setCleanUp(uint32_t id)
503
void MSBackupTable::insertRow(char *data)
505
uint32_t ref_id = 0, db_id, cloud_ref, cloud_backup_no;
506
String name, start, end, isRunning, isDump, location;
507
MSBackupInfo *info = NULL;
509
InsertRowCleanUp *cleanup;
513
new_(cleanup, InsertRowCleanUp(self));
516
getFieldValue(data, 0, &ref_id);
518
// The id must be unique.
519
if (ref_id && MSBackupInfo::gBackupInfo->get(ref_id)) {
520
CSException::throwException(CS_CONTEXT, MS_ERR_DUPLICATE, "Attempt to insert a row with a duplicate key in the "BACKUP_TABLE_NAME" table.");
523
// The 'Database_Id', 'Start', 'Completion' and "IsDump" fields are ignored.
524
// I still need to get the fields though to advance the field position pointer.
525
getFieldValue(data, 1, &name);
526
getFieldValue(data, 2, &db_id);
527
getFieldValue(data, 3, &start);
528
getFieldValue(data, 4, &end);
529
getFieldValue(data, 5, &isRunning);
530
getFieldValue(data, 6, &isDump);
531
getFieldValue(data, 7, &location);
532
getFieldValue(data, 8, &cloud_ref);
533
getFieldValue(data, 9, &cloud_backup_no);
536
ref_id = MSBackupInfo::gMaxInfoRef++;
537
else if (ref_id >= MSBackupInfo::gMaxInfoRef)
538
MSBackupInfo::gMaxInfoRef = ref_id +1;
540
db_name = name.c_ptr();
541
db_id = MSDatabase::getDatabaseID(db_name, false);
543
cleanup->setCleanUp(ref_id);
544
new_(info, MSBackupInfo(ref_id, db_name, db_id, 0, 0, false, location.c_ptr(), cloud_ref, cloud_backup_no));
545
MSBackupInfo::gBackupInfo->set(ref_id, info);
547
// There is no need to call this now, startBackup() will call it
548
// after the backup is started.
549
// saveTable(RETAIN(myShare->mySysDatabase));
550
info->startBackup(RETAIN(myShare->mySysDatabase));
552
cleanup->cancelCleanUp();
558
void MSBackupTable::deleteRow(char *data)
560
uint32_t ref_id, indx;
564
getFieldValue(data, 0, &ref_id);
566
// Adjust the current position in the array if required.
567
indx = MSBackupInfo::gBackupInfo->getIndex(ref_id);
568
if (indx <= iBackupIndex)
571
MSBackupInfo::gBackupInfo->remove(ref_id);
572
saveTable(RETAIN(myShare->mySysDatabase));
576
void MSBackupTable::transferTable(MSDatabase *to_db, MSDatabase *from_db)
584
path = CSPath::newPath(getPBMSPath(RETAIN(from_db->myDatabasePath)), BACKUP_TABLE_NAME".dat");
586
if (path->exists()) {
588
bu_path = CSPath::newPath(getPBMSPath(RETAIN(to_db->myDatabasePath)), BACKUP_TABLE_NAME".dat");
589
path->copyTo(bu_path, true);
599
CSStringBuffer *MSBackupTable::dumpTable(MSDatabase *db)
603
CSStringBuffer *dump;
608
path = getSysFile(getPBMSPath(RETAIN(db->myDatabasePath)), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);
612
new_(dump, CSStringBuffer(20));
615
if (path->exists()) {
619
file = path->openFile(CSFile::READONLY);
622
size = file->getEOF();
623
dump->setLength(size);
624
file->read(dump->getBuffer(0), 0, size, size);
633
void MSBackupTable::restoreTable(MSDatabase *db, const char *data, size_t size, bool reload)
641
path = getSysFile(getPBMSPath(RETAIN(db->myDatabasePath)), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);
644
file = path->openFile(CSFile::CREATE | CSFile::TRUNCATE);
647
file->write(data, 0, size);
662
// The cloud info table is only removed from the pbms database
663
// if there are no more databases.
664
void MSBackupTable::removeTable(CSString *db_path)
667
char pbms_path[PATH_MAX];
672
cs_strcpy(PATH_MAX, pbms_path, db_path->getCString());
675
if (strcmp(cs_last_name_of_path(pbms_path), "pbms") != 0)
678
cs_remove_last_name_of_path(pbms_path);
680
path = getSysFile(CSString::newString(pbms_path), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);