2
* Copyright (C) 2010 PrimeBase Technologies GmbH, Germany
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; version 2 of the License.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
#include <drizzled/session.h>
27
#include <drizzled/field/blob.h>
28
#include <drizzled/sql_lex.h>
30
#include "cslib/CSConfig.h"
31
#include "cslib/CSGlobal.h"
32
#include "cslib/CSStrUtil.h"
33
#include "cslib/CSThread.h"
35
#include "events_ms.h"
36
#include "parameters_ms.h"
37
#include "engine_ms.h"
39
using namespace drizzled;
40
using namespace plugin;
44
//==================================
45
// My table event observers:
46
static bool insertRecord(const char *db, const char *table_name, char *possible_blob_url, size_t length,
47
Session &session, Field_blob *field, unsigned char *blob_rec, size_t packlength)
50
char safe_url[PBMS_BLOB_URL_SIZE+1];
51
PBMSBlobURLRec blob_url_buffer;
52
size_t org_length = length;
56
// Tell PBMS to record a new reference to the BLOB.
57
// If 'blob' is not a BLOB URL then it will be stored in the repositor as a new BLOB
58
// and a reference to it will be created.
60
if (MSEngine::couldBeURL(possible_blob_url, length) == false) {
61
err = MSEngine::createBlob(db, table_name, possible_blob_url, length, &blob_url_buffer, &result);
63
// If it fails log the error and continue to try and release any other BLOBs in the row.
64
fprintf(stderr, "PBMSEvents: createBlob(\"%s.%s\") error (%d):'%s'\n",
65
db, table_name, result.mr_code, result.mr_message);
69
blob_url = blob_url_buffer.bu_data;
71
// The BLOB URL may not be null terminate, if so
72
// then copy it to a safe buffer and terminate it.
73
if (possible_blob_url[length]) {
74
memcpy(safe_url, possible_blob_url, length);
78
blob_url = possible_blob_url;
81
// Tell PBMS to add a reference to the BLOB.
82
err = MSEngine::referenceBlob(db, table_name, &blob_url_buffer, blob_url, field->position(), &result);
84
// If it fails log the error and continue to try and release any other BLOBs in the row.
85
fprintf(stderr, "PBMSEvents: referenceBlob(\"%s.%s\", \"%s\" ) error (%d):'%s'\n",
86
db, table_name, blob_url, result.mr_code, result.mr_message);
91
// The URL is modified on insert so if the BLOB length changed reset it.
92
// This will happen if the BLOB data was replaced with a BLOB reference.
93
length = strlen(blob_url_buffer.bu_data) +1;
94
if ((length != org_length) || memcmp(blob_url_buffer.bu_data, possible_blob_url, length)) {
95
char *blob = possible_blob_url; // This is the BLOB as the server currently sees it.
97
if (length != org_length) {
98
field->store_length(blob_rec, length);
101
if (length > org_length) {
102
// This can only happen if the BLOB URL is actually larger than the BLOB itself.
103
blob = (char *) session.mem.alloc(length);
104
memcpy(blob_rec+packlength, &blob, sizeof(char*));
106
memcpy(blob, blob_url_buffer.bu_data, length);
113
static bool deleteRecord(const char *db, const char *table_name, char *blob_url, size_t length)
116
char safe_url[PBMS_BLOB_URL_SIZE+1];
117
PBMSResultRec result;
118
bool call_failed = false;
120
// Check to see if this is a valid URL.
121
if (MSEngine::couldBeURL(blob_url, length)) {
123
// The BLOB URL may not be null terminate, if so
124
// then copy it to a safe buffer and terminate it.
125
if (blob_url[length]) {
126
memcpy(safe_url, blob_url, length);
127
safe_url[length] = 0;
131
// Signal PBMS to delete the reference to the BLOB.
132
err = MSEngine::dereferenceBlob(db, table_name, blob_url, &result);
134
// If it fails log the error and continue to try and release any other BLOBs in the row.
135
fprintf(stderr, "PBMSEvents: dereferenceBlob(\"%s.%s\") error (%d):'%s'\n",
136
db, table_name, result.mr_code, result.mr_message);
146
static bool observeBeforeInsertRecord(BeforeInsertRecordEventData &data)
149
unsigned char *blob_rec;
151
size_t packlength, i, length;
153
for (i= 0; i < data.table.sizeBlobFields(); i++) {
154
field = data.table.getBlobFieldAt(i);
156
if (field->is_null_in_record(data.row))
159
// Get the blob record:
160
packlength = field->pack_length() - data.table.getBlobPtrSize();
162
blob_rec = (unsigned char *)data.row + field->offset(data.table.getInsertRecord());
163
length = field->get_length(blob_rec);
164
memcpy(&blob_url, blob_rec +packlength, sizeof(char*));
166
if (insertRecord(data.table.getSchemaName(), data.table.getTableName(),
167
blob_url, length, data.session, field, blob_rec, packlength))
175
static bool observeAfterInsertRecord(AfterInsertRecordEventData &data)
177
bool has_blob = false;
179
for (uint32_t i= 0; (i < data.table.sizeBlobFields()) && (has_blob == false); i++) {
180
Field_blob *field = data.table.getBlobFieldAt(i);
182
if ( field->is_null_in_record(data.row) == false)
187
MSEngine::callCompleted(data.err == 0);
193
static bool observeBeforeUpdateRecord(BeforeUpdateRecordEventData &data)
196
uint32_t field_offset;
197
const unsigned char *old_blob_rec;
198
unsigned char *new_blob_rec= NULL;
199
char *old_blob_url, *new_blob_url;
200
size_t packlength, i, old_length= 0, new_length= 0;
201
const unsigned char *old_row = data.old_row;
202
unsigned char *new_row = data.new_row;
203
const char *db = data.table.getSchemaName();
204
const char *table_name = data.table.getTableName();
205
bool old_null, new_null;
207
for (i= 0; i < data.table.sizeBlobFields(); i++) {
208
field = data.table.getBlobFieldAt(i);
210
new_null = field->is_null_in_record(new_row);
211
old_null = field->is_null_in_record(old_row);
213
if (new_null && old_null)
216
// Check to see if the BLOB data was updated.
218
// Get the blob records:
219
field_offset = field->offset(data.table.getInsertRecord());
220
packlength = field->pack_length() - data.table.getBlobPtrSize();
225
new_blob_rec = new_row + field_offset;
226
new_length = field->get_length(new_blob_rec);
227
memcpy(&new_blob_url, new_blob_rec +packlength, sizeof(char*));
233
old_blob_rec = old_row + field_offset;
234
old_length = field->get_length(old_blob_rec);
235
memcpy(&old_blob_url, old_blob_rec +packlength, sizeof(char*));
238
// Check to see if the BLOBs are the same.
239
// I am assuming that if the BLOB pointer is different then teh BLOB has changed.
240
// Zero length BLOBs are a special case because they may have a NULL data pointer,
241
// to catch this and distiguish it from a NULL BLOB I do a check to see if one field was NULL:
242
// (old_null != new_null)
243
if ((old_blob_url != new_blob_url) || (old_null != new_null)) {
245
// The BLOB was updated so delete the old one and insert the new one.
246
if ((old_null == false) && deleteRecord(db, table_name, old_blob_url, old_length))
249
if ((new_null == false) && insertRecord(db, table_name, new_blob_url, new_length, data.session, field, new_blob_rec, packlength))
260
static bool observeAfterUpdateRecord(AfterUpdateRecordEventData &data)
262
bool has_blob = false;
263
const unsigned char *old_row = data.old_row;
264
const unsigned char *new_row = data.new_row;
266
for (uint32_t i= 0; (i < data.table.sizeBlobFields()) && (has_blob == false); i++) {
267
Field_blob *field = data.table.getBlobFieldAt(i);
268
bool new_null = field->is_null_in_record(new_row);
269
bool old_null = field->is_null_in_record(old_row);
271
if ( (new_null == false) || (old_null == false)) {
272
const unsigned char *blob_rec;
273
size_t field_offset = field->offset(data.table.getInsertRecord());
274
size_t packlength = field->pack_length() - data.table.getBlobPtrSize();
275
char *old_blob_url, *new_blob_url;
277
blob_rec = new_row + field_offset;
278
memcpy(&new_blob_url, blob_rec +packlength, sizeof(char*));
280
blob_rec = old_row + field_offset;
281
memcpy(&old_blob_url, blob_rec +packlength, sizeof(char*));
283
has_blob = ((old_blob_url != new_blob_url) || (old_null != new_null));
288
MSEngine::callCompleted(data.err == 0);
294
static bool observeAfterDeleteRecord(AfterDeleteRecordEventData &data)
297
const unsigned char *blob_rec;
299
size_t packlength, i, length;
300
bool call_failed = false;
301
bool has_blob = false;
306
for (i= 0; (i < data.table.sizeBlobFields()) && (call_failed == false); i++) {
307
field = data.table.getBlobFieldAt(i);
309
if (field->is_null_in_record(data.row))
313
// Get the blob record:
314
packlength = field->pack_length() - data.table.getBlobPtrSize();
316
blob_rec = data.row + field->offset(data.table.getInsertRecord());
317
length = field->get_length(blob_rec);
318
memcpy(&blob_url, blob_rec +packlength, sizeof(char*));
320
if (deleteRecord(data.table.getSchemaName(), data.table.getTableName(), blob_url, length))
325
MSEngine::callCompleted(call_failed == false);
330
//==================================
331
// My session event observers:
332
static bool observeAfterDropDatabase(AfterDropDatabaseEventData &data)
334
PBMSResultRec result;
338
if (MSEngine::dropDatabase(data.db.c_str(), &result) != 0) {
339
fprintf(stderr, "PBMSEvents: dropDatabase(\"%s\") error (%d):'%s'\n",
340
data.db.c_str(), result.mr_code, result.mr_message);
343
// Always return no error for after drop database. What could the server do about it?
347
//==================================
348
// My schema event observers:
349
static bool observeAfterDropTable(AfterDropTableEventData &data)
351
PBMSResultRec result;
355
if (MSEngine::dropTable(data.table.getSchemaName().c_str(), data.table.getTableName().c_str(), &result) != 0) {
356
fprintf(stderr, "PBMSEvents: dropTable(\"%s.%s\") error (%d):'%s'\n",
357
data.table.getSchemaName().c_str(), data.table.getTableName().c_str(), result.mr_code, result.mr_message);
360
MSEngine::callCompleted(true);
366
static bool observeAfterRenameTable(AfterRenameTableEventData &data)
368
PBMSResultRec result;
372
const char *from_db = data.from.getSchemaName().c_str();
373
const char *from_table = data.from.getTableName().c_str();
374
const char *to_db = data.to.getSchemaName().c_str();
375
const char *to_table = data.to.getTableName().c_str();
377
if (MSEngine::renameTable(from_db, from_table, to_db, to_table, &result) != 0) {
378
fprintf(stderr, "PBMSEvents: renameTable(\"%s.%s\" To \"%s.%s\") error (%d):'%s'\n",
379
from_db, from_table, to_db, to_table, result.mr_code, result.mr_message);
382
MSEngine::callCompleted(true);
387
//==================================
388
/* This is where I register which table events my pluggin is interested in.*/
389
void PBMSEvents::registerTableEventsDo(TableShare &table_share, EventObserverList &observers)
391
if ((PBMSParameters::isPBMSEventsEnabled() == false)
392
|| (PBMSParameters::isBLOBTable(table_share.getSchemaName(), table_share.getTableName()) == false))
395
if (table_share.blob_fields > 0) {
396
registerEvent(observers, BEFORE_INSERT_RECORD, PBMSParameters::getBeforeInsertEventPosition()); // I want to be called first if passible
397
registerEvent(observers, AFTER_INSERT_RECORD);
398
registerEvent(observers, BEFORE_UPDATE_RECORD, PBMSParameters::getBeforeUptateEventPosition());
399
registerEvent(observers, AFTER_UPDATE_RECORD);
400
registerEvent(observers, AFTER_DELETE_RECORD);
404
//==================================
405
/* This is where I register which schema events my pluggin is interested in.*/
406
void PBMSEvents::registerSchemaEventsDo(const std::string &db, EventObserverList &observers)
408
if ((PBMSParameters::isPBMSEventsEnabled() == false)
409
|| (PBMSParameters::isBLOBDatabase(db.c_str()) == false))
412
registerEvent(observers, AFTER_DROP_TABLE);
413
registerEvent(observers, AFTER_RENAME_TABLE);
416
//==================================
417
/* This is where I register which schema events my pluggin is interested in.*/
418
void PBMSEvents::registerSessionEventsDo(Session &, EventObserverList &observers)
420
if (PBMSParameters::isPBMSEventsEnabled() == false)
423
registerEvent(observers, AFTER_DROP_DATABASE);
426
//==================================
427
/* The event observer.*/
428
bool PBMSEvents::observeEventDo(EventData &data)
432
switch (data.event) {
433
case AFTER_DROP_DATABASE:
434
result = observeAfterDropDatabase((AfterDropDatabaseEventData &)data);
437
case AFTER_DROP_TABLE:
438
result = observeAfterDropTable((AfterDropTableEventData &)data);
441
case AFTER_RENAME_TABLE:
442
result = observeAfterRenameTable((AfterRenameTableEventData &)data);
445
case BEFORE_INSERT_RECORD:
446
result = observeBeforeInsertRecord((BeforeInsertRecordEventData &)data);
449
case AFTER_INSERT_RECORD:
450
result = observeAfterInsertRecord((AfterInsertRecordEventData &)data);
453
case BEFORE_UPDATE_RECORD:
454
result = observeBeforeUpdateRecord((BeforeUpdateRecordEventData &)data);
457
case AFTER_UPDATE_RECORD:
458
result = observeAfterUpdateRecord((AfterUpdateRecordEventData &)data);
461
case AFTER_DELETE_RECORD:
462
result = observeAfterDeleteRecord((AfterDeleteRecordEventData &)data);
466
fprintf(stderr, "PBMSEvents: Unexpected event '%s'\n", EventObserver::eventName(data.event));