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
25
* PBMS interface used to enable engines for use with the PBMS daemon.
27
* For an example on how to build this into an engine have a look at the PBXT engine
28
* in file ha_pbxt.cc. Search for 'PBMS_ENABLED'.
33
#if defined(MSDOS) || defined(__WIN__)
34
#include "pbms_enabled.h"
36
// Windows is not supported yet so just stub out the functions..
37
bool pbms_initialize(const char *engine_name __attribute__((unused)),
38
bool isServer __attribute__((unused)),
39
bool isTransactional __attribute__((unused)),
40
PBMSResultPtr result __attribute__((unused)),
41
IsPBMSFilterFunc is_pbms_blob __attribute__((unused))
43
void pbms_finalize() {}
44
int pbms_write_row_blobs(const TABLE *table __attribute__((unused)),
45
unsigned char *buf __attribute__((unused)),
46
PBMSResultPtr result __attribute__((unused))
48
int pbms_update_row_blobs(const TABLE *table __attribute__((unused)),
49
const unsigned char *old_row __attribute__((unused)),
50
unsigned char *new_row __attribute__((unused)),
51
PBMSResultPtr result __attribute__((unused))
53
int pbms_delete_row_blobs(const TABLE *table __attribute__((unused)),
54
const unsigned char *buf __attribute__((unused)),
55
PBMSResultPtr result __attribute__((unused))
57
int pbms_rename_table_with_blobs(const char *old_table_path __attribute__((unused)),
58
const char *new_table_path __attribute__((unused)),
59
PBMSResultPtr result __attribute__((unused))
61
int pbms_delete_table_with_blobs(const char *table_path __attribute__((unused)),
62
PBMSResultPtr result __attribute__((unused))
64
void pbms_completed(TABLE *table __attribute__((unused)),
65
bool ok __attribute__((unused))
68
#define PBMS_API pbms_enabled_api
70
#include "pbms_enabled.h"
71
#include "mysql_priv.h"
72
#include <mysql/plugin.h>
73
#define session_alloc(sess, size) thd_alloc(sess, size);
74
#define current_session current_thd
76
#define GET_BLOB_FIELD(t, i) (Field_blob *)(t->field[t->s->blob_field[i]])
77
#define DB_NAME(f) (f->table->s->db.str)
78
#define TAB_NAME(f) (*(f->table_name))
80
static PBMS_API pbms_api;
83
* A callback function to check if the column is a PBMS BLOB.
84
* Can be NULL if no check is to be done.
86
static IsPBMSFilterFunc is_pbms_blob = NULL;
88
//====================
89
bool pbms_initialize(const char *engine_name, bool isServer, bool isTransactional, PBMSResultPtr result, IsPBMSFilterFunc is_pbms_blob_arg)
92
PBMSEngineRec enabled_engine = {
101
strncpy(enabled_engine.ms_engine_name, engine_name, 32);
102
enabled_engine.ms_internal = isServer;
103
enabled_engine.ms_has_transactions = isTransactional;
104
enabled_engine.ms_engine_name[31] = 0;
106
err = pbms_api.registerEngine(&enabled_engine, result);
107
is_pbms_blob = is_pbms_blob_arg;
113
//====================
114
void pbms_finalize(const char *engine_name)
116
pbms_api.deregisterEngine(engine_name);
119
//==================================
120
static int insertRecord(Field_blob *field, char *blob, size_t org_length, unsigned char *blob_rec, size_t packlength, PBMSResultPtr result)
124
PBMSBlobURLRec blob_url;
126
err = pbms_api.retainBlob(DB_NAME(field), TAB_NAME(field), &blob_url, blob, org_length, field->field_index, result);
130
// If the BLOB length changed reset it.
131
// This will happen if the BLOB data was replaced with a BLOB reference.
132
length = strlen(blob_url.bu_data) +1;
133
if ((length != org_length) || memcmp(blob_url.bu_data, blob, length)) {
134
if (length != org_length) {
135
field->store_length(blob_rec, packlength, length);
138
if (length > org_length) {
139
// This can only happen if the BLOB URL is actually larger than the BLOB itself.
140
blob = (char *) session_alloc(current_session, length);
141
memcpy(blob_rec+packlength, &blob, sizeof(char*));
143
memcpy(blob, blob_url.bu_data, length);
149
//====================
150
int pbms_update_row_blobs(const TABLE *table, const unsigned char *old_row, unsigned char *new_row, PBMSResultPtr result)
153
uint32_t field_offset;
154
const unsigned char *old_blob_rec;
155
unsigned char *new_blob_rec;
156
char *old_blob_url, *new_blob_url;
157
size_t packlength, i, old_length, new_length;
159
bool old_null_blob, new_null_blob;
161
result->mr_had_blobs = false;
163
if (!pbms_api.isPBMSLoaded())
166
if (table->s->blob_fields == 0)
169
for (i= 0; i < table->s->blob_fields; i++) {
170
field = GET_BLOB_FIELD(table, i);
172
old_null_blob = field->is_null_in_record(old_row);
173
new_null_blob = field->is_null_in_record(new_row);
174
if (old_null_blob && new_null_blob)
179
// Note: field->type() always returns MYSQL_TYPE_BLOB regardless of the type of BLOB
180
field->sql_type(type_name);
181
if (strcasecmp(type_name.c_ptr(), "LongBlob"))
185
if( is_pbms_blob && !is_pbms_blob(field) )
189
// Get the blob record:
190
field_offset = field->offset(field->table->record[0]);
191
packlength = field->pack_length() - field->table->s->blob_ptr_size;
196
new_blob_rec = new_row + field_offset;
197
new_length = field->get_length(new_blob_rec);
198
memcpy(&new_blob_url, new_blob_rec +packlength, sizeof(char*));
204
old_blob_rec = old_row + field_offset;
205
old_length = field->get_length(old_blob_rec);
206
memcpy(&old_blob_url, old_blob_rec +packlength, sizeof(char*));
209
// Check to see if the BLOBs are the same.
210
// I am assuming that if the BLOB pointer is different then teh BLOB has changed.
211
// Zero length BLOBs are a special case because they may have a NULL data pointer,
212
// to catch this and distiguish it from a NULL BLOB I do a check to see if one field was NULL:
213
// (old_null_blob != new_null_blob)
214
if ((old_blob_url != new_blob_url) || (old_null_blob != new_null_blob)) {
216
result->mr_had_blobs = true;
218
// The BLOB was updated so delete the old one and insert the new one.
219
if ((old_null_blob == false) && (err = pbms_api.releaseBlob(DB_NAME(field), TAB_NAME(field), old_blob_url, old_length, result)))
222
if ((new_null_blob == false) && (err = insertRecord(field, new_blob_url, new_length, new_blob_rec, packlength, result)))
230
//====================
231
int pbms_write_row_blobs(const TABLE *table, unsigned char *row_buffer, PBMSResultPtr result)
235
unsigned char *blob_rec;
237
size_t packlength, i, length;
240
result->mr_had_blobs = false;
242
if (!pbms_api.isPBMSLoaded())
245
if (table->s->blob_fields == 0)
248
for (i= 0; i < table->s->blob_fields; i++) {
249
field = GET_BLOB_FIELD(table, i);
251
if (field->is_null_in_record(row_buffer))
256
// Note: field->type() always returns MYSQL_TYPE_BLOB regardless of the type of BLOB
257
field->sql_type(type_name);
258
if (strcasecmp(type_name.c_ptr(), "LongBlob"))
262
if( is_pbms_blob && !is_pbms_blob(field) )
265
result->mr_had_blobs = true;
267
// Get the blob record:
268
packlength = field->pack_length() - field->table->s->blob_ptr_size;
269
blob_rec = row_buffer + field->offset(field->table->record[0]);
271
length = field->get_length(blob_rec);
272
memcpy(&blob_url, blob_rec +packlength, sizeof(char*));
274
if ((err = insertRecord(field, blob_url, length, blob_rec, packlength, result)))
281
//====================
282
int pbms_delete_row_blobs(const TABLE *table, const unsigned char *row_buffer, PBMSResultPtr result)
285
const unsigned char *blob_rec;
287
size_t packlength, i, length;
288
bool call_failed = false;
291
result->mr_had_blobs = false;
293
if (!pbms_api.isPBMSLoaded())
296
if (table->s->blob_fields == 0)
299
for (i= 0; i < table->s->blob_fields; i++) {
300
field = GET_BLOB_FIELD(table, i);
302
if (field->is_null_in_record(row_buffer))
307
// Note: field->type() always returns MYSQL_TYPE_BLOB regardless of the type of BLOB
308
field->sql_type(type_name);
309
if (strcasecmp(type_name.c_ptr(), "LongBlob"))
313
if(is_pbms_blob && !is_pbms_blob(field) )
316
result->mr_had_blobs = true;
318
// Get the blob record:
319
packlength = field->pack_length() - field->table->s->blob_ptr_size;
321
blob_rec = row_buffer + field->offset(field->table->record[0]);
322
length = field->get_length(blob_rec);
323
memcpy(&blob, blob_rec +packlength, sizeof(char*));
325
// Signal PBMS to delete the reference to the BLOB.
326
err = pbms_api.releaseBlob(DB_NAME(field), TAB_NAME(field), blob, length, result);
334
#define MAX_NAME_SIZE 64
335
static void parse_table_path(const char *path, char *db_name, char *tab_name)
337
const char *ptr = path + strlen(path) -1, *eptr;
340
*db_name = *tab_name = 0;
342
while ((ptr > path) && (*ptr != '/'))ptr --;
346
strncpy(tab_name, ptr+1, MAX_NAME_SIZE);
347
tab_name[MAX_NAME_SIZE-1] = 0;
351
while ((ptr > path) && (*ptr != '/'))ptr --;
357
if (len >= MAX_NAME_SIZE)
358
len = MAX_NAME_SIZE-1;
360
memcpy(db_name, ptr, len);
365
//====================
366
int pbms_rename_table_with_blobs(const char *old_table_path, const char *new_table_path, PBMSResultPtr result)
368
char o_db_name[MAX_NAME_SIZE], n_db_name[MAX_NAME_SIZE], o_tab_name[MAX_NAME_SIZE], n_tab_name[MAX_NAME_SIZE];
370
result->mr_had_blobs = false;
371
if (!pbms_api.isPBMSLoaded())
374
result->mr_had_blobs = true; // Assume it has blobs.
376
parse_table_path(old_table_path, o_db_name, o_tab_name);
377
parse_table_path(new_table_path, n_db_name, n_tab_name);
379
return pbms_api.renameTable(o_db_name, o_tab_name, n_db_name, n_tab_name, result);
382
//====================
383
int pbms_delete_table_with_blobs(const char *table_path, PBMSResultPtr result)
385
char db_name[MAX_NAME_SIZE], tab_name[MAX_NAME_SIZE];
387
result->mr_had_blobs = false;
388
if (!pbms_api.isPBMSLoaded())
391
result->mr_had_blobs = true; // Assume it has blobs.
392
parse_table_path(table_path, db_name, tab_name);
394
return pbms_api.dropTable(db_name, tab_name, result);
397
//====================
398
void pbms_completed(const TABLE *table, bool ok)
400
if (!pbms_api.isPBMSLoaded())
403
if ((!table) || (table->s->blob_fields != 0))
404
pbms_api.completed(ok) ;