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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
* System backup info table for repository backups.
27
#include <drizzled/common.h>
28
#include <drizzled/session.h>
31
#include "cslib/CSConfig.h"
34
#include <sys/types.h>
39
//#include "mysql_priv.h"
40
#include "cslib/CSGlobal.h"
41
#include "cslib/CSStrUtil.h"
42
#include "cslib/CSLog.h"
43
#include "cslib/CSPath.h"
44
#include "cslib/CSDirectory.h"
50
#include "database_ms.h"
51
#include "open_table_ms.h"
52
#include "discover_ms.h"
53
#include "systab_util_ms.h"
54
#include "backup_ms.h"
56
#include "systab_backup_ms.h"
59
DT_FIELD_INFO pbms_backup_info[]=
61
{"Id", NULL, NULL, MYSQL_TYPE_LONG, NULL, NOT_NULL_FLAG, "The backup reference ID"},
62
{"Database_Name", 64, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, NOT_NULL_FLAG, "The database name"},
63
{"Database_Id", NULL, NULL, MYSQL_TYPE_LONG, NULL, 0, "The database ID"},
64
{"Started", 32, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "The start time"},
65
{"Completed", 32, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "The completion time"},
66
{"IsRunning", 3, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "Is the backup still running"},
67
{"IsDump", 3, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "Is the backup the result of a dump"},
68
{"Location", 1024, NULL, MYSQL_TYPE_VARCHAR, &UTF8_CHARSET, 0, "The backup location"},
69
{"Cloud_Ref", NULL, NULL, MYSQL_TYPE_LONG, NULL, 0, "The S3 cloud reference number refering to the pbms.pbms_cloud table."},
70
{"Cloud_Backup_No", NULL, NULL, MYSQL_TYPE_LONG, NULL, 0, "The cloud backup number"},
71
{NULL,NULL, NULL, MYSQL_TYPE_STRING,NULL, 0, NULL}
74
DT_KEY_INFO pbms_backup_keys[]=
76
{"pbms_backup_pk", PRI_KEY_FLAG, {"Id", NULL}},
80
#define MIN_BACKUP_TABLE_FILE_SIZE 4
83
//----------------------------
84
void MSBackupTable::startUp()
86
MSBackupInfo::startUp();
89
//----------------------------
90
void MSBackupTable::shutDown()
92
MSBackupInfo::shutDown();
95
//----------------------------
96
void MSBackupTable::loadTable(MSDatabase *db)
102
lock_(MSBackupInfo::gBackupInfo);
104
if (MSBackupInfo::gMaxInfoRef == 0) {
106
path = getSysFile(getPBMSPath(RETAIN(db->myDatabasePath)), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);
109
if (path->exists()) {
111
SysTabRec *backupData;
112
const char *name, *location;
113
uint32_t info_id, db_id, start, end, cloud_ref, cloud_backup_no;
118
new_(backupData, SysTabRec("pbms", BACKUP_TABLE_NAME".dat", BACKUP_TABLE_NAME));
121
file = path->openFile(CSFile::READONLY);
123
size = file->getEOF();
124
backupData->setLength(size);
125
file->read(backupData->getBuffer(0), 0, size, size);
128
backupData->firstRecord();
129
MSBackupInfo::gMaxInfoRef = backupData->getInt4Field();
131
if (! backupData->isValidRecord())
132
MSBackupInfo::gMaxInfoRef = 1;
134
while (backupData->nextRecord()) {
135
info_id = backupData->getInt4Field();
136
name = backupData->getStringField();
137
db_id = backupData->getInt4Field();
138
start = backupData->getInt4Field();
139
end = backupData->getInt4Field();
140
isDump = backupData->getInt1Field();
141
location = backupData->getStringField();
142
cloud_ref = backupData->getInt4Field();
143
cloud_backup_no = backupData->getInt4Field();
145
if (backupData->isValidRecord()) {
146
if (info_id > MSBackupInfo::gMaxInfoRef) {
148
snprintf(msg, 80, "backup info id (%"PRIu32") larger than expected (%"PRIu32")\n", info_id, MSBackupInfo::gMaxInfoRef);
149
CSL.log(self, CSLog::Warning, "pbms "BACKUP_TABLE_NAME".dat :possible damaged file or record. ");
150
CSL.log(self, CSLog::Warning, msg);
151
MSBackupInfo::gMaxInfoRef = info_id +1;
153
if ( MSBackupInfo::gBackupInfo->get(info_id)) {
155
snprintf(msg, 80, "Duplicate Backup info id (%"PRIu32") being ignored\n", info_id);
156
CSL.log(self, CSLog::Warning, "pbms "BACKUP_TABLE_NAME".dat :possible damaged file or record. ");
157
CSL.log(self, CSLog::Warning, msg);
159
new_(info, MSBackupInfo(info_id, name, db_id, start, end, isDump, location, cloud_ref, cloud_backup_no));
160
MSBackupInfo::gBackupInfo->set(info_id, info);
164
release_(backupData); backupData = NULL;
167
MSBackupInfo::gMaxInfoRef = 1;
172
unlock_(MSBackupInfo::gBackupInfo);
179
void MSBackupTable::saveTable(MSDatabase *db)
181
SysTabRec *backupData;
187
new_(backupData, SysTabRec("pbms", BACKUP_TABLE_NAME".dat", BACKUP_TABLE_NAME));
190
// Build the table records
192
lock_(MSBackupInfo::gBackupInfo);
194
backupData->beginRecord();
195
backupData->setInt4Field(MSBackupInfo::gMaxInfoRef);
196
backupData->endRecord();
197
for (int i = 0;(info = (MSBackupInfo*) MSBackupInfo::gBackupInfo->itemAt(i)); i++) { // info is not referenced.
199
backupData->beginRecord();
200
backupData->setInt4Field(info->getBackupRefId());
202
backupData->setStringField(info->getName());
203
backupData->setInt4Field(info->getDatabaseId());
204
backupData->setInt4Field(info->getStart());
205
backupData->setInt4Field(info->getEnd());
206
backupData->setInt1Field(info->isDump());
207
backupData->setStringField(info->getLocation());
208
backupData->setInt4Field(info->getcloudRef());
209
backupData->setInt4Field(info->getcloudBackupNo());
210
backupData->endRecord();
212
unlock_(MSBackupInfo::gBackupInfo);
214
restoreTable(RETAIN(db), backupData->getBuffer(0), backupData->length(), false);
216
release_(backupData);
222
MSBackupTable::MSBackupTable(MSSystemTableShare *share, TABLE *table):
223
MSOpenSystemTable(share, table),
228
MSBackupTable::~MSBackupTable()
233
void MSBackupTable::use()
235
MSBackupInfo::gBackupInfo->lock();
238
void MSBackupTable::unuse()
240
MSBackupInfo::gBackupInfo->unlock();
245
void MSBackupTable::seqScanInit()
250
bool MSBackupTable::seqScanNext(char *buf)
252
TABLE *table = mySQLTable;
255
MY_BITMAP *save_write_set;
262
info = (MSBackupInfo *) MSBackupInfo::gBackupInfo->itemAt(iBackupIndex++); // Object is not referenced.
266
save_write_set = table->write_set;
267
table->write_set = NULL;
269
new_(timeVal, CSTime());
271
memset(buf, 0xFF, table->s->null_bytes);
272
for (Field **field=GET_TABLE_FIELDS(table) ; *field ; field++) {
274
save = curr_field->ptr;
275
#if MYSQL_VERSION_ID < 50114
276
curr_field->ptr = (byte *) buf + curr_field->offset();
278
curr_field->ptr = (byte *) buf + curr_field->offset(curr_field->table->record[0]);
280
switch (curr_field->field_name[0]) {
282
if (curr_field->field_name[1] == 'd') {
283
ASSERT(strcmp(curr_field->field_name, "Id") == 0);
284
curr_field->store(info->getBackupRefId(), true);
285
setNotNullInRecord(curr_field, buf);
286
} else if (curr_field->field_name[2] == 'D') {
287
ASSERT(strcmp(curr_field->field_name, "IsDump") == 0);
288
val = (info->isDump())? "Yes": "No";
289
curr_field->store(val, strlen(val), &UTF8_CHARSET);
290
setNotNullInRecord(curr_field, buf);
292
ASSERT(strcmp(curr_field->field_name, "IsRunning") == 0);
293
val = (info->isBackupRunning())? "Yes": "No";
294
curr_field->store(val, strlen(val), &UTF8_CHARSET);
295
setNotNullInRecord(curr_field, buf);
300
if (curr_field->field_name[9] == 'I') {
301
ASSERT(strcmp(curr_field->field_name, "Database_Id") == 0);
302
curr_field->store(info->getDatabaseId(), true);
303
setNotNullInRecord(curr_field, buf);
305
ASSERT(strcmp(curr_field->field_name, "Database_Name") == 0);
306
val = info->getName();
307
curr_field->store(val, strlen(val), &UTF8_CHARSET);
308
setNotNullInRecord(curr_field, buf);
314
ASSERT(strcmp(curr_field->field_name, "Started") == 0);
315
if (info->getStart()) {
316
timeVal->setUTC1970(info->getStart(), 0);
317
val = timeVal->getCString();
318
curr_field->store(val, strlen(val), &UTF8_CHARSET);
319
setNotNullInRecord(curr_field, buf);
324
ASSERT(strcmp(curr_field->field_name, "Location") == 0);
325
val = info->getLocation();
327
curr_field->store(val, strlen(val), &UTF8_CHARSET);
328
setNotNullInRecord(curr_field, buf);
333
if (curr_field->field_name[1] == 'o') {
334
ASSERT(strcmp(curr_field->field_name, "Completed") == 0);
335
if (info->getEnd()) {
336
timeVal->setUTC1970(info->getEnd(), 0);
337
val = timeVal->getCString();
338
curr_field->store(val, strlen(val), &UTF8_CHARSET);
339
setNotNullInRecord(curr_field, buf);
341
} else if (curr_field->field_name[6] == 'R') {
342
ASSERT(strcmp(curr_field->field_name, "Cloud_Ref") == 0);
343
curr_field->store(info->getcloudRef(), true);
344
setNotNullInRecord(curr_field, buf);
345
} else if (curr_field->field_name[6] == 'B') {
346
ASSERT(strcmp(curr_field->field_name, "Cloud_Backup_No") == 0);
347
curr_field->store(info->getcloudBackupNo(), true);
348
setNotNullInRecord(curr_field, buf);
358
curr_field->ptr = save;
362
table->write_set = save_write_set;
367
void MSBackupTable::seqScanPos(uint8_t *pos)
369
int32_t index = iBackupIndex -1;
371
index = 0; // This is probably an error condition.
373
mi_int4store(pos, index);
376
void MSBackupTable::seqScanRead(uint8_t *pos, char *buf)
378
iBackupIndex = mi_uint4korr(pos);
382
void MSBackupTable::updateRow(char *old_data, char *new_data)
384
uint32_t n_id, db_id, cloud_ref, cloud_backup_no, n_indx;
385
uint32_t o_id, o_db_id, o_cloud_ref, o_cloud_backup_no, o_indx;
386
String name, start, end, isRunning, isDump, location;
387
String o_name, o_start, o_end, o_isRunning, o_isDump, o_location;
388
MSBackupInfo *info, *old_info;
392
getFieldValue(new_data, 0, &n_id);
393
getFieldValue(new_data, 1, &name);
394
getFieldValue(new_data, 2, &db_id);
395
getFieldValue(new_data, 3, &start);
396
getFieldValue(new_data, 4, &end);
397
getFieldValue(new_data, 5, &isRunning);
398
getFieldValue(new_data, 6, &isDump);
399
getFieldValue(new_data, 7, &location);
400
getFieldValue(new_data, 8, &cloud_ref);
401
getFieldValue(new_data, 9, &cloud_backup_no);
403
getFieldValue(old_data, 0, &o_id);
404
getFieldValue(old_data, 1, &o_name);
405
getFieldValue(old_data, 2, &o_db_id);
406
getFieldValue(old_data, 3, &o_start);
407
getFieldValue(old_data, 4, &o_end);
408
getFieldValue(old_data, 5, &o_isRunning);
409
getFieldValue(old_data, 6, &o_isDump);
410
getFieldValue(old_data, 7, &o_location);
411
getFieldValue(old_data, 8, &o_cloud_ref);
412
getFieldValue(old_data, 9, &o_cloud_backup_no);
414
// The only fields that are allowed to be updated are 'Location' and 'Cloud_Ref'.
415
// It makes no scence to update any of the other fields.
417
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Id) in the "BACKUP_TABLE_NAME" table.");
419
if (strcmp(name.c_ptr(), o_name.c_ptr()) == 0 )
420
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Database_Name) in the "BACKUP_TABLE_NAME" table.");
422
if (db_id != o_db_id )
423
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Database_Id) in the "BACKUP_TABLE_NAME" table.");
425
if (strcmp(start.c_ptr(), o_start.c_ptr()) == 0 )
426
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Started) in the "BACKUP_TABLE_NAME" table.");
428
if (strcmp(end.c_ptr(), o_end.c_ptr()) == 0 )
429
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Completed) in the "BACKUP_TABLE_NAME" table.");
431
if (strcmp(isRunning.c_ptr(), o_isRunning.c_ptr()) == 0 )
432
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (isRunning) in the "BACKUP_TABLE_NAME" table.");
434
if (strcmp(isDump.c_ptr(), o_isDump.c_ptr()) == 0 )
435
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (IsDump) in the "BACKUP_TABLE_NAME" table.");
437
if (cloud_backup_no != o_cloud_backup_no )
438
CSException::throwException(CS_CONTEXT, HA_ERR_TABLE_READONLY, "Attempt to update read only field (Cloud_Backup_No) in the "BACKUP_TABLE_NAME" table.");
440
old_info = (MSBackupInfo*) MSBackupInfo::gBackupInfo->get(o_id); // A non referenced object.
442
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));
445
o_indx = MSBackupInfo::gBackupInfo->getIndex(o_id);
447
MSBackupInfo::gBackupInfo->remove(o_id);
449
MSBackupInfo::gBackupInfo->set(n_id, info);
451
// Adjust the current position in the array if required.
452
n_indx = MSBackupInfo::gBackupInfo->getIndex(n_id);
453
if (o_indx < n_indx )
456
saveTable(RETAIN(myShare->mySysDatabase));
461
void MSBackupTable::insertRow(char *data)
463
uint32_t ref_id = 0, db_id, cloud_ref, cloud_backup_no;
464
String name, start, end, isRunning, isDump, location;
465
MSBackupInfo *info = NULL;
467
bool duplicate = true;
472
getFieldValue(data, 0, &ref_id);
474
// The id must be unique.
475
if (ref_id && MSBackupInfo::gBackupInfo->get(ref_id)) {
476
CSException::throwException(CS_CONTEXT, MS_ERR_DUPLICATE, "Attempt to insert a row with a duplicate key in the "BACKUP_TABLE_NAME" table.");
480
// The 'Database_Id', 'Start', 'Completion' and "IsDump" fields are ignored.
481
// I still need to get the fields though to advance the field position pointer.
482
getFieldValue(data, 1, &name);
483
getFieldValue(data, 2, &db_id);
484
getFieldValue(data, 3, &start);
485
getFieldValue(data, 4, &end);
486
getFieldValue(data, 5, &isRunning);
487
getFieldValue(data, 6, &isDump);
488
getFieldValue(data, 7, &location);
489
getFieldValue(data, 8, &cloud_ref);
490
getFieldValue(data, 9, &cloud_backup_no);
493
ref_id = MSBackupInfo::gMaxInfoRef++;
494
else if (ref_id >= MSBackupInfo::gMaxInfoRef)
495
MSBackupInfo::gMaxInfoRef = ref_id +1;
497
db_name = name.c_ptr();
498
db_id = MSDatabase::getDatabaseID(db_name, false);
500
new_(info, MSBackupInfo(ref_id, db_name, db_id, 0, 0, false, location.c_ptr(), cloud_ref, cloud_backup_no));
501
MSBackupInfo::gBackupInfo->set(ref_id, info);
503
// There is no need to call this now, startBackup() will call it
504
// after the backup is started.
505
// saveTable(RETAIN(myShare->mySysDatabase));
506
info->startBackup(RETAIN(myShare->mySysDatabase));
510
// It is good to know the details if the backup could not be started.
511
self->logException();
512
if (ref_id && ! duplicate)
513
MSBackupInfo::gBackupInfo->remove(ref_id);
520
void MSBackupTable::deleteRow(char *data)
522
uint32_t ref_id, indx;
526
getFieldValue(data, 0, &ref_id);
528
// Adjust the current position in the array if required.
529
indx = MSBackupInfo::gBackupInfo->getIndex(ref_id);
530
if (indx <= iBackupIndex)
533
MSBackupInfo::gBackupInfo->remove(ref_id);
534
saveTable(RETAIN(myShare->mySysDatabase));
538
void MSBackupTable::transferTable(MSDatabase *to_db, MSDatabase *from_db)
546
path = CSPath::newPath(getPBMSPath(RETAIN(from_db->myDatabasePath)), BACKUP_TABLE_NAME".dat");
548
if (path->exists()) {
550
bu_path = CSPath::newPath(getPBMSPath(RETAIN(to_db->myDatabasePath)), BACKUP_TABLE_NAME".dat");
551
path->copyTo(bu_path, true);
561
CSStringBuffer *MSBackupTable::dumpTable(MSDatabase *db)
565
CSStringBuffer *dump;
570
path = getSysFile(getPBMSPath(RETAIN(db->myDatabasePath)), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);
574
new_(dump, CSStringBuffer(20));
577
if (path->exists()) {
581
file = path->openFile(CSFile::READONLY);
584
size = file->getEOF();
585
dump->setLength(size);
586
file->read(dump->getBuffer(0), 0, size, size);
595
void MSBackupTable::restoreTable(MSDatabase *db, const char *data, size_t size, bool reload)
603
path = getSysFile(getPBMSPath(RETAIN(db->myDatabasePath)), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);
606
file = path->openFile(CSFile::CREATE | CSFile::TRUNCATE);
609
file->write(data, 0, size);
624
// The cloud info table is only removed from the pbms database
625
// if there are no more databases.
626
void MSBackupTable::removeTable(CSString *db_path)
629
char pbms_path[PATH_MAX];
634
cs_strcpy(PATH_MAX, pbms_path, db_path->getCString());
637
if (strcmp(cs_last_name_of_path(pbms_path), "pbms") != 0)
640
cs_remove_last_name_of_path(pbms_path);
642
path = getSysFile(CSString::newString(pbms_path), BACKUP_TABLE_NAME, MIN_BACKUP_TABLE_FILE_SIZE);