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
26
#include <drizzled/common.h>
27
#include <drizzled/session.h>
28
#include <drizzled/sql_lex.h>
29
#include <drizzled/field/blob.h>
32
#include "cslib/CSConfig.h"
34
#include <sys/types.h>
40
//#include "mysql_priv.h"
41
#include "cslib/CSGlobal.h"
42
#include "cslib/CSStrUtil.h"
48
#include "repository_ms.h"
49
#include "database_ms.h"
50
#include "compactor_ms.h"
51
#include "open_table_ms.h"
52
#include "discover_ms.h"
53
#include "transaction_ms.h"
54
#include "systab_variable_ms.h"
55
#include "backup_ms.h"
58
#include "systab_dump_ms.h"
61
DT_FIELD_INFO pbms_dump_info[]=
63
{"Data", NOVAL, NULL, MYSQL_TYPE_LONG_BLOB, &my_charset_bin, NOT_NULL_FLAG, "A BLOB repository record"},
64
{NULL,NOVAL, NULL, MYSQL_TYPE_STRING,NULL, 0, NULL}
67
DT_KEY_INFO pbms_dump_keys[]=
74
* -------------------------------------------------------------------------
77
//-----------------------
78
MSDumpTable::MSDumpTable(MSSystemTableShare *share, TABLE *table):
79
MSRepositoryTable(share, table)
83
//-----------------------
84
MSDumpTable::~MSDumpTable()
88
//-----------------------
89
void MSDumpTable::use()
91
dt_hasInfo = dt_hasCompleted = dt_haveCloudInfo = false;
94
// Suspend the transaction writer while the dump is running.
95
MSTransactionManager::suspend(true);
97
MSRepositoryTable::use();
100
//-----------------------
101
void MSDumpTable::unuse()
103
MSBackupInfo *backupInfo;
105
backupInfo = myShare->mySysDatabase->myBlobCloud->cl_getBackupInfo();
109
myShare->mySysDatabase->myBlobCloud->cl_clearBackupInfo();
110
if (backupInfo->isBackupRunning()) {
112
backupInfo->backupCompleted(RETAIN(myShare->mySysDatabase));
114
backupInfo->backupTerminated(RETAIN(myShare->mySysDatabase));
116
release_(backupInfo);
120
MSTransactionManager::resume();
121
MSRepositoryTable::unuse();
124
//-----------------------
125
void MSDumpTable::seqScanInit()
127
dt_hasInfo = dt_hasCompleted = false;
128
return MSRepositoryTable::seqScanInit();
130
//-----------------------
131
bool MSDumpTable::seqScanNext(char *buf)
135
return returnInfoRow(buf);
137
// Reset the position
138
if (!MSRepositoryTable::seqScanNext(buf))
139
dt_hasCompleted = true;
141
return !dt_hasCompleted;
144
//-----------------------
145
bool MSDumpTable::returnDumpRow(char *record, uint64_t record_size, char *buf)
147
TABLE *table = mySQLTable;
150
MY_BITMAP *save_write_set;
155
/* ASSERT_COLUMN_MARKED_FOR_WRITE is failing when
157
* But I want to use it! :(
159
save_write_set = table->write_set;
160
table->write_set = NULL;
162
memset(buf, 0xFF, table->getNullBytes());
164
memset(buf, 0xFF, table->s->null_bytes);
167
for (Field **field=GET_TABLE_FIELDS(table) ; *field ; field++) {
170
save = curr_field->ptr;
171
#if MYSQL_VERSION_ID < 50114
172
curr_field->ptr = (byte *) buf + curr_field->offset();
175
curr_field->ptr = (byte *) buf + curr_field->offset(curr_field->getTable()->getInsertRecord());
177
curr_field->ptr = (byte *) buf + curr_field->offset(curr_field->table->record[0]);
180
switch (curr_field->field_name[0]) {
184
ASSERT(strcmp(curr_field->field_name, "Data") == 0);
185
if (record_size <= 0xFFFFFFF) {
186
((Field_blob *) curr_field)->set_ptr(record_size, (byte *) record);
187
setNotNullInRecord(curr_field, buf);
191
curr_field->ptr = save;
194
table->write_set = save_write_set;
198
//-----------------------
199
bool MSDumpTable::returnRow(MSBlobHeadPtr blob, char *buf)
201
uint64_t record_size, blob_repo_size;
202
uint16_t ref_size, ref_count, refs = 0, table_refs = 0, header_size;
203
uint8_t blob_storage_type;
204
MSRepoPointersRec ptr;
205
MSDatabase *myDB = myShare->mySysDatabase;
208
// Reset the references for the BLOB and recreate
209
// the temp log references.
210
ref_count = CS_GET_DISK_2(blob->rb_ref_count_2);
211
ref_size = CS_GET_DISK_1(blob->rb_ref_size_1);
213
blob_storage_type = CS_GET_DISK_1(blob->rb_storage_type_1);
215
header_size = CS_GET_DISK_2(blob->rb_head_size_2);
216
blob_repo_size = CS_GET_DISK_6(blob->rb_blob_repo_size_6);
218
iBlobBuffer->setLength(header_size);
219
iRepoFile->read(iBlobBuffer->getBuffer(0), iRepoOffset, (size_t) header_size, header_size);
221
// First check to see if the BLOB is referenced
222
ptr.rp_chars = iBlobBuffer->getBuffer(0) + dt_headerSize;
223
for (int count = 0; count < ref_count; count++) {
224
int ref_type = CS_GET_DISK_2(ptr.rp_ref->rr_type_2);
227
case MS_BLOB_TABLE_REF:
231
case MS_BLOB_FREE_REF:
232
case MS_BLOB_DELETE_REF:
235
default: // Assumed to be a MSRepoBlobRefRec.
236
// Only committed references are backed up.
237
if (IS_COMMITTED(CS_GET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8))) {
240
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
246
ptr.rp_chars += ref_size;
250
if (refs && table_refs) { // Unreferenced BLOBs are ignored.
251
if (blob_storage_type == MS_CLOUD_STORAGE) {
252
CloudKeyRec cloud_key;
253
MSRepoFile::getBlobKey(blob, &cloud_key);
254
myDB->myBlobCloud->cl_backupBLOB(&cloud_key);
255
record_size = header_size;
257
record_size = header_size + blob_repo_size;
258
iBlobBuffer->setLength(record_size);
259
iRepoFile->read(iBlobBuffer->getBuffer(header_size), iRepoOffset + header_size, (size_t) blob_repo_size, blob_repo_size);
262
record_size = 0; // An empty record is returned for unreferenced BLOBs.
266
return_(returnDumpRow(iBlobBuffer->getBuffer(0), record_size, buf));
269
//-----------------------
270
#define INC_INFO_SPACE(i) record_size+=i; space-=i;ptr+=i;
271
#define MS_DUMP_MAGIC 0x5A74C1EB
273
CSDiskValue4 ti_table_id_4;
274
char ti_name[1]; // variable length buffer
275
} TabInfoRec, *TabInfoPtr;
278
CSDiskValue4 di_magic_4;
279
CSDiskValue2 di_header_size_2;
280
} RepInfoRec, *RepInfoPtr;
282
// Repository DUMP info record format:
283
// <Dump magic><BLOB header size><database ID><backup number><sysTables size><sysTables dump>[<table ID><table name>]...
284
bool MSDumpTable::returnInfoRow(char *buf)
286
uint64_t record_size = 0, space = 1024;
289
uint32_t space_needed, next_tab = 0, cloudRef, cloudbackupNo, backupRef;
292
CSStringBuffer *sysTablesDump;
293
MSBackupInfo *backupInfo;
297
// Setup the sysvar table with the cloud backup number then dump it.
298
if (myShare->mySysDatabase->myBlobType == MS_CLOUD_STORAGE) {
299
cloudbackupNo = myShare->mySysDatabase->myBlobCloud->cl_getNextBackupNumber();
300
cloudRef = myShare->mySysDatabase->myBlobCloud->cl_getDefaultCloudRef();
302
// It is still possible that the database contains BLOBs in cloud storage
303
// even if it isn't currently flaged to use cloud storage.
304
cloudbackupNo = cloudRef = 0;
307
backupInfo = MSBackupInfo::startDump(RETAIN(myShare->mySysDatabase), cloudRef, cloudbackupNo);
308
backupRef = backupInfo->getBackupRefId();
309
myShare->mySysDatabase->myBlobCloud->cl_setBackupInfo(backupInfo);
311
dt_cloudbackupDBID = myShare->mySysDatabase->myDatabaseID;
313
sysTablesDump = PBMSSystemTables::dumpSystemTables(RETAIN(myShare->mySysDatabase));
314
push_(sysTablesDump);
316
iBlobBuffer->setLength(space + sysTablesDump->length() + 4 + 4);
317
ptr = iBlobBuffer->getBuffer(0);
318
rep_info = (RepInfoPtr) iBlobBuffer->getBuffer(0);
319
dt_headerSize = sizeof(MSBlobHeadRec);
322
CS_SET_DISK_4(rep_info->di_magic_4, MS_DUMP_MAGIC);
323
CS_SET_DISK_2(rep_info->di_header_size_2, dt_headerSize);
325
INC_INFO_SPACE(sizeof(RepInfoRec));
328
CS_SET_DISK_4(d.int_val->val_4, dt_cloudbackupDBID);
332
CS_SET_DISK_4(d.int_val->val_4, backupRef);
335
// Add the system tables to the dump
337
CS_SET_DISK_4(d.int_val->val_4, sysTablesDump->length());
339
memcpy(ptr, sysTablesDump->getBuffer(0), sysTablesDump->length());
340
INC_INFO_SPACE(sysTablesDump->length());
341
release_(sysTablesDump);
342
sysTablesDump = NULL;
344
tab_info = (TabInfoPtr)ptr;
346
// Get a list of the tables containing BLOB references.
347
while ((tab = myShare->mySysDatabase->getNextTable(&next_tab))) {
349
space_needed = tab->myTableName->length() + 5;
350
if (space < space_needed) {
352
iBlobBuffer->setLength(space);
353
ptr = iBlobBuffer->getBuffer(0) + record_size;
356
tab_info = (TabInfoPtr)ptr;
357
CS_SET_DISK_4(tab_info->ti_table_id_4, tab->myTableID);
358
strcpy(tab_info->ti_name, tab->myTableName->getCString());
359
INC_INFO_SPACE(space_needed);
364
return_(returnDumpRow(iBlobBuffer->getBuffer(0), record_size, buf));
367
#define INC_INFO_REC(i) info_buffer+=i; length-=i; tab_info = (TabInfoPtr) info_buffer;
368
//-----------------------
369
void MSDumpTable::setUpRepository(const char *info_buffer, uint32_t length)
371
uint32_t tab_id, magic;
373
MSDatabase *myDB = myShare->mySysDatabase;
374
RepInfoPtr rep_info = (RepInfoPtr) info_buffer;
376
uint32_t sys_size, backupRefID;
377
MSBackupInfo *backupInfo;
380
if (length < sizeof(RepInfoRec)) {
381
CSException::throwException(CS_CONTEXT, CS_ERR_INVALID_RECORD, "Invalid repository info record.");
384
magic = CS_GET_DISK_4(rep_info->di_magic_4);
385
if (CS_GET_DISK_4(rep_info->di_magic_4) != MS_DUMP_MAGIC) {
386
CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HEADER_MAGIC, "Invalid repository info record.");
389
dt_headerSize = CS_GET_DISK_2(rep_info->di_header_size_2);
390
INC_INFO_REC(sizeof(RepInfoRec));
392
d.rec_cchars = info_buffer;
393
dt_cloudbackupDBID = CS_GET_DISK_4(d.int_val->val_4);
396
// Get the backup information
397
d.rec_cchars = info_buffer;
398
backupRefID = CS_GET_DISK_4(d.int_val->val_4);
401
// If the backup information is missing then the restore may still
402
// be able to complete so long as cloud storage was not used.
403
backupInfo = MSBackupInfo::findBackupInfo(backupRefID);
405
myShare->mySysDatabase->myBlobCloud->cl_setBackupInfo(backupInfo);
406
dt_haveCloudInfo = true;
409
// Restore the System table.
410
d.rec_cchars = info_buffer;
411
sys_size = CS_GET_DISK_4(d.int_val->val_4);
414
PBMSSystemTables::restoreSystemTables(RETAIN(myDB), info_buffer, sys_size);
415
INC_INFO_REC(sys_size);
418
tab_id = CS_GET_DISK_4(tab_info->ti_table_id_4);
419
myDB->addTable(tab_id, tab_info->ti_name, 0, false);
420
INC_INFO_REC(strlen(tab_info->ti_name) +5);
424
CSException::throwException(CS_CONTEXT, CS_ERR_INVALID_RECORD, "Invalid repository info record.");
428
//-----------------------
429
void MSDumpTable::insertRow(char *buf)
431
TABLE *table = mySQLTable;
433
uint32_t packlength, length;
434
const char *blob_rec, *blob_ptr;
436
field = (Field_blob *)GET_FIELD(table, 0);
438
/* Get the blob record: */
440
blob_rec= buf + field->offset(table->getInsertRecord());
441
packlength= field->pack_length() - table->getShare()->sizeBlobPtr();
443
blob_rec= buf + field->offset(table->record[0]);
444
packlength= field->pack_length() - table->s->sizeBlobPtr();
447
memcpy(&blob_ptr, blob_rec +packlength, sizeof(char*));
448
length= field->get_length();
451
setUpRepository(blob_ptr, length);
454
insertRepoRow((MSBlobHeadPtr)blob_ptr, length);
458
//-----------------------
459
void MSDumpTable::insertRepoRow(MSBlobHeadPtr blob, uint32_t length)
462
MSRepoFile *repo_file;
463
uint64_t repo_offset;
464
uint64_t blob_data_size;
466
uint16_t ref_size, ref_count, refs = 0, table_refs = 0;
467
uint8_t blob_storage_type;
468
MSRepoPointersRec ptr;
469
MSDatabase *myDB = myShare->mySysDatabase;
470
CloudKeyRec cloud_key;
476
if (length != (CS_GET_DISK_2(blob->rb_head_size_2) + CS_GET_DISK_6(blob->rb_blob_repo_size_6))) {
477
CSException::throwException(CS_CONTEXT, MS_ERR_INVALID_RECORD, "Damaged Repository record");
480
// Get a repository file.
481
repo = myDB->lockRepo(length);
484
repo_file = myDB->getRepoFileFromPool(repo->myRepoID, false);
485
frompool_(repo_file);
487
repo_offset = repo->myRepoFileSize;
489
// Reset the references for the BLOB and recreate
490
// the temp log references.
491
auth_code = CS_GET_DISK_4(blob->rb_auth_code_4);
492
ref_count = CS_GET_DISK_2(blob->rb_ref_count_2);
493
ref_size = CS_GET_DISK_1(blob->rb_ref_size_1);
494
blob_data_size = CS_GET_DISK_6(blob->rb_blob_data_size_6);
496
blob_storage_type = CS_GET_DISK_1(blob->rb_storage_type_1);
497
if (blob_storage_type == MS_CLOUD_STORAGE) {
498
MSRepoFile::getBlobKey(blob, &cloud_key);
501
// First check to see if the BLOB is referenced
502
ptr.rp_chars = ((char*) blob) + dt_headerSize;
503
for (int count = 0; count < ref_count; count++) {
504
int ref_type = CS_GET_DISK_2(ptr.rp_ref->rr_type_2);
507
case MS_BLOB_TABLE_REF:
511
case MS_BLOB_FREE_REF:
512
case MS_BLOB_DELETE_REF:
515
default: // Assumed to be a MSRepoBlobRefRec.
516
// Only committed references are backed up.
517
if (IS_COMMITTED(CS_GET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8))) {
520
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
526
ptr.rp_chars += ref_size;
530
if (refs && table_refs) { // Unreferenced BLOBs are ignored.
533
// Set table references.
534
ptr.rp_chars = ((char*) blob) + dt_headerSize;
535
for (int count = 0; count < ref_count; count++) {
536
int ref_type = CS_GET_DISK_2(ptr.rp_ref->rr_type_2);
542
case MS_BLOB_TABLE_REF:
543
tab_id = CS_GET_DISK_4(ptr.rp_tab_ref->tr_table_id_4);
544
blob_id = CS_GET_DISK_6(ptr.rp_tab_ref->tr_blob_id_6);
545
otab = MSTableList::getOpenTableByID(myDB->myDatabaseID, tab_id);
548
otab->getDBTable()->setBlobHandle(otab, blob_id, repo->myRepoID, repo_offset, blob_data_size, dt_headerSize, auth_code);
552
case MS_BLOB_DELETE_REF:
555
case MS_BLOB_FREE_REF:
560
ptr.rp_chars += ref_size;
563
// Write the repository record.
564
repo_file->write(blob, repo_offset, length);
565
repo->myRepoFileSize += length;
567
#ifdef HAVE_ALIAS_SUPPORT
568
uint16_t alias_offset;
569
if (alias_offset = CS_GET_DISK_2(blob->rb_alias_offset_2)) {
570
myDB->registerBlobAlias(repo->myRepoID, repo_offset, ((char*)blob) + alias_offset);
573
if (blob_storage_type == MS_CLOUD_STORAGE) {
574
if (!dt_haveCloudInfo) {
575
CSException::throwException(CS_CONTEXT, MS_ERR_MISSING_CLOUD_REFFERENCE, "Missing cloud backup information.");
577
myDB->myBlobCloud->cl_restoreBLOB(&cloud_key, dt_cloudbackupDBID);
581
backtopool_(repo_file);